import type { IpcMainInvokeEvent } from 'electron'; import Store from 'electron-store'; // ============================================================ // SESSION MANAGEMENT - Auto-inject userId and lang // ============================================================ /** * Electron store instance for session management * - userId: Set during login via 'login-success' event * - userLang: Set via 'set-lang' handler */ const store = new Store({ encryptionKey: 'eritors-scribe-secure-key' }); /** * Get userId from electron-store * Set during login via 'login-success' event */ function getUserIdFromSession(): string | null { return store.get('userId', null) as string | null; } /** * Get lang from electron-store * Set via 'set-lang' handler, defaults to 'fr' */ function getLangFromSession(): 'fr' | 'en' { return store.get('userLang', 'fr') as 'fr' | 'en'; } // ============================================================ // UNIVERSAL HANDLER - Like a Fastify route // Automatically injects: userId, lang // Optional body parameter (for GET, POST, PUT, DELETE) // Generic return type (void, object, etc.) // ============================================================ /** * Universal IPC handler - works like a Fastify route * Automatically injects: userId, lang from session * * @template TBody - Request body type (use void for no params) * @template TReturn - Response type (use void for no return) * * @example * // GET with no params * ipcMain.handle('db:books:getAll', * createHandler( * async (userId, body, lang) => { * return await Book.getBooks(userId, lang); * } * ) * ); * // Frontend: invoke('db:books:getAll') * * @example * // GET with 1 param * ipcMain.handle('db:book:get', * createHandler( * async (userId, bookId, lang) => { * return await Book.getBook(bookId, userId, lang); * } * ) * ); * // Frontend: invoke('db:book:get', bookId) * * @example * // POST with object body * ipcMain.handle('db:book:create', * createHandler( * async (userId, data, lang) => { * return await Book.addBook(userId, data, lang); * } * ) * ); * // Frontend: invoke('db:book:create', { title: '...', ... }) * * @example * // DELETE with void return * ipcMain.handle('db:book:delete', * createHandler( * async (userId, bookId, lang) => { * await Book.deleteBook(bookId, userId, lang); * } * ) * ); * // Frontend: invoke('db:book:delete', bookId) */ export function createHandler( handler: (userId: string, body: TBody, lang: 'fr' | 'en') => TReturn | Promise ): (event: IpcMainInvokeEvent, body?: TBody) => Promise { return async function(event: IpcMainInvokeEvent, body?: TBody): Promise { const userId = getUserIdFromSession(); const lang = getLangFromSession(); if (!userId) { throw new Error('User not authenticated'); } try { return await handler(userId, body as TBody, lang); } catch (error: unknown) { if (error instanceof Error) { console.error(`[DB] ${error.message}`); throw error; } throw new Error('An unknown error occurred.'); } }; }