From 44d790ababbd8e9f4201f12f23ef30f6e68ed01c Mon Sep 17 00:00:00 2001 From: JaiPandey425 Date: Fri, 14 Nov 2025 16:04:48 +0530 Subject: [PATCH] if any user start the tracking , not login other devices and the expire token then the logout --- src/electron/api/axiosClient.ts | 2 ++ src/electron/ipc/ipcHandler.ts | 25 ++++++++++++++++++++++--- src/electron/main.ts | 3 ++- src/electron/preload.cts | 24 ++++++++++++++++++------ src/electron/utils/utilityFunctions.ts | 4 +++- src/ui/App.tsx | 2 ++ src/ui/layout/DashboardLayout.tsx | 23 ++++++++++++++++++++++- types.d.ts | 3 ++- 8 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/electron/api/axiosClient.ts b/src/electron/api/axiosClient.ts index 18d5805..5fedd1c 100644 --- a/src/electron/api/axiosClient.ts +++ b/src/electron/api/axiosClient.ts @@ -2,6 +2,7 @@ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; import { getToken } from '../utils/tokenHelper.js'; import chalk from "chalk"; +import { sendTokenExpired } from '../ipc/ipcHandler.js'; const TOKEN_KEY = 'ACCESS_TOKEN'; @@ -37,6 +38,7 @@ api.interceptors.response.use( console.error('[Axios Error]', error.message); if (error.response?.status === 401) { console.warn('Token expired — trigger refresh logic if needed'); + sendTokenExpired(); } return Promise.reject(error); } diff --git a/src/electron/ipc/ipcHandler.ts b/src/electron/ipc/ipcHandler.ts index 6e76996..c97479c 100644 --- a/src/electron/ipc/ipcHandler.ts +++ b/src/electron/ipc/ipcHandler.ts @@ -4,6 +4,9 @@ import { getActiveApp, handleLogin, takeScreenshot, startWindowPolling } from ". import api from "../api/axiosClient.js"; import { getMainWindow } from '../main.js'; + + + export const registerIPCHandlers = (mainWindow: BrowserWindow) => { console.log("⚙️ Registering IPC Handlers..."); @@ -43,6 +46,10 @@ export const registerIPCHandlers = (mainWindow: BrowserWindow) => { return { success: false, message: "Logout failed" }; } }); + + + + ipcMain.handle("get-active-app", async () => { return await getActiveApp(); @@ -80,15 +87,16 @@ ipcMain.handle("get-project-list", async () => { try { const res = await api.get("/list/projects"); - // console.log(" API /list/project response:", res); + console.log(" API /list/project response:", res); // Return only serializable data return JSON.parse(JSON.stringify(res)); } catch (error: any) { - console.error(" get-project-list error:", error); + console.error(" get-project-list error:", error.status); return { success: false, - message: error?.message || String(error) || "Unknown error" + message: error?.message || String(error) || "Unknown error", + status:error?.status }; } }); @@ -264,3 +272,14 @@ ipcMain.handle("change-password", async (event, data) => { }; } }); + + +export const sendTokenExpired = () => { + console.log("⚠️ Sending token-expired event to renderer"); + + BrowserWindow.getAllWindows().forEach((win) => { + if (!win.isDestroyed()) { + win.webContents.send("token-expired"); + } + }); +}; diff --git a/src/electron/main.ts b/src/electron/main.ts index 4083518..739da33 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -94,4 +94,5 @@ function handleCloseEvents(mainWindow: BrowserWindow) { export function getMainWindow() { return mainWindow; -} \ No newline at end of file +} + diff --git a/src/electron/preload.cts b/src/electron/preload.cts index c885819..08a3d6d 100644 --- a/src/electron/preload.cts +++ b/src/electron/preload.cts @@ -15,22 +15,34 @@ contextBridge.exposeInMainWorld('electron', { onWindowUpdate: (callback) => ipcRenderer.on("window:updated", (_, data) => callback(data)), + startProjectTracking: (data) => ipcRenderer.invoke("start-project-tracking", data), stopProjectTracking: (data) => ipcRenderer.invoke("stop-project-tracking", data), trackingTabsOpeningClosing: (data) => ipcRenderer.invoke("tracking-tabs-opening-and-closing", data), getProjectLogs: ( - projectID: string, - fromDate?: string, - toDate?: string -) => ipcRenderer.invoke("get-project-logs", { projectID, fromDate, toDate }), + projectID: string, + fromDate?: string, + toDate?: string + ) => ipcRenderer.invoke("get-project-logs", { projectID, fromDate, toDate }), -getProfile: () => ipcRenderer.invoke("get-profile"), + getProfile: () => ipcRenderer.invoke("get-profile"), -changePassword: (data: { oldPassword: string; password: string; cPassword: string }) => + changePassword: (data: { oldPassword: string; password: string; cPassword: string }) => ipcRenderer.invoke("change-password", data), + onTokenExpired: (callback) => { + // Remove old listeners + ipcRenderer.removeAllListeners("token-expired"); + + // Add single-use listener + ipcRenderer.once("token-expired", () => { + console.log("⚠️ Token expired event received in PRELOAD (ONE TIME)"); + callback(); + }); + }, + } satisfies Window['electron']); // function ipcInvoke( diff --git a/src/electron/utils/utilityFunctions.ts b/src/electron/utils/utilityFunctions.ts index b285a3b..b2e3808 100644 --- a/src/electron/utils/utilityFunctions.ts +++ b/src/electron/utils/utilityFunctions.ts @@ -114,4 +114,6 @@ export const startWindowPolling = (win: BrowserWindow) => { console.error("❌ Polling error:", err); } }, 5000); -}; \ No newline at end of file +}; + + diff --git a/src/ui/App.tsx b/src/ui/App.tsx index 7a66012..474ce9f 100644 --- a/src/ui/App.tsx +++ b/src/ui/App.tsx @@ -20,6 +20,8 @@ const App: React.FC = () => { const theme = ThemeToggle(); const dispatch = useDispatch(); + + // ✅ Get user directly from Redux const user = useSelector((state: RootState) => state.user.user); diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index e748606..ca9c8bb 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -126,6 +126,24 @@ const DashboardLayout = () => { const [projectsData, setProjectsData] = useState([]); + useEffect(() => { + if (!window.electron?.onTokenExpired) return; + + console.log("🔐 Token expiry listener registered"); + + window.electron.onTokenExpired(() => { + console.log("🔥 Token expired — logging out..."); + handleLogout(); + }); + + return () => { + console.log("🧹 Cleaning token-expired listener"); + }; + }, []); + + + + const fetchProfile = async () => { try { dispatch(setLoading(true)); @@ -210,10 +228,13 @@ const DashboardLayout = () => { // Set the initial elapsed time in the component state setElapsedTime(initialElapsedTime); } - fetchProfile() + fetchProfile() } else { console.warn("⚠️ Unexpected response:", res); + // if (res?.status) { + // // handleLogout() + // } } } catch (err) { console.error("❌ Error fetching projects:", err); diff --git a/types.d.ts b/types.d.ts index 5058b2b..25a3095 100644 --- a/types.d.ts +++ b/types.d.ts @@ -33,6 +33,7 @@ interface ProjectListResponse { totalWorkTime?: string; totalWorkMs?: number; data?: any[]; // you can replace `any[]` with a typed array later + status : number } interface TabActivity { @@ -142,7 +143,7 @@ interface Window { }) => Promise<{ success: boolean; message?: string; data?: any }>; - +onTokenExpired: (callback: () => void) => void; }; }