Remove DataService and OfflineDataService, refactor book and character operations to use streamlined handlers in LocalSystem

- 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.
This commit is contained in:
natreex
2025-11-18 21:02:38 -05:00
parent d46aecc80d
commit d018e75be4
11 changed files with 222 additions and 1111 deletions

View File

@@ -1,15 +1,19 @@
import type { IpcMainInvokeEvent } from 'electron';
import Store from 'electron-store';
// Electron store instance for session management
// ============================================================
// 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'
});
// ============================================================
// SESSION MANAGEMENT - Retrieve userId and lang from store
// ============================================================
/**
* Get userId from electron-store
* Set during login via 'login-success' event
@@ -27,122 +31,67 @@ function getLangFromSession(): '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
// UNIVERSAL HANDLER - Like a Fastify route
// Automatically injects: userId, lang
// Optional body parameter (for GET, POST, PUT, DELETE)
// Generic return type (void, object, etc.)
// ============================================================
/**
* Auto-handler with 0 parameters
* Automatically injects: userId, lang
* 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
* ipcMain.handle('db:user:get', createAutoHandler<UserProps>(
* function(userId: string, lang: 'fr' | 'en') {
* return User.getUser(userId, lang);
* }
* ));
* // 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')
*
* // Frontend call (no params needed):
* const user = await window.electron.invoke('db:user:get');
* @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 createAutoHandler<TReturn>(
handler: (userId: string, lang: 'fr' | 'en') => TReturn | Promise<TReturn>
): (event: IpcMainInvokeEvent) => Promise<TReturn> {
return async function(event: IpcMainInvokeEvent): Promise<TReturn> {
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();
@@ -151,7 +100,7 @@ export function createAutoHandler<TReturn>(
}
try {
return await handler(userId, lang);
return await handler(userId, body as TBody, lang);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`[DB] ${error.message}`);
@@ -161,122 +110,3 @@ export function createAutoHandler<TReturn>(
}
};
}
/**
* 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();
}

View File

@@ -254,7 +254,7 @@ export default class Book {
return BookRepo.insertBook(id,userId,encryptedTitle,hashedTitle,encryptedSubTitle,hashedSubTitle,encryptedSummary,type,serie,publicationDate,desiredWordCount,lang);
}
public static async getBook(userId:string,bookId: string, lang: 'fr' | 'en' = 'fr'): Promise<BookProps> {
public static async getBook(userId:string,bookId: string): Promise<BookProps> {
const book:Book = new Book(bookId);
await book.getBookInfos(userId);
return {

View File

@@ -1,34 +1,30 @@
import { ipcMain } from 'electron';
import {
createDbHandler2,
// Auto-handlers: automatically inject userId AND lang
createAutoHandler,
createAutoHandler1
} from '../database/LocalSystem.js';
import { createHandler } from '../database/LocalSystem.js';
import Book from '../database/models/Book.js';
import type { BookProps, GuideLine, GuideLineAI, Act, Issue, WorldProps } from '../database/models/Book.js';
ipcMain.handle(
'db:book:getAll',
createAutoHandler<BookProps[]>(
async function(userId: string, lang: 'fr' | 'en') {
// ============================================================
// 1. GET /books - Get all books
// ============================================================
ipcMain.handle('db:book:books', createHandler<void, BookProps[]>(
async function(userId: string, _body: void, lang: 'fr' | 'en'):Promise<BookProps[]> {
return await Book.getBooks(userId, lang);
}
)
);
ipcMain.handle(
'db:book:get',
createAutoHandler1<string, BookProps>(
async function(userId: string, bookId: string, lang: 'fr' | 'en') {
return await Book.getBook(bookId, userId, lang);
// ============================================================
// 2. GET /book/:id - Get single book
// ============================================================
ipcMain.handle('db:book:bookBasicInformation', createHandler<string, BookProps>(
async function(userId: string, bookId: string, lang: 'fr' | 'en'):Promise<BookProps> {
return await Book.getBook(bookId, userId);
}
)
);
// Frontend call: await window.electron.invoke('db:book:get', bookId);
// ============================================================
// 3. POST /book/basic-information
// 3. POST /book/basic-information - Update book basic info
// ============================================================
interface UpdateBookBasicData {
title: string;
@@ -41,7 +37,7 @@ interface UpdateBookBasicData {
ipcMain.handle(
'db:book:updateBasicInformation',
createAutoHandler1<UpdateBookBasicData, boolean>(
createHandler<UpdateBookBasicData, boolean>(
function(userId: string, data: UpdateBookBasicData, lang: 'fr' | 'en') {
return Book.updateBookBasicInformation(
userId,
@@ -56,22 +52,23 @@ ipcMain.handle(
}
)
);
// Frontend call: await window.electron.invoke('db:book:updateBasicInformation', data);
// Frontend: invoke('db:book:updateBasicInformation', data)
// ============================================================
// 4. GET /book/guide-line
// 4. GET /book/guide-line - Get guideline
// ============================================================
ipcMain.handle(
'db:book:guideline:get',
createAutoHandler1<string, GuideLine | null>(
createHandler<string, GuideLine | null>(
async function(userId: string, bookId: string, lang: 'fr' | 'en') {
return await Book.getGuideLine(userId, bookId, lang);
}
)
);
// Frontend: invoke('db:book:guideline:get', bookId)
// ============================================================
// 5. POST /book/guide-line
// 5. POST /book/guide-line - Update guideline
// ============================================================
interface UpdateGuideLineData {
bookId: string;
@@ -89,7 +86,7 @@ interface UpdateGuideLineData {
ipcMain.handle(
'db:book:guideline:update',
createAutoHandler1<UpdateGuideLineData, boolean>(
createHandler<UpdateGuideLineData, boolean>(
async function(userId: string, data: UpdateGuideLineData, lang: 'fr' | 'en') {
return await Book.updateGuideLine(
userId,
@@ -109,9 +106,10 @@ ipcMain.handle(
}
)
);
// Frontend: invoke('db:book:guideline:update', data)
// ============================================================
// 6. GET /book/story
// 6. GET /book/story - Get story data (acts + issues)
// ============================================================
interface StoryData {
acts: Act[];
@@ -120,7 +118,7 @@ interface StoryData {
ipcMain.handle(
'db:book:story:get',
createDbHandler2<string, 'fr' | 'en', StoryData>(
createHandler<string, StoryData>(
async function(userId: string, bookId: string, lang: 'fr' | 'en') {
const acts = await Book.getActsData(userId, bookId, lang);
const issues = await Book.getIssuesFromBook(userId, bookId, lang);
@@ -131,9 +129,10 @@ ipcMain.handle(
}
)
);
// Frontend: invoke('db:book:story:get', bookId)
// ============================================================
// 7. POST /book/story
// 7. POST /book/story - Update story
// TODO: Implement updateStory in Book.ts
// ============================================================
// interface StoryUpdateData {
@@ -144,15 +143,16 @@ ipcMain.handle(
//
// ipcMain.handle(
// 'db:book:story:update',
// createDbHandler2<StoryUpdateData, 'fr' | 'en', boolean>(
// createHandler<StoryUpdateData, boolean>(
// function(userId: string, data: StoryUpdateData, lang: 'fr' | 'en') {
// return Book.updateStory(userId, data.bookId, data.acts, data.mainChapters, lang);
// }
// )
// );
// // Frontend: invoke('db:book:story:update', data)
// ============================================================
// 8. POST /book/add
// 8. POST /book/add - Create new book
// ============================================================
interface CreateBookData {
title: string;
@@ -166,7 +166,7 @@ interface CreateBookData {
ipcMain.handle(
'db:book:create',
createDbHandler2<CreateBookData, 'fr' | 'en', string>(
createHandler<CreateBookData, string>(
function(userId: string, data: CreateBookData, lang: 'fr' | 'en') {
return Book.addBook(
null,
@@ -183,22 +183,29 @@ ipcMain.handle(
}
)
);
// Frontend: invoke('db:book:create', data)
// ============================================================
// 9. POST /book/cover
// ============================================================
// 9. POST /book/cover - Update book cover
// TODO: Implement updateBookCover in Book.ts
// ============================================================
// interface UpdateCoverData {
// bookId: string;
// coverImageName: string;
// }
//
// ipcMain.handle(
// 'db:book:cover:update',
// createDbHandler3<string, string, 'fr' | 'en', boolean>(
// function(userId: string, bookId: string, coverImageName: string, lang: 'fr' | 'en') {
// return Book.updateBookCover(userId, bookId, coverImageName, lang);
// createHandler<UpdateCoverData, boolean>(
// function(userId: string, data: UpdateCoverData, lang: 'fr' | 'en') {
// return Book.updateBookCover(userId, data.bookId, data.coverImageName, lang);
// }
// )
// );
// // Frontend: invoke('db:book:cover:update', { bookId, coverImageName })
// ============================================================
// 10. POST /book/incident/new
// 10. POST /book/incident/new - Add incident
// ============================================================
interface AddIncidentData {
bookId: string;
@@ -207,15 +214,16 @@ interface AddIncidentData {
ipcMain.handle(
'db:book:incident:add',
createDbHandler2<AddIncidentData, 'fr' | 'en', string>(
createHandler<AddIncidentData, string>(
function(userId: string, data: AddIncidentData, lang: 'fr' | 'en') {
return Book.addNewIncident(userId, data.bookId, data.name, lang);
}
)
);
// Frontend: invoke('db:book:incident:add', { bookId, name })
// ============================================================
// 11. DELETE /book/incident/remove
// 11. DELETE /book/incident/remove - Remove incident
// ============================================================
interface RemoveIncidentData {
bookId: string;
@@ -224,15 +232,16 @@ interface RemoveIncidentData {
ipcMain.handle(
'db:book:incident:remove',
createDbHandler2<RemoveIncidentData, 'fr' | 'en', boolean>(
createHandler<RemoveIncidentData, boolean>(
function(userId: string, data: RemoveIncidentData, lang: 'fr' | 'en') {
return Book.removeIncident(userId, data.bookId, data.incidentId, lang);
}
)
);
// Frontend: invoke('db:book:incident:remove', { bookId, incidentId })
// ============================================================
// 12. POST /book/plot/new
// 12. POST /book/plot/new - Add plot point
// ============================================================
interface AddPlotPointData {
bookId: string;
@@ -242,7 +251,7 @@ interface AddPlotPointData {
ipcMain.handle(
'db:book:plot:add',
createDbHandler2<AddPlotPointData, 'fr' | 'en', string>(
createHandler<AddPlotPointData, string>(
function(userId: string, data: AddPlotPointData, lang: 'fr' | 'en') {
return Book.addNewPlotPoint(
userId,
@@ -254,21 +263,23 @@ ipcMain.handle(
}
)
);
// Frontend: invoke('db:book:plot:add', { bookId, name, incidentId })
// ============================================================
// 13. DELETE /book/plot/remove
// 13. DELETE /book/plot/remove - Remove plot point
// ============================================================
ipcMain.handle(
'db:book:plot:remove',
createDbHandler2<string, 'fr' | 'en', boolean>(
createHandler<string, boolean>(
function(userId: string, plotPointId: string, lang: 'fr' | 'en') {
return Book.removePlotPoint(userId, plotPointId, lang);
}
)
);
// Frontend: invoke('db:book:plot:remove', plotPointId)
// ============================================================
// 14. POST /book/issue/add
// 14. POST /book/issue/add - Add issue
// ============================================================
interface AddIssueData {
bookId: string;
@@ -277,39 +288,42 @@ interface AddIssueData {
ipcMain.handle(
'db:book:issue:add',
createDbHandler2<AddIssueData, 'fr' | 'en', string>(
createHandler<AddIssueData, string>(
function(userId: string, data: AddIssueData, lang: 'fr' | 'en') {
return Book.addNewIssue(userId, data.bookId, data.name, lang);
}
)
);
// Frontend: invoke('db:book:issue:add', { bookId, name })
// ============================================================
// 15. DELETE /book/issue/remove
// 15. DELETE /book/issue/remove - Remove issue
// ============================================================
ipcMain.handle(
'db:book:issue:remove',
createDbHandler2<string, 'fr' | 'en', boolean>(
createHandler<string, boolean>(
function(userId: string, issueId: string, lang: 'fr' | 'en') {
return Book.removeIssue(userId, issueId, lang);
}
)
);
// Frontend: invoke('db:book:issue:remove', issueId)
// ============================================================
// 16. GET /book/worlds
// 16. GET /book/worlds - Get worlds for book
// ============================================================
ipcMain.handle(
'db:book:worlds:get',
createDbHandler2<string, 'fr' | 'en', WorldProps[]>(
createHandler<string, WorldProps[]>(
function(userId: string, bookId: string, lang: 'fr' | 'en') {
return Book.getWorlds(userId, bookId, lang);
}
)
);
// Frontend: invoke('db:book:worlds:get', bookId)
// ============================================================
// 17. POST /book/world/add
// 17. POST /book/world/add - Add world
// ============================================================
interface AddWorldData {
bookId: string;
@@ -318,15 +332,16 @@ interface AddWorldData {
ipcMain.handle(
'db:book:world:add',
createDbHandler2<AddWorldData, 'fr' | 'en', string>(
createHandler<AddWorldData, string>(
function(userId: string, data: AddWorldData, lang: 'fr' | 'en') {
return Book.addNewWorld(userId, data.bookId, data.worldName, lang);
}
)
);
// Frontend: invoke('db:book:world:add', { bookId, worldName })
// ============================================================
// 18. POST /book/world/element/add
// 18. POST /book/world/element/add - Add element to world
// ============================================================
interface AddWorldElementData {
worldId: string;
@@ -336,7 +351,7 @@ interface AddWorldElementData {
ipcMain.handle(
'db:book:world:element:add',
createDbHandler2<AddWorldElementData, 'fr' | 'en', string>(
createHandler<AddWorldElementData, string>(
function(userId: string, data: AddWorldElementData, lang: 'fr' | 'en') {
return Book.addNewElementToWorld(
userId,
@@ -348,71 +363,77 @@ ipcMain.handle(
}
)
);
// Frontend: invoke('db:book:world:element:add', { worldId, elementName, elementType })
// ============================================================
// 19. DELETE /book/world/element/delete
// 19. DELETE /book/world/element/delete - Remove element from world
// ============================================================
ipcMain.handle(
'db:book:world:element:remove',
createDbHandler2<string, 'fr' | 'en', boolean>(
createHandler<string, boolean>(
function(userId: string, elementId: string, lang: 'fr' | 'en') {
return Book.removeElementFromWorld(userId, elementId, lang);
}
)
);
// Frontend: invoke('db:book:world:element:remove', elementId)
// ============================================================
// 20. PUT /book/world/update
// 20. PUT /book/world/update - Update world
// TODO: Implement updateWorld in Book.ts
// ============================================================
// ipcMain.handle(
// 'db:book:world:update',
// createDbHandler2<WorldProps, 'fr' | 'en', boolean>(
// createHandler<WorldProps, boolean>(
// function(userId: string, world: WorldProps, lang: 'fr' | 'en') {
// return Book.updateWorld(userId, world, lang);
// }
// )
// );
// // Frontend: invoke('db:book:world:update', worldData)
// ============================================================
// 21. DELETE /book/cover/delete
// 21. DELETE /book/cover/delete - Delete book cover
// TODO: Implement deleteCoverPicture in Book.ts
// ============================================================
// ipcMain.handle(
// 'db:book:cover:delete',
// createDbHandler2<string, 'fr' | 'en', boolean>(
// createHandler<string, boolean>(
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
// return Book.deleteCoverPicture(userId, bookId, lang);
// }
// )
// );
// // Frontend: invoke('db:book:cover:delete', bookId)
// ============================================================
// 22. DELETE /book/delete
// 22. DELETE /book/delete - Delete book
// ============================================================
ipcMain.handle(
'db:book:delete',
createDbHandler2<string, 'fr' | 'en', boolean>(
createHandler<string, boolean>(
function(userId: string, bookId: string, lang: 'fr' | 'en') {
return Book.removeBook(userId, bookId, lang);
}
)
);
// Frontend: invoke('db:book:delete', bookId)
// ============================================================
// 23. GET /book/ai/guideline
// 23. GET /book/ai/guideline - Get AI guideline
// ============================================================
ipcMain.handle(
'db:book:guideline:ai:get',
createDbHandler2<string, 'fr' | 'en', GuideLineAI>(
createHandler<string, GuideLineAI>(
function(userId: string, bookId: string, lang: 'fr' | 'en') {
return Book.getGuideLineAI(bookId, userId, lang);
}
)
);
// Frontend: invoke('db:book:guideline:ai:get', bookId)
// ============================================================
// 24. POST /book/ai/guideline (set)
// 24. POST /book/ai/guideline (set) - Set AI guideline
// ============================================================
interface SetAIGuideLineData {
bookId: string;
@@ -427,7 +448,7 @@ interface SetAIGuideLineData {
ipcMain.handle(
'db:book:guideline:ai:set',
createDbHandler2<SetAIGuideLineData, 'fr' | 'en', boolean>(
createHandler<SetAIGuideLineData, boolean>(
function(userId: string, data: SetAIGuideLineData, lang: 'fr' | 'en') {
return Book.setAIGuideLine(
data.bookId,
@@ -444,48 +465,52 @@ ipcMain.handle(
}
)
);
// Frontend: invoke('db:book:guideline:ai:set', data)
// ============================================================
// 25. GET /book/transform/epub
// ============================================================
// 25. GET /book/transform/epub - Export to EPUB
// TODO: Implement transformToEpub in Book.ts
// ============================================================
// ipcMain.handle(
// 'db:book:export:epub',
// createDbHandler2<string, 'fr' | 'en', ExportData>(
// createHandler<string, ExportData>(
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
// return Book.transformToEpub(userId, bookId, lang);
// }
// )
// );
// // Frontend: invoke('db:book:export:epub', bookId)
// ============================================================
// 26. GET /book/transform/pdf
// ============================================================
// 26. GET /book/transform/pdf - Export to PDF
// TODO: Implement transformToPDF in Book.ts
// ============================================================
// ipcMain.handle(
// 'db:book:export:pdf',
// createDbHandler2<string, 'fr' | 'en', ExportData>(
// createHandler<string, ExportData>(
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
// return Book.transformToPDF(userId, bookId, lang);
// }
// )
// );
// // Frontend: invoke('db:book:export:pdf', bookId)
// ============================================================
// 27. GET /book/transform/docx
// ============================================================
// 27. GET /book/transform/docx - Export to DOCX
// TODO: Implement transformToDOCX in Book.ts
// ============================================================
// ipcMain.handle(
// 'db:book:export:docx',
// createDbHandler2<string, 'fr' | 'en', ExportData>(
// createHandler<string, ExportData>(
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
// return Book.transformToDOCX(userId, bookId, lang);
// }
// )
// );
// // Frontend: invoke('db:book:export:docx', bookId)
// ============================================================
// 28. GET /book/tags
// 28. GET /book/tags - Get tags from book
// TODO: Implement getTagsFromBook in Book.ts
// ============================================================
// interface BookTags {
@@ -497,11 +522,12 @@ ipcMain.handle(
//
// ipcMain.handle(
// 'db:book:tags:get',
// createDbHandler2<string, 'fr' | 'en', BookTags>(
// createHandler<string, BookTags>(
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
// return Book.getTagsFromBook(userId, bookId, lang);
// }
// )
// );
// // Frontend: invoke('db:book:tags:get', bookId)
console.log('[IPC] Book handlers registered');

View File

@@ -6,6 +6,9 @@ import Store from 'electron-store';
import * as fs from 'fs';
import { getDatabaseService } from './database/database.service.js';
// Import IPC handlers
import './ipc/book.ipc.js';
// Fix pour __dirname en ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

View File

@@ -1,36 +1,34 @@
const { contextBridge, ipcRenderer } = require('electron');
// Exposer des APIs sécurisées au renderer process
/**
* Exposer des APIs sécurisées au renderer process
* Utilise invoke() générique pour tous les appels IPC
*/
contextBridge.exposeInMainWorld('electron', {
// Platform info
platform: process.platform,
// Token management
// Generic invoke method - use this for all IPC calls
invoke: (channel: string, ...args: any[]) => ipcRenderer.invoke(channel, ...args),
// Token management (shortcuts for convenience)
getToken: () => ipcRenderer.invoke('get-token'),
setToken: (token: string) => ipcRenderer.invoke('set-token', token),
removeToken: () => ipcRenderer.invoke('remove-token'),
// Language management
// Language management (shortcuts for convenience)
getLang: () => ipcRenderer.invoke('get-lang'),
setLang: (lang: 'fr' | 'en') => ipcRenderer.invoke('set-lang', lang),
// Auth events
// Auth events (use send for one-way communication)
loginSuccess: (token: string, userId: string) => ipcRenderer.send('login-success', token, userId),
logout: () => ipcRenderer.send('logout'),
// Database operations
// Encryption key management (shortcuts for convenience)
generateEncryptionKey: (userId: string) => ipcRenderer.invoke('generate-encryption-key', userId),
getUserEncryptionKey: (userId: string) => ipcRenderer.invoke('get-user-encryption-key', userId),
setUserEncryptionKey: (userId: string, encryptionKey: string) => ipcRenderer.invoke('set-user-encryption-key', userId, encryptionKey),
// Database initialization (shortcut for convenience)
dbInitialize: (userId: string, encryptionKey: string) => ipcRenderer.invoke('db-initialize', userId, encryptionKey),
dbGetBooks: () => ipcRenderer.invoke('db-get-books'),
dbGetBook: (bookId: string) => ipcRenderer.invoke('db-get-book', bookId),
dbSaveBook: (book: any, authorId?: string) => ipcRenderer.invoke('db-save-book', book, authorId),
dbDeleteBook: (bookId: string) => ipcRenderer.invoke('db-delete-book', bookId),
dbSaveChapter: (chapter: any, bookId: string, contentId?: string) => ipcRenderer.invoke('db-save-chapter', chapter, bookId, contentId),
dbGetCharacters: (bookId: string) => ipcRenderer.invoke('db-get-characters', bookId),
dbSaveCharacter: (character: any, bookId: string) => ipcRenderer.invoke('db-save-character', character, bookId),
dbGetConversations: (bookId: string) => ipcRenderer.invoke('db-get-conversations', bookId),
dbSaveConversation: (conversation: any, bookId: string) => ipcRenderer.invoke('db-save-conversation', conversation, bookId),
dbGetSyncStatus: () => ipcRenderer.invoke('db-get-sync-status'),
dbGetPendingChanges: (limit?: number) => ipcRenderer.invoke('db-get-pending-changes', limit),
});