Integrate session management, multilingual IPC handlers, and Book database operations

- Add `LocalSystem` with handlers for session retrieval (`userId`, `lang`) and error handling.
- Extend IPC handlers to support multilingual operations (`fr`, `en`) and session data injection
This commit is contained in:
natreex
2025-11-18 19:51:17 -05:00
parent 5a891a72ff
commit d46aecc80d
5 changed files with 874 additions and 2 deletions

View File

@@ -0,0 +1,282 @@
import type { IpcMainInvokeEvent } from 'electron';
import Store from 'electron-store';
// Electron store instance for session management
const store = new Store({
encryptionKey: 'eritors-scribe-secure-key'
});
// ============================================================
// SESSION MANAGEMENT - Retrieve userId and lang from store
// ============================================================
/**
* 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';
}
// ============================================================
// LEGACY HANDLERS - Manual userId injection, lang must be passed
// Keep these for backward compatibility
// Updated to support Promises
// ============================================================
export function createDbHandler<TReturn>(
handler: (userId: string) => TReturn | Promise<TReturn>
): (event: IpcMainInvokeEvent) => Promise<TReturn> {
return async function(event: IpcMainInvokeEvent): Promise<TReturn> {
const userId = getUserIdFromSession();
if (!userId) {
throw new Error('User not authenticated');
}
try {
return await handler(userId);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`[DB] ${error.message}`);
throw error;
}
throw new Error('An unknown error occurred.');
}
};
}
export function createDbHandler1<T1, TReturn>(
handler: (userId: string, arg1: T1) => TReturn | Promise<TReturn>
): (event: IpcMainInvokeEvent, arg1: T1) => Promise<TReturn> {
return async function(event: IpcMainInvokeEvent, arg1: T1): Promise<TReturn> {
const userId = getUserIdFromSession();
if (!userId) {
throw new Error('User not authenticated');
}
try {
return await handler(userId, arg1);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`[DB] ${error.message}`);
throw error;
}
throw new Error('An unknown error occurred.');
}
};
}
export function createDbHandler2<T1, T2, TReturn>(
handler: (userId: string, arg1: T1, arg2: T2) => TReturn | Promise<TReturn>
): (event: IpcMainInvokeEvent, arg1: T1, arg2: T2) => Promise<TReturn> {
return async function(event: IpcMainInvokeEvent, arg1: T1, arg2: T2): Promise<TReturn> {
const userId = getUserIdFromSession();
if (!userId) {
throw new Error('User not authenticated');
}
try {
return await handler(userId, arg1, arg2);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`[DB] ${error.message}`);
throw error;
}
throw new Error('An unknown error occurred.');
}
};
}
export function createDbHandler3<T1, T2, T3, TReturn>(
handler: (userId: string, arg1: T1, arg2: T2, arg3: T3) => TReturn | Promise<TReturn>
): (event: IpcMainInvokeEvent, arg1: T1, arg2: T2, arg3: T3) => Promise<TReturn> {
return async function(event: IpcMainInvokeEvent, arg1: T1, arg2: T2, arg3: T3): Promise<TReturn> {
const userId = getUserIdFromSession();
if (!userId) {
throw new Error('User not authenticated');
}
try {
return await handler(userId, arg1, arg2, arg3);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`[DB] ${error.message}`);
throw error;
}
throw new Error('An unknown error occurred.');
}
};
}
// ============================================================
// AUTO HANDLERS - Automatically inject userId AND lang
// Use these for new handlers - no need to pass lang from frontend
// ============================================================
/**
* Auto-handler with 0 parameters
* Automatically injects: userId, lang
*
* @example
* ipcMain.handle('db:user:get', createAutoHandler<UserProps>(
* function(userId: string, lang: 'fr' | 'en') {
* return User.getUser(userId, lang);
* }
* ));
*
* // Frontend call (no params needed):
* const user = await window.electron.invoke('db:user:get');
*/
export function createAutoHandler<TReturn>(
handler: (userId: string, lang: 'fr' | 'en') => TReturn | Promise<TReturn>
): (event: IpcMainInvokeEvent) => Promise<TReturn> {
return async function(event: IpcMainInvokeEvent): Promise<TReturn> {
const userId = getUserIdFromSession();
const lang = getLangFromSession();
if (!userId) {
throw new Error('User not authenticated');
}
try {
return await handler(userId, lang);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`[DB] ${error.message}`);
throw error;
}
throw new Error('An unknown error occurred.');
}
};
}
/**
* Auto-handler with 1 parameter
* Automatically injects: userId, lang
*
* @example
* ipcMain.handle('db:book:get', createAutoHandler1<string, BookProps>(
* function(userId: string, bookId: string, lang: 'fr' | 'en') {
* return Book.getBook(bookId, userId, lang);
* }
* ));
*
* // Frontend call (only bookId needed):
* const book = await window.electron.invoke('db:book:get', bookId);
*/
export function createAutoHandler1<T1, TReturn>(
handler: (userId: string, arg1: T1, lang: 'fr' | 'en') => TReturn | Promise<TReturn>
): (event: IpcMainInvokeEvent, arg1: T1) => Promise<TReturn> {
return async function(event: IpcMainInvokeEvent, arg1: T1): Promise<TReturn> {
const userId = getUserIdFromSession();
const lang = getLangFromSession();
if (!userId) {
throw new Error('User not authenticated');
}
try {
return await handler(userId, arg1, lang);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`[DB] ${error.message}`);
throw error;
}
throw new Error('An unknown error occurred.');
}
};
}
/**
* Auto-handler with 2 parameters
* Automatically injects: userId, lang
*
* @example
* ipcMain.handle('db:book:create', createAutoHandler1<CreateBookData, string>(
* function(userId: string, data: CreateBookData, lang: 'fr' | 'en') {
* return Book.addBook(null, userId, data.title, ..., lang);
* }
* ));
*
* // Frontend call (only data needed):
* const bookId = await window.electron.invoke('db:book:create', bookData);
*/
export function createAutoHandler2<T1, T2, TReturn>(
handler: (userId: string, arg1: T1, arg2: T2, lang: 'fr' | 'en') => TReturn | Promise<TReturn>
): (event: IpcMainInvokeEvent, arg1: T1, arg2: T2) => Promise<TReturn> {
return async function(event: IpcMainInvokeEvent, arg1: T1, arg2: T2): Promise<TReturn> {
const userId = getUserIdFromSession();
const lang = getLangFromSession();
if (!userId) {
throw new Error('User not authenticated');
}
try {
return await handler(userId, arg1, arg2, lang);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`[DB] ${error.message}`);
throw error;
}
throw new Error('An unknown error occurred.');
}
};
}
/**
* Auto-handler with 3 parameters
* Automatically injects: userId, lang
*
* @example
* ipcMain.handle('db:book:cover:update', createAutoHandler2<string, string, boolean>(
* function(userId: string, bookId: string, coverImageName: string, lang: 'fr' | 'en') {
* return Book.updateBookCover(userId, bookId, coverImageName, lang);
* }
* ));
*
* // Frontend call (bookId and coverImageName needed):
* const success = await window.electron.invoke('db:book:cover:update', bookId, coverImageName);
*/
export function createAutoHandler3<T1, T2, T3, TReturn>(
handler: (userId: string, arg1: T1, arg2: T2, arg3: T3, lang: 'fr' | 'en') => TReturn | Promise<TReturn>
): (event: IpcMainInvokeEvent, arg1: T1, arg2: T2, arg3: T3) => Promise<TReturn> {
return async function(event: IpcMainInvokeEvent, arg1: T1, arg2: T2, arg3: T3): Promise<TReturn> {
const userId = getUserIdFromSession();
const lang = getLangFromSession();
if (!userId) {
throw new Error('User not authenticated');
}
try {
return await handler(userId, arg1, arg2, arg3, lang);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`[DB] ${error.message}`);
throw error;
}
throw new Error('An unknown error occurred.');
}
};
}
export function createUniqueId(): string {
return crypto.randomUUID();
}
export function getCurrentDate(): string {
return new Date().toISOString();
}