if any user start the tracking , not login other devices and the expire token then the logout

This commit is contained in:
JaiPandey425 2025-11-14 16:04:48 +05:30
parent b56a6e11c4
commit 44d790abab
8 changed files with 73 additions and 13 deletions

View File

@ -2,6 +2,7 @@
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { getToken } from '../utils/tokenHelper.js'; import { getToken } from '../utils/tokenHelper.js';
import chalk from "chalk"; import chalk from "chalk";
import { sendTokenExpired } from '../ipc/ipcHandler.js';
const TOKEN_KEY = 'ACCESS_TOKEN'; const TOKEN_KEY = 'ACCESS_TOKEN';
@ -37,6 +38,7 @@ api.interceptors.response.use(
console.error('[Axios Error]', error.message); console.error('[Axios Error]', error.message);
if (error.response?.status === 401) { if (error.response?.status === 401) {
console.warn('Token expired — trigger refresh logic if needed'); console.warn('Token expired — trigger refresh logic if needed');
sendTokenExpired();
} }
return Promise.reject(error); return Promise.reject(error);
} }

View File

@ -4,6 +4,9 @@ import { getActiveApp, handleLogin, takeScreenshot, startWindowPolling } from ".
import api from "../api/axiosClient.js"; import api from "../api/axiosClient.js";
import { getMainWindow } from '../main.js'; import { getMainWindow } from '../main.js';
export const registerIPCHandlers = (mainWindow: BrowserWindow) => { export const registerIPCHandlers = (mainWindow: BrowserWindow) => {
console.log("⚙️ Registering IPC Handlers..."); console.log("⚙️ Registering IPC Handlers...");
@ -44,6 +47,10 @@ export const registerIPCHandlers = (mainWindow: BrowserWindow) => {
} }
}); });
ipcMain.handle("get-active-app", async () => { ipcMain.handle("get-active-app", async () => {
return await getActiveApp(); return await getActiveApp();
}); });
@ -80,15 +87,16 @@ ipcMain.handle("get-project-list", async () => {
try { try {
const res = await api.get("/list/projects"); 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 only serializable data
return JSON.parse(JSON.stringify(res)); return JSON.parse(JSON.stringify(res));
} catch (error: any) { } catch (error: any) {
console.error(" get-project-list error:", error); console.error(" get-project-list error:", error.status);
return { return {
success: false, 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");
}
});
};

View File

@ -95,3 +95,4 @@ function handleCloseEvents(mainWindow: BrowserWindow) {
export function getMainWindow() { export function getMainWindow() {
return mainWindow; return mainWindow;
} }

View File

@ -15,22 +15,34 @@ contextBridge.exposeInMainWorld('electron', {
onWindowUpdate: (callback) => onWindowUpdate: (callback) =>
ipcRenderer.on("window:updated", (_, data) => callback(data)), ipcRenderer.on("window:updated", (_, data) => callback(data)),
startProjectTracking: (data) => ipcRenderer.invoke("start-project-tracking", data), startProjectTracking: (data) => ipcRenderer.invoke("start-project-tracking", data),
stopProjectTracking: (data) => ipcRenderer.invoke("stop-project-tracking", data), stopProjectTracking: (data) => ipcRenderer.invoke("stop-project-tracking", data),
trackingTabsOpeningClosing: (data) => trackingTabsOpeningClosing: (data) =>
ipcRenderer.invoke("tracking-tabs-opening-and-closing", data), ipcRenderer.invoke("tracking-tabs-opening-and-closing", data),
getProjectLogs: ( getProjectLogs: (
projectID: string, projectID: string,
fromDate?: string, fromDate?: string,
toDate?: string toDate?: string
) => ipcRenderer.invoke("get-project-logs", { projectID, fromDate, toDate }), ) => 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), 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']); } satisfies Window['electron']);
// function ipcInvoke<Key extends keyof EventPayloadMapping>( // function ipcInvoke<Key extends keyof EventPayloadMapping>(

View File

@ -115,3 +115,5 @@ export const startWindowPolling = (win: BrowserWindow) => {
} }
}, 5000); }, 5000);
}; };

View File

@ -20,6 +20,8 @@ const App: React.FC = () => {
const theme = ThemeToggle(); const theme = ThemeToggle();
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
// ✅ Get user directly from Redux // ✅ Get user directly from Redux
const user = useSelector((state: RootState) => state.user.user); const user = useSelector((state: RootState) => state.user.user);

View File

@ -126,6 +126,24 @@ const DashboardLayout = () => {
const [projectsData, setProjectsData] = useState<Project[]>([]); const [projectsData, setProjectsData] = useState<Project[]>([]);
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 () => { const fetchProfile = async () => {
try { try {
dispatch(setLoading(true)); dispatch(setLoading(true));
@ -210,10 +228,13 @@ const DashboardLayout = () => {
// Set the initial elapsed time in the component state // Set the initial elapsed time in the component state
setElapsedTime(initialElapsedTime); setElapsedTime(initialElapsedTime);
} }
fetchProfile() fetchProfile()
} else { } else {
console.warn("⚠️ Unexpected response:", res); console.warn("⚠️ Unexpected response:", res);
// if (res?.status) {
// // handleLogout()
// }
} }
} catch (err) { } catch (err) {
console.error("❌ Error fetching projects:", err); console.error("❌ Error fetching projects:", err);

3
types.d.ts vendored
View File

@ -33,6 +33,7 @@ interface ProjectListResponse {
totalWorkTime?: string; totalWorkTime?: string;
totalWorkMs?: number; totalWorkMs?: number;
data?: any[]; // you can replace `any[]` with a typed array later data?: any[]; // you can replace `any[]` with a typed array later
status : number
} }
interface TabActivity { interface TabActivity {
@ -142,7 +143,7 @@ interface Window {
}) => Promise<{ success: boolean; message?: string; data?: any }>; }) => Promise<{ success: boolean; message?: string; data?: any }>;
onTokenExpired: (callback: () => void) => void;
}; };
} }