Compare commits

...

2 Commits

Author SHA1 Message Date
BhagwanSingh 42dd4da26b Get Active Window 2025-11-07 13:51:55 +05:30
BhagwanSingh e2b0561251 LogOut Fixed 2025-11-07 13:04:42 +05:30
11 changed files with 819 additions and 216 deletions

825
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@
"@tailwindcss/vite": "^4.1.16", "@tailwindcss/vite": "^4.1.16",
"axios": "^1.13.2", "axios": "^1.13.2",
"electron-store": "^11.0.2", "electron-store": "^11.0.2",
"get-windows": "^9.2.3",
"lucide-react": "^0.552.0", "lucide-react": "^0.552.0",
"os-utils": "^0.0.14", "os-utils": "^0.0.14",
"react": "^18.3.1", "react": "^18.3.1",

View File

@ -2,19 +2,21 @@
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';
const TOKEN_KEY = 'auth_token'; const TOKEN_KEY = 'ACCESS_TOKEN';
const api: AxiosInstance = axios.create({ const api: AxiosInstance = axios.create({
baseURL: 'https://wired-terminally-muskrat.ngrok-free.app/api/v1', // 🔁 your backend baseURL: 'https://wired-terminally-muskrat.ngrok-free.app/api/v1/va', // 🔁 your backend
timeout: 10000, // timeout: 10000,
}); });
// --- Request Interceptor --- // --- Request Interceptor ---
api.interceptors.request.use( api.interceptors.request.use(
async (config: AxiosRequestConfig): Promise<any> => { async (config: AxiosRequestConfig): Promise<any> => {
const token = await getToken(TOKEN_KEY); const token = await getToken(TOKEN_KEY);
if (token && config.headers) { config.headers = config.headers || {}
config.headers.token = token;
if (token) {
config.headers['token'] = token;
config.headers['ngrok-skip-browser-warning'] = 98098; config.headers['ngrok-skip-browser-warning'] = 98098;
} }

View File

@ -1,14 +1,14 @@
// electron/ipc/tokenHandler.ts // electron/ipc/tokenHandler.ts
import { ipcMain } from 'electron'; import { ipcMain } from 'electron';
import { getToken, saveToken, clearToken } from '../utils/tokenHelper.js'; import { getToken, saveToken, clearToken } from '../utils/tokenHelper.js';
import { handleLogin, takeScreenshot } from '../utils/utilityFunctions.js'; import { getActiveApp, handleLogin, takeScreenshot } from '../utils/utilityFunctions.js';
import api from '../api/axiosClient.js'; import api from '../api/axiosClient.js';
ipcMain.handle('get-token', async (_, TOKEN_KEY: string) => { ipcMain.handle('get-token', async (_, TOKEN_KEY: "ACCESS_TOKEN" | "REFRESH_TOKEN") => {
return await getToken(TOKEN_KEY); return await getToken(TOKEN_KEY);
}); });
ipcMain.handle('save-token', async (_, TOKEN_KEY: string, token: string) => { ipcMain.handle('save-token', async (_, TOKEN_KEY: "ACCESS_TOKEN" | "REFRESH_TOKEN", token: string) => {
await saveToken(TOKEN_KEY, token); await saveToken(TOKEN_KEY, token);
return true; return true;
}); });
@ -29,8 +29,19 @@ ipcMain.handle('login-request', async (event, credentials: { email: string, pass
}); });
ipcMain.handle('logout-request', async () => { ipcMain.handle('logout-request', async () => {
(async () => { try {
console.log("Handle Logout listened") console.log("Handle Logout listened")
await api.get("/logout"); console.log("Token", await getToken("ACCESS_TOKEN"))
})() const res = await api.get("/logout");
if(res && ("success" in res) && res.success) {
return {success: true, message: "Logout Successfully"}
}
return {success: false, message: ""}
} catch(error) {
return {success: false, message: ""}
}
});
ipcMain.handle('get-active-app', () => {
return getActiveApp();
}); });

View File

@ -11,6 +11,12 @@ app.on('ready', async () => {
webPreferences: { webPreferences: {
preload: getPreloadPath(), preload: getPreloadPath(),
}, },
resizable: false, // disables resizing (no maximize)
minimizable: false, // disables minimize button
maximizable: false, // disables maximize button
closable: true,
alwaysOnTop: true,
// disables default system frame (dont do this if you want a proper working menu bar) // disables default system frame (dont do this if you want a proper working menu bar)
frame: process.platform === 'darwin' ? false : true, frame: process.platform === 'darwin' ? false : true,
autoHideMenuBar: true autoHideMenuBar: true
@ -30,19 +36,19 @@ app.on('ready', async () => {
return getStaticData(); return getStaticData();
}); });
ipcMainOn('sendFrameAction', (payload) => { // ipcMainOn('sendFrameAction', (payload) => {
switch (payload) { // switch (payload) {
case 'CLOSE': // case 'CLOSE':
mainWindow.close(); // mainWindow.close();
break; // break;
case 'MAXIMIZE': // case 'MAXIMIZE':
mainWindow.maximize(); // mainWindow.maximize();
break; // break;
case 'MINIMIZE': // case 'MINIMIZE':
mainWindow.minimize(); // mainWindow.minimize();
break; // break;
} // }
}); // });
// createTray(mainWindow); // createTray(mainWindow);
handleCloseEvents(mainWindow); handleCloseEvents(mainWindow);

33
src/electron/preload.cts Normal file
View File

@ -0,0 +1,33 @@
import { contextBridge, ipcRenderer } from "electron";
console.log("setting PreLoad")
contextBridge.exposeInMainWorld('electron', {
login: (credentials: { email: string, password: string }) => ipcRenderer.invoke('login-request', credentials),
logout: () => ipcRenderer.invoke('logout-request'),
getToken: (TOKEN_KEY: "ACCESS_TOKEN" | "REFRESH_TOKEN") => ipcRenderer.invoke('get-token', TOKEN_KEY),
uploadScreenshot: () => ipcRenderer.invoke('upload-screenshot'),
getActiveAppDetails: () => ipcRenderer.invoke('get-active-app'),
} satisfies Window['electron']);
// function ipcInvoke<Key extends keyof EventPayloadMapping>(
// key: Key
// ): Promise<EventPayloadMapping[Key]> {
// return ipcRenderer.invoke(key);
// }
// function ipcOn<Key extends keyof EventPayloadMapping>(
// key: Key,
// callback: (payload: EventPayloadMapping[Key]) => void
// ) {
// const cb = (_: Electron.IpcRendererEvent, payload: any) => callback(payload);
// ipcRenderer.on(key, cb);
// return () => ipcRenderer.off(key, cb);
// }
// function ipcSend<Key extends keyof EventPayloadMapping>(
// key: Key,
// payload: EventPayloadMapping[Key]
// ) {
// ipcRenderer.send(key, payload);
// }

View File

@ -1,55 +0,0 @@
import { contextBridge, ipcRenderer } from "electron";
const electron = require('electron');
console.log("setting PreLoad")
contextBridge.exposeInMainWorld('electron', {
subscribeStatistics: (callback) =>
ipcOn('statistics', (stats) => {
callback(stats);
}),
subscribeChangeView: (callback) =>
ipcOn('changeView', (view) => {
callback(view);
}),
getStaticData: () => ipcInvoke('getStaticData'),
sendFrameAction: (payload) => ipcSend('sendFrameAction', payload),
login: (credentials: { email: string, password: string }) => ipcRenderer.invoke('login-request', credentials),
logout: () => ipcRenderer.invoke('logout-request'),
getToken: (TOKEN_KEY: string) => ipcRenderer.invoke('get-token', TOKEN_KEY),
uploadScreenshot: () => ipcRenderer.invoke('upload-screenshot'),
} satisfies Window['electron']);
function ipcInvoke<Key extends keyof EventPayloadMapping>(
key: Key
): Promise<EventPayloadMapping[Key]> {
return electron.ipcRenderer.invoke(key);
}
function ipcOn<Key extends keyof EventPayloadMapping>(
key: Key,
callback: (payload: EventPayloadMapping[Key]) => void
) {
const cb = (_: Electron.IpcRendererEvent, payload: any) => callback(payload);
electron.ipcRenderer.on(key, cb);
return () => electron.ipcRenderer.off(key, cb);
}
function ipcSend<Key extends keyof EventPayloadMapping>(
key: Key,
payload: EventPayloadMapping[Key]
) {
electron.ipcRenderer.send(key, payload);
}
contextBridge.exposeInMainWorld('electronAPI', {
getToken: (key: string) => ipcRenderer.invoke('get-token', key),
saveToken: (key: string, token: string) => ipcRenderer.invoke('save-token', key, token),
clearToken: (key: string) => ipcRenderer.invoke('clear-token', key),
});

View File

@ -4,14 +4,14 @@ import Store from 'electron-store';
const store = new Store(); const store = new Store();
export async function getToken(TOKEN_KEY: string): Promise<string | null> { export async function getToken(TOKEN_KEY: "ACCESS_TOKEN" | "REFRESH_TOKEN"): Promise<string | null> {
const encryptedBase64 = store.get(TOKEN_KEY) as string | undefined; const encryptedBase64 = store.get(TOKEN_KEY) as string | undefined;
if (!encryptedBase64) return null; if (!encryptedBase64) return null;
const encryptedBuffer = Buffer.from(encryptedBase64, 'base64'); const encryptedBuffer = Buffer.from(encryptedBase64, 'base64');
return safeStorage.decryptString(encryptedBuffer); return safeStorage.decryptString(encryptedBuffer);
} }
export async function saveToken(TOKEN_KEY: string, token: string): Promise<void> { export async function saveToken(TOKEN_KEY: "ACCESS_TOKEN" | "REFRESH_TOKEN", token: string): Promise<void> {
const encryptedBuffer = safeStorage.encryptString(token); const encryptedBuffer = safeStorage.encryptString(token);
store.set(TOKEN_KEY, encryptedBuffer.toString('base64')); store.set(TOKEN_KEY, encryptedBuffer.toString('base64'));
} }

View File

@ -4,6 +4,7 @@ import path from "path";
import axios from 'axios'; import axios from 'axios';
import sharp from 'sharp'; import sharp from 'sharp';
import { saveToken } from "./tokenHelper.js"; import { saveToken } from "./tokenHelper.js";
import { activeWindow } from "get-windows";
// Blur using an offscreen canvas // Blur using an offscreen canvas
async function blurAndCompressBase64Image(base64: string): Promise<Buffer> { async function blurAndCompressBase64Image(base64: string): Promise<Buffer> {
@ -62,7 +63,10 @@ export const handleLogin = async (credentials: { email: string, password: string
console.log("API CAlled", response) console.log("API CAlled", response)
const { accessToken, refreshToken } = response.data; const { accessToken, refreshToken } = response.data;
await saveToken(accessToken, refreshToken);
await saveToken("ACCESS_TOKEN", accessToken);
await saveToken("REFRESH_TOKEN", refreshToken);
return { success: true, user: response.data.user }; return { success: true, user: response.data.user };
} catch (error) { } catch (error) {
@ -90,3 +94,8 @@ export const handleLogin = async (credentials: { email: string, password: string
return { success: false, error: "Unexpected error occurred" }; return { success: false, error: "Unexpected error occurred" };
} }
} }
export const getActiveApp = async () => {
const result = await activeWindow();
return result;
};

View File

@ -101,22 +101,28 @@ const DashboardLayout: React.FC<DashboardLayoutProps> = ({ setUser }) => {
}, []); }, []);
const handleLogout = async () => { const handleLogout = async () => {
localStorage.removeItem("user");
setUser(null);
console.log("calling Logout UI")
try { try {
const res = await window.electron.logout() const res = await window.electron.logout()
console.log(res) if (res && res.success) {
localStorage.removeItem("user");
setUser(null);
navigate("/login");
}
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
navigate("/login");
}; };
useEffect(() => {
setInterval(async () => {
const details = await window.electron.getActiveAppDetails()
console.log(details)
}, 15000)
}, [])
const toggleSidebar = () => setSidebarOpen(!isSidebarOpen); const toggleSidebar = () => setSidebarOpen(!isSidebarOpen);
const closeSidebarOnMobile = () => { const closeSidebarOnMobile = () => {

13
types.d.ts vendored
View File

@ -25,18 +25,11 @@ type UnsubscribeFunction = () => void;
interface Window { interface Window {
electron: { electron: {
subscribeStatistics: (
callback: (statistics: Statistics) => void
) => UnsubscribeFunction;
getStaticData: () => Promise<StaticData>;
subscribeChangeView: (
callback: (view: View) => void
) => UnsubscribeFunction;
sendFrameAction: (payload: FrameWindowAction) => void;
login: (credentials: { email: string; password: string }) => Promise<any>; login: (credentials: { email: string; password: string }) => Promise<any>;
logout: () => {}; logout: () => Promise<any>;
getToken: (tokenKey: string) => Promise<string | null>; getToken: (tokenKey: "ACCESS_TOKEN" | "REFRESH_TOKEN") => Promise<string | null>;
uploadScreenshot: () => {}; uploadScreenshot: () => {};
getActiveAppDetails: () => {};
}; };
} }