diff --git a/.eslintrc.js b/.eslintrc.js index e928b86..1ebc781 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,10 +1,16 @@ // This configuration only applies to the package manager root. /** @type {import("eslint").Linter.Config} */ module.exports = { - ignorePatterns: ["apps/**", "packages/**"], extends: ["@repo/eslint-config/library.js"], + ignorePatterns: ["apps/**", "packages/**"], parser: "@typescript-eslint/parser", parserOptions: { project: true, }, + plugins: ["@typescript-eslint"], + root: true, + rules: { + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], + "no-unused-vars": "off", + }, }; diff --git a/apps/mocksi-lite-next/.eslintrc b/apps/mocksi-lite-next/.eslintrc index b8e2ff3..baab62f 100644 --- a/apps/mocksi-lite-next/.eslintrc +++ b/apps/mocksi-lite-next/.eslintrc @@ -22,7 +22,14 @@ "@typescript-eslint" ], "rules": { - "react/react-in-jsx-scope": "off" + "react/react-in-jsx-scope": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_" + } + ] }, "globals": { "chrome": "readonly" diff --git a/apps/mocksi-lite-next/package.json b/apps/mocksi-lite-next/package.json index 104ddea..4ac696e 100755 --- a/apps/mocksi-lite-next/package.json +++ b/apps/mocksi-lite-next/package.json @@ -1,6 +1,7 @@ { "dependencies": { "@repo/reactor": "workspace:*", + "jwt-decode": "^4.0.0", "react": "^18.3.1", "react-dom": "^18.3.1", "uuid": "^9.0.1", diff --git a/apps/mocksi-lite-next/src/pages/background/index.ts b/apps/mocksi-lite-next/src/pages/background/index.ts index c3d004b..87923c4 100644 --- a/apps/mocksi-lite-next/src/pages/background/index.ts +++ b/apps/mocksi-lite-next/src/pages/background/index.ts @@ -1,3 +1,5 @@ +import { jwtDecode } from "jwt-decode"; + console.log("background script loaded"); const MOCKSI_AUTH = "mocksi-auth"; @@ -7,12 +9,23 @@ const getAuth = async (): Promise => { try { const storageAuth = await chrome.storage.local.get(MOCKSI_AUTH); + if (!storageAuth[MOCKSI_AUTH]) { + return null; + } const mocksiAuth = JSON.parse(storageAuth[MOCKSI_AUTH]); + const jwtPayload = jwtDecode(mocksiAuth.accessToken); + const isExpired = jwtPayload.exp && Date.now() >= jwtPayload.exp * 1000; + + if (isExpired) { + console.log("token expired, clearing chrome storage"); + await clearAuth(); + return null; + } return mocksiAuth; } catch (err) { console.error(err); - return null; } + return null; }; const clearAuth = async (): Promise => { @@ -31,19 +44,22 @@ async function getCurrentTab() { return tab; } -function showAuthTab() { - return new Promise((resolve: (value?: unknown) => void) => { +async function showAuthTab(force?: boolean) { + return new Promise(async (resolve: (value?: unknown) => void) => { chrome.tabs.query({}, function (tabs) { let tabExists = false; - for (const tab of tabs) { - const tabUrlStr = tab.url || tab.pendingUrl || ""; - const loadUrl = new URL(import.meta.env.VITE_NEST_APP); - const tabUrl = new URL(tabUrlStr); - if (loadUrl.href === tabUrl.href) { - tabExists = true; - break; + if (!force) { + for (const tab of tabs) { + const tabUrlStr = tab.url || tab.pendingUrl || ""; + const loadUrl = new URL(import.meta.env.VITE_NEST_APP); + const tabUrl = new URL(tabUrlStr); + if (loadUrl.href === tabUrl.href) { + tabExists = true; + break; + } } } + if (!tabExists) { chrome.tabs.create({ url: import.meta.env.VITE_NEST_APP }, resolve); } else { @@ -71,7 +87,6 @@ chrome.action.onClicked.addListener((tab) => { chrome.runtime.onMessage.addListener( (request, _sender, sendResponse): boolean => { - console.log("Received message:", request); sendResponse({ data: request.data, message: request.message, @@ -88,7 +103,13 @@ chrome.runtime.onMessageExternal.addListener( // execute in async block so that we return true // synchronously, telling chrome to wait for the response (async () => { - if (request.message === "UNAUTHORIZED") { + if (request.message === "AUTH_ERROR") { + await clearAuth(); + sendResponse({ + message: "retry", + status: "ok", + }); + } else if (request.message === "UNAUTHORIZED") { const auth = await getAuth(); if (auth) { const { accessToken, email } = auth; @@ -98,25 +119,18 @@ chrome.runtime.onMessageExternal.addListener( status: "ok", }); } else { - await showAuthTab(); + await showAuthTab(true); sendResponse({ message: "authenticating", status: "ok", }); } - } else if (request.message === "AUTH_ERROR") { - await clearAuth(); - await showAuthTab(); - sendResponse({ - message: "authenticating", - status: "ok", - }); } else { const tab = await getCurrentTab(); if (!tab.id) { sendResponse({ message: request.message, status: "no-tab" }); console.log("No active tab found, could not send message"); - return; + return true; } chrome.tabs.sendMessage( tab.id, diff --git a/apps/mocksi-lite-next/src/pages/content/mocksi-extension.tsx b/apps/mocksi-lite-next/src/pages/content/mocksi-extension.tsx index 75dea5b..a6fcc9b 100644 --- a/apps/mocksi-lite-next/src/pages/content/mocksi-extension.tsx +++ b/apps/mocksi-lite-next/src/pages/content/mocksi-extension.tsx @@ -8,13 +8,14 @@ import { getHighlighter } from "./highlighter"; const STORAGE_CHANGE_EVENT = "MOCKSI_STORAGE_CHANGE"; const div = document.createElement("div"); -div.id = "__root"; +div.id = "__mocksi__root"; document.body.appendChild(div); let mounted = false; const reactor = new Reactor(); window.addEventListener("message", (event: MessageEvent) => { const eventData = event.data; + if (event.source !== window || !eventData || !eventData.type) { return; } @@ -28,7 +29,7 @@ window.addEventListener("message", (event: MessageEvent) => { chrome.runtime.onMessage.addListener((request) => { if (request.message === "mount-extension") { - const rootContainer = document.querySelector("#__root"); + const rootContainer = document.querySelector("#__mocksi__root"); if (!rootContainer) throw new Error("Can't find Content root element"); const root = createRoot(rootContainer); const Iframe = () => { @@ -84,7 +85,10 @@ chrome.runtime.onMessage.addListener((request) => { ); } } - if (request.message === "STOP_EDITING" || request.message === "STOP_PLAYING") { + if ( + request.message === "STOP_EDITING" || + request.message === "STOP_PLAYING" + ) { reactor.detach(); } // resize iframe @@ -162,14 +166,15 @@ chrome.runtime.onMessage.addListener((request) => { }; // avoid remounting react tree - if (!mounted) { - root.render(