- Delete `data.service.ts` and `offline-data.service.ts`, consolidating functionality into `LocalSystem`. - Refactor book, character, and conversation operations to adopt unified, multilingual, and session-enabled IPC handlers in `LocalSystem`. - Simplify redundant legacy methods, enhancing maintainability and consistency.
113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
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<void, BookProps[]>(
|
|
* 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<string, BookProps>(
|
|
* 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<CreateBookData, string>(
|
|
* 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<string, void>(
|
|
* async (userId, bookId, lang) => {
|
|
* await Book.deleteBook(bookId, userId, lang);
|
|
* }
|
|
* )
|
|
* );
|
|
* // Frontend: invoke('db:book:delete', bookId)
|
|
*/
|
|
export function createHandler<TBody = void, TReturn = void>(
|
|
handler: (userId: string, body: TBody, lang: 'fr' | 'en') => TReturn | Promise<TReturn>
|
|
): (event: IpcMainInvokeEvent, body?: TBody) => Promise<TReturn> {
|
|
return async function(event: IpcMainInvokeEvent, body?: TBody): Promise<TReturn> {
|
|
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.');
|
|
}
|
|
};
|
|
}
|