if any user start the tracking , not login other devices and the expire token then the logout
This commit is contained in:
parent
b56a6e11c4
commit
44d790abab
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -95,3 +95,4 @@ function handleCloseEvents(mainWindow: BrowserWindow) {
|
||||||
export function getMainWindow() {
|
export function getMainWindow() {
|
||||||
return mainWindow;
|
return mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>(
|
||||||
|
|
|
||||||
|
|
@ -115,3 +115,5 @@ export const startWindowPolling = (win: BrowserWindow) => {
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue