diff --git a/apps/mocksi-lite-next/src/pages/background/index.ts b/apps/mocksi-lite-next/src/pages/background/index.ts
index 484536c..98244ad 100644
--- a/apps/mocksi-lite-next/src/pages/background/index.ts
+++ b/apps/mocksi-lite-next/src/pages/background/index.ts
@@ -1,194 +1,219 @@
-import { jwtDecode } from "jwt-decode";
+///
-console.log("background script loaded");
+import { jwtDecode } from "jwt-decode";
+// Constants
const MOCKSI_AUTH = "mocksi-auth";
-let prevRequest = {
- data: {},
- message: "INIT",
-};
-const getAuth = async (): Promise => {
+}
+
+interface Message {
+ message: string;
+ data?: any;
+}
+
+interface Response {
+ message: string | object;
+ status: string;
+ error?: any; // FIXME: it should not be any
+}
+
+// State
+let prevRequest: Message = { message: "INIT" };
+
+// Auth Utilities
+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;
+ const result = await chrome.storage.local.get(MOCKSI_AUTH);
+ return result[MOCKSI_AUTH] || null;
} catch (err) {
- console.error(err);
+ console.error("Error getting auth:", err);
+ return null;
}
- return null;
};
const clearAuth = async (): Promise => {
try {
- const storageAuth = await chrome.storage.local.get(MOCKSI_AUTH);
- storageAuth[MOCKSI_AUTH] = null;
+ await chrome.storage.local.set({ [MOCKSI_AUTH]: null });
} catch (err) {
- console.error(err);
+ console.error("Error clearing auth:", err);
}
};
-async function getCurrentTab() {
- const queryOptions = { active: true, lastFocusedWindow: true };
- // `tab` will either be a `tabs.Tab` instance or `undefined`.
- const [tab] = await chrome.tabs.query(queryOptions);
+// Browser Utilities
+const getCurrentTab = async () => {
+ const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
+ if (!tab || !tab.id) {
+ console.error("Cannot find active tab ID");
+ // NOTE: This is a a hack to prevent errors from crashing the extension
+ return { id: -1, url: "no-tab" };
+ }
return tab;
-}
+};
+
+const showAuthTab = async (force = false): Promise => {
+ const tabs = await chrome.tabs.query({});
+ const authUrl = new URL(import.meta.env.VITE_NEST_APP);
+ const tabExists = !force && tabs.some(tab => {
+ const tabUrlStr = tab.url || tab.pendingUrl || "";
+ return new URL(tabUrlStr).href === authUrl.href;
+ });
+
+ if (!tabExists) {
+ await chrome.tabs.create({ url: authUrl.href });
+ }
+};
+
+const setIcon = async (iconPath: string): Promise => {
+ await chrome.action.setIcon({ path: iconPath });
+};
-async function showAuthTab(force?: boolean) {
- return new Promise(async (resolve: (value?: unknown) => void) => {
- chrome.tabs.query({}, function (tabs) {
- let tabExists = false;
- 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;
+// Message Handlers
+const handleAuthError = async (): Promise => {
+ await clearAuth();
+ return { message: "retry", status: "ok" };
+};
+
+const handleUnauthorized = async (): Promise => {
+ const auth = await getAuth();
+
+ // FIXME: I have a hunch that this is not the best way to handle this situation
+ if (auth) {
+ const tab = await getCurrentTab();
+ return { message: { ...auth, url: tab?.url }, status: "ok" };
+ }
+
+ await showAuthTab(true);
+ return { message: "authenticating", status: "ok" };
+};
+
+const updateIcon = async (message: string): Promise => {
+ switch (message) {
+ case "PLAY":
+ await setIcon("play-icon.png");
+ break;
+ case "MINIMIZED":
+ // No action needed
+ break;
+ default:
+ console.log(`Unhandled icon update for message: ${message}`);
+ }
+};
+
+const handleOtherMessages = async (request: Message): Promise => {
+ const tab = await getCurrentTab();
+ if (!tab?.id) {
+ console.log("No active tab found, could not send message");
+ return { message: request.message, status: "no-tab" };
+ }
+
+ await updateIcon(request.message);
+ return { message: "processed", status: "ok" };
+};
+
+
+const checkAndHandleAuthRequest = async (request?: Message, sender?: chrome.runtime.MessageSender, sendResponse?: (response: Response) => void) => {
+ if (!request || !sendResponse) {
+ console.error("Invalid request or sendResponse");
+ return false;
+ }
+
+ try {
+ let response: Response;
+
+ switch (request.message) {
+ case "AUTH_ERROR":
+ response = await handleAuthError();
+ break;
+ case "UNAUTHORIZED":
+ response = await handleUnauthorized();
break;
- }
+ default:
+ response = await handleOtherMessages(request);
}
+
+ sendResponse(response);
+ } catch (error) {
+ console.error("Error processing message:", error);
+ sendResponse({ message: "error", status: "error", error: String(error) });
}
- if (!tabExists) {
- chrome.tabs.create({ url: import.meta.env.VITE_NEST_APP }, resolve);
- } else {
- resolve();
+ // Update prevRequest if not minimized
+ if (request.message !== "MINIMIZED") {
+ prevRequest = request;
}
- });
- });
+
+ return true;
}
-addEventListener("install", () => {
- // TODO test if this works on other browsers
- chrome.tabs.create({
- url: import.meta.env.VITE_NEST_APP,
- });
+// Main message listener
+chrome.runtime.onMessageExternal.addListener(
+ (request: Message, sender, sendResponse) => {
+ console.log("Previous message from external:", prevRequest);
+ console.log("Received new message from external:", request);
+
+ checkAndHandleAuthRequest(request, sender, sendResponse);
+
+ return true; // Indicates that the response is sent asynchronously
+ }
+);
+
+// Install event listener
+chrome.runtime.onInstalled.addListener(() => {
+ chrome.tabs.create({ url: import.meta.env.VITE_NEST_APP });
});
-// when user clicks toolbar mount extension
+// Browser action click listener
chrome.action.onClicked.addListener((tab) => {
if (!tab?.id) {
console.log("No tab found, could not mount extension");
return;
}
- chrome.tabs.sendMessage(tab.id, { message: "mount-extension" });
-
- if (prevRequest.message) {
- chrome.tabs.sendMessage(tab.id, {
- data: prevRequest.data,
- message: prevRequest.message,
- });
- }
+ chrome.tabs.sendMessage(tab.id, { action: "toggleExtension" }, (response) => {
+ if (chrome.runtime.lastError) {
+ console.error("Error sending message:", chrome.runtime.lastError);
+ return;
+ }
- if (prevRequest.message === "PLAY") {
- chrome.action.setIcon({
- path: "play-icon.png",
- tabId: tab.id,
- });
- }
+ if (response && response.status === "ok") {
+ console.log("Extension toggled successfully");
+ } else {
+ console.error("Failed to toggle extension:", response);
+ }
+ });
});
-chrome.runtime.onMessage.addListener(
- (request, _sender, sendResponse): boolean => {
- sendResponse({
- data: request.data,
- message: request.message,
- status: "ok",
- });
- return true;
- },
-);
+const checkAndHandleAuth = async () => {
+ const auth = await getAuth();
+ if (!auth) {
+ console.log("No auth token found");
+ return;
+ }
-chrome.runtime.onMessageExternal.addListener(
- (request, _sender, sendResponse) => {
- // This logging is useful and only shows up in the service worker
- console.log(" ");
- console.log("Previous message from external:", prevRequest);
- console.log("Received new message from external:", request);
+ const decodedToken: any = jwtDecode(auth.accessToken);
+ const currentTime = Math.floor(Date.now() / 1000);
- // execute in async block so that we return true
- // synchronously, telling chrome to wait for the response
- (async () => {
- 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;
- const tab = await getCurrentTab();
- sendResponse({
- message: { accessToken, email, url: tab.url },
- status: "ok",
- });
- } else {
- await showAuthTab(true);
- 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 true;
- }
+ if (!decodedToken.exp || decodedToken.exp >= currentTime) {
+ console.log("Valid auth token found");
+ return;
+ }
- // handle icon changes triggered by messaging
- switch (request.message) {
- case "MINIMIZED": // No action needed for "MINIMIZED"
- break;
- case "PLAY":
- await chrome.action.setIcon({
- path: "play-icon.png",
- tabId: tab.id,
- });
- break;
- default:
- chrome.action.setIcon({ path: "mocksi-icon.png", tabId: tab.id });
- break;
- }
+ console.log("Token expired, clearing auth");
+ await clearAuth();
+};
- chrome.tabs.sendMessage(
- tab.id,
- {
- data: request.data,
- message: request.message,
- },
- (response) => {
- sendResponse(response);
- },
- );
- }
- })();
+const initialize = async () => {
+ try {
+ await checkAndHandleAuth();
+ } catch (error) {
+ console.error("Error during initialization:", error);
+ }
+};
- // Store last app state so we can return to the correct state when the
- // menu is reopened
- if (request.message !== "MINIMIZED") {
- prevRequest = request;
- }
- return true;
- },
-);
+// Call the initialization function
+initialize();