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:
282
electron/database/LocalSystem.ts
Normal file
282
electron/database/LocalSystem.ts
Normal 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();
|
||||||
|
}
|
||||||
66
electron/database/System.ts
Normal file
66
electron/database/System.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { getDatabaseService } from './database.service.js';
|
||||||
|
import { encryptDataWithUserKey, decryptDataWithUserKey, hashElement } from './encryption.js';
|
||||||
|
import type { Database } from 'node-sqlite3-wasm';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
export default class System {
|
||||||
|
public static getDb(): Database {
|
||||||
|
const db: Database | null = getDatabaseService().getDb();
|
||||||
|
if (!db) {
|
||||||
|
throw new Error('Database not initialized');
|
||||||
|
}
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static encryptDataWithUserKey(data: string, userKey: string): string {
|
||||||
|
return encryptDataWithUserKey(data, userKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static decryptDataWithUserKey(encryptedData: string, userKey: string): string {
|
||||||
|
return decryptDataWithUserKey(encryptedData, userKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createUniqueId(): string {
|
||||||
|
return crypto.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
static htmlToText(htmlNode: string): string {
|
||||||
|
let text: string = htmlNode
|
||||||
|
.replace(/<\/?p[^>]*>/gi, '\n')
|
||||||
|
.replace(/<br\s*\/?>/gi, '\n')
|
||||||
|
.replace(/<\/?(span|h[1-6])[^>]*>/gi, '');
|
||||||
|
text = text
|
||||||
|
.replace(/'/g, "'")
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
text = text.replace(/\r?\n\s*\n/g, '\n');
|
||||||
|
text = text.replace(/[ \t]+/g, ' ');
|
||||||
|
|
||||||
|
return text.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getCurrentDate(): string {
|
||||||
|
return new Date().toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static dateToMySqlDate(isoDateString: string): string {
|
||||||
|
const dateObject: Date = new Date(isoDateString);
|
||||||
|
|
||||||
|
function padWithZeroes(value: number): string {
|
||||||
|
return value.toString().padStart(2, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
const year: number = dateObject.getFullYear();
|
||||||
|
const month: string = padWithZeroes(dateObject.getMonth() + 1);
|
||||||
|
const day: string = padWithZeroes(dateObject.getDate());
|
||||||
|
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static hashElement(element: string): string {
|
||||||
|
return hashElement(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
507
electron/ipc/book.ipc.ts
Normal file
507
electron/ipc/book.ipc.ts
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
import {
|
||||||
|
createDbHandler2,
|
||||||
|
// Auto-handlers: automatically inject userId AND lang
|
||||||
|
createAutoHandler,
|
||||||
|
createAutoHandler1
|
||||||
|
} 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') {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// Frontend call: await window.electron.invoke('db:book:get', bookId);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 3. POST /book/basic-information
|
||||||
|
// ============================================================
|
||||||
|
interface UpdateBookBasicData {
|
||||||
|
title: string;
|
||||||
|
subTitle: string;
|
||||||
|
summary: string;
|
||||||
|
publicationDate: string;
|
||||||
|
wordCount: number;
|
||||||
|
bookId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:updateBasicInformation',
|
||||||
|
createAutoHandler1<UpdateBookBasicData, boolean>(
|
||||||
|
function(userId: string, data: UpdateBookBasicData, lang: 'fr' | 'en') {
|
||||||
|
return Book.updateBookBasicInformation(
|
||||||
|
userId,
|
||||||
|
data.title,
|
||||||
|
data.subTitle,
|
||||||
|
data.summary,
|
||||||
|
data.publicationDate,
|
||||||
|
data.wordCount,
|
||||||
|
data.bookId,
|
||||||
|
lang
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// Frontend call: await window.electron.invoke('db:book:updateBasicInformation', data);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 4. GET /book/guide-line
|
||||||
|
// ============================================================
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:guideline:get',
|
||||||
|
createAutoHandler1<string, GuideLine | null>(
|
||||||
|
async function(userId: string, bookId: string, lang: 'fr' | 'en') {
|
||||||
|
return await Book.getGuideLine(userId, bookId, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 5. POST /book/guide-line
|
||||||
|
// ============================================================
|
||||||
|
interface UpdateGuideLineData {
|
||||||
|
bookId: string;
|
||||||
|
tone: string | null;
|
||||||
|
atmosphere: string | null;
|
||||||
|
writingStyle: string | null;
|
||||||
|
themes: string | null;
|
||||||
|
symbolism: string | null;
|
||||||
|
motifs: string | null;
|
||||||
|
narrativeVoice: string | null;
|
||||||
|
pacing: string | null;
|
||||||
|
keyMessages: string | null;
|
||||||
|
intendedAudience: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:guideline:update',
|
||||||
|
createAutoHandler1<UpdateGuideLineData, boolean>(
|
||||||
|
async function(userId: string, data: UpdateGuideLineData, lang: 'fr' | 'en') {
|
||||||
|
return await Book.updateGuideLine(
|
||||||
|
userId,
|
||||||
|
data.bookId,
|
||||||
|
data.tone,
|
||||||
|
data.atmosphere,
|
||||||
|
data.writingStyle,
|
||||||
|
data.themes,
|
||||||
|
data.symbolism,
|
||||||
|
data.motifs,
|
||||||
|
data.narrativeVoice,
|
||||||
|
data.pacing,
|
||||||
|
data.keyMessages,
|
||||||
|
data.intendedAudience,
|
||||||
|
lang
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 6. GET /book/story
|
||||||
|
// ============================================================
|
||||||
|
interface StoryData {
|
||||||
|
acts: Act[];
|
||||||
|
issues: Issue[];
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:story:get',
|
||||||
|
createDbHandler2<string, 'fr' | 'en', 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);
|
||||||
|
return {
|
||||||
|
acts,
|
||||||
|
issues
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 7. POST /book/story
|
||||||
|
// TODO: Implement updateStory in Book.ts
|
||||||
|
// ============================================================
|
||||||
|
// interface StoryUpdateData {
|
||||||
|
// bookId: string;
|
||||||
|
// acts: Act[];
|
||||||
|
// mainChapters: ChapterProps[];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ipcMain.handle(
|
||||||
|
// 'db:book:story:update',
|
||||||
|
// createDbHandler2<StoryUpdateData, 'fr' | 'en', boolean>(
|
||||||
|
// function(userId: string, data: StoryUpdateData, lang: 'fr' | 'en') {
|
||||||
|
// return Book.updateStory(userId, data.bookId, data.acts, data.mainChapters, lang);
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 8. POST /book/add
|
||||||
|
// ============================================================
|
||||||
|
interface CreateBookData {
|
||||||
|
title: string;
|
||||||
|
subTitle: string | null;
|
||||||
|
summary: string | null;
|
||||||
|
type: string;
|
||||||
|
serieId: number | null;
|
||||||
|
desiredReleaseDate: string | null;
|
||||||
|
desiredWordCount: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:create',
|
||||||
|
createDbHandler2<CreateBookData, 'fr' | 'en', string>(
|
||||||
|
function(userId: string, data: CreateBookData, lang: 'fr' | 'en') {
|
||||||
|
return Book.addBook(
|
||||||
|
null,
|
||||||
|
userId,
|
||||||
|
data.title,
|
||||||
|
data.subTitle || '',
|
||||||
|
data.summary || '',
|
||||||
|
data.type,
|
||||||
|
data.serieId || 0,
|
||||||
|
data.desiredReleaseDate || '',
|
||||||
|
data.desiredWordCount || 0,
|
||||||
|
lang
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 9. POST /book/cover
|
||||||
|
// ============================================================
|
||||||
|
// TODO: Implement updateBookCover in Book.ts
|
||||||
|
// 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);
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 10. POST /book/incident/new
|
||||||
|
// ============================================================
|
||||||
|
interface AddIncidentData {
|
||||||
|
bookId: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:incident:add',
|
||||||
|
createDbHandler2<AddIncidentData, 'fr' | 'en', string>(
|
||||||
|
function(userId: string, data: AddIncidentData, lang: 'fr' | 'en') {
|
||||||
|
return Book.addNewIncident(userId, data.bookId, data.name, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 11. DELETE /book/incident/remove
|
||||||
|
// ============================================================
|
||||||
|
interface RemoveIncidentData {
|
||||||
|
bookId: string;
|
||||||
|
incidentId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:incident:remove',
|
||||||
|
createDbHandler2<RemoveIncidentData, 'fr' | 'en', boolean>(
|
||||||
|
function(userId: string, data: RemoveIncidentData, lang: 'fr' | 'en') {
|
||||||
|
return Book.removeIncident(userId, data.bookId, data.incidentId, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 12. POST /book/plot/new
|
||||||
|
// ============================================================
|
||||||
|
interface AddPlotPointData {
|
||||||
|
bookId: string;
|
||||||
|
name: string;
|
||||||
|
incidentId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:plot:add',
|
||||||
|
createDbHandler2<AddPlotPointData, 'fr' | 'en', string>(
|
||||||
|
function(userId: string, data: AddPlotPointData, lang: 'fr' | 'en') {
|
||||||
|
return Book.addNewPlotPoint(
|
||||||
|
userId,
|
||||||
|
data.bookId,
|
||||||
|
data.incidentId,
|
||||||
|
data.name,
|
||||||
|
lang
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 13. DELETE /book/plot/remove
|
||||||
|
// ============================================================
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:plot:remove',
|
||||||
|
createDbHandler2<string, 'fr' | 'en', boolean>(
|
||||||
|
function(userId: string, plotPointId: string, lang: 'fr' | 'en') {
|
||||||
|
return Book.removePlotPoint(userId, plotPointId, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 14. POST /book/issue/add
|
||||||
|
// ============================================================
|
||||||
|
interface AddIssueData {
|
||||||
|
bookId: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:issue:add',
|
||||||
|
createDbHandler2<AddIssueData, 'fr' | 'en', string>(
|
||||||
|
function(userId: string, data: AddIssueData, lang: 'fr' | 'en') {
|
||||||
|
return Book.addNewIssue(userId, data.bookId, data.name, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 15. DELETE /book/issue/remove
|
||||||
|
// ============================================================
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:issue:remove',
|
||||||
|
createDbHandler2<string, 'fr' | 'en', boolean>(
|
||||||
|
function(userId: string, issueId: string, lang: 'fr' | 'en') {
|
||||||
|
return Book.removeIssue(userId, issueId, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 16. GET /book/worlds
|
||||||
|
// ============================================================
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:worlds:get',
|
||||||
|
createDbHandler2<string, 'fr' | 'en', WorldProps[]>(
|
||||||
|
function(userId: string, bookId: string, lang: 'fr' | 'en') {
|
||||||
|
return Book.getWorlds(userId, bookId, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 17. POST /book/world/add
|
||||||
|
// ============================================================
|
||||||
|
interface AddWorldData {
|
||||||
|
bookId: string;
|
||||||
|
worldName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:world:add',
|
||||||
|
createDbHandler2<AddWorldData, 'fr' | 'en', string>(
|
||||||
|
function(userId: string, data: AddWorldData, lang: 'fr' | 'en') {
|
||||||
|
return Book.addNewWorld(userId, data.bookId, data.worldName, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 18. POST /book/world/element/add
|
||||||
|
// ============================================================
|
||||||
|
interface AddWorldElementData {
|
||||||
|
worldId: string;
|
||||||
|
elementName: string;
|
||||||
|
elementType: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:world:element:add',
|
||||||
|
createDbHandler2<AddWorldElementData, 'fr' | 'en', string>(
|
||||||
|
function(userId: string, data: AddWorldElementData, lang: 'fr' | 'en') {
|
||||||
|
return Book.addNewElementToWorld(
|
||||||
|
userId,
|
||||||
|
data.worldId,
|
||||||
|
data.elementName,
|
||||||
|
data.elementType.toString(),
|
||||||
|
lang
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 19. DELETE /book/world/element/delete
|
||||||
|
// ============================================================
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:world:element:remove',
|
||||||
|
createDbHandler2<string, 'fr' | 'en', boolean>(
|
||||||
|
function(userId: string, elementId: string, lang: 'fr' | 'en') {
|
||||||
|
return Book.removeElementFromWorld(userId, elementId, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 20. PUT /book/world/update
|
||||||
|
// TODO: Implement updateWorld in Book.ts
|
||||||
|
// ============================================================
|
||||||
|
// ipcMain.handle(
|
||||||
|
// 'db:book:world:update',
|
||||||
|
// createDbHandler2<WorldProps, 'fr' | 'en', boolean>(
|
||||||
|
// function(userId: string, world: WorldProps, lang: 'fr' | 'en') {
|
||||||
|
// return Book.updateWorld(userId, world, lang);
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 21. DELETE /book/cover/delete
|
||||||
|
// TODO: Implement deleteCoverPicture in Book.ts
|
||||||
|
// ============================================================
|
||||||
|
// ipcMain.handle(
|
||||||
|
// 'db:book:cover:delete',
|
||||||
|
// createDbHandler2<string, 'fr' | 'en', boolean>(
|
||||||
|
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
|
||||||
|
// return Book.deleteCoverPicture(userId, bookId, lang);
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 22. DELETE /book/delete
|
||||||
|
// ============================================================
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:delete',
|
||||||
|
createDbHandler2<string, 'fr' | 'en', boolean>(
|
||||||
|
function(userId: string, bookId: string, lang: 'fr' | 'en') {
|
||||||
|
return Book.removeBook(userId, bookId, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 23. GET /book/ai/guideline
|
||||||
|
// ============================================================
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:guideline:ai:get',
|
||||||
|
createDbHandler2<string, 'fr' | 'en', GuideLineAI>(
|
||||||
|
function(userId: string, bookId: string, lang: 'fr' | 'en') {
|
||||||
|
return Book.getGuideLineAI(bookId, userId, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 24. POST /book/ai/guideline (set)
|
||||||
|
// ============================================================
|
||||||
|
interface SetAIGuideLineData {
|
||||||
|
bookId: string;
|
||||||
|
narrativeType: number;
|
||||||
|
dialogueType: number;
|
||||||
|
globalResume: string;
|
||||||
|
atmosphere: string;
|
||||||
|
verbeTense: number;
|
||||||
|
langue: number;
|
||||||
|
themes: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
'db:book:guideline:ai:set',
|
||||||
|
createDbHandler2<SetAIGuideLineData, 'fr' | 'en', boolean>(
|
||||||
|
function(userId: string, data: SetAIGuideLineData, lang: 'fr' | 'en') {
|
||||||
|
return Book.setAIGuideLine(
|
||||||
|
data.bookId,
|
||||||
|
userId,
|
||||||
|
data.narrativeType,
|
||||||
|
data.dialogueType,
|
||||||
|
data.globalResume,
|
||||||
|
data.atmosphere,
|
||||||
|
data.verbeTense,
|
||||||
|
data.langue,
|
||||||
|
data.themes,
|
||||||
|
lang
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 25. GET /book/transform/epub
|
||||||
|
// ============================================================
|
||||||
|
// TODO: Implement transformToEpub in Book.ts
|
||||||
|
// ipcMain.handle(
|
||||||
|
// 'db:book:export:epub',
|
||||||
|
// createDbHandler2<string, 'fr' | 'en', ExportData>(
|
||||||
|
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
|
||||||
|
// return Book.transformToEpub(userId, bookId, lang);
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 26. GET /book/transform/pdf
|
||||||
|
// ============================================================
|
||||||
|
// TODO: Implement transformToPDF in Book.ts
|
||||||
|
// ipcMain.handle(
|
||||||
|
// 'db:book:export:pdf',
|
||||||
|
// createDbHandler2<string, 'fr' | 'en', ExportData>(
|
||||||
|
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
|
||||||
|
// return Book.transformToPDF(userId, bookId, lang);
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 27. GET /book/transform/docx
|
||||||
|
// ============================================================
|
||||||
|
// TODO: Implement transformToDOCX in Book.ts
|
||||||
|
// ipcMain.handle(
|
||||||
|
// 'db:book:export:docx',
|
||||||
|
// createDbHandler2<string, 'fr' | 'en', ExportData>(
|
||||||
|
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
|
||||||
|
// return Book.transformToDOCX(userId, bookId, lang);
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 28. GET /book/tags
|
||||||
|
// TODO: Implement getTagsFromBook in Book.ts
|
||||||
|
// ============================================================
|
||||||
|
// interface BookTags {
|
||||||
|
// characters: Tag[];
|
||||||
|
// locations: Tag[];
|
||||||
|
// objects: Tag[];
|
||||||
|
// worldElements: Tag[];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ipcMain.handle(
|
||||||
|
// 'db:book:tags:get',
|
||||||
|
// createDbHandler2<string, 'fr' | 'en', BookTags>(
|
||||||
|
// function(userId: string, bookId: string, lang: 'fr' | 'en') {
|
||||||
|
// return Book.getTagsFromBook(userId, bookId, lang);
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
console.log('[IPC] Book handlers registered');
|
||||||
@@ -129,8 +129,19 @@ ipcMain.handle('remove-token', () => {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('login-success', (_event, token: string) => {
|
// IPC Handlers pour la gestion de la langue
|
||||||
|
ipcMain.handle('get-lang', () => {
|
||||||
|
return store.get('userLang', 'fr');
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('set-lang', (_event, lang: 'fr' | 'en') => {
|
||||||
|
store.set('userLang', lang);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('login-success', (_event, token: string, userId: string) => {
|
||||||
store.set('authToken', token);
|
store.set('authToken', token);
|
||||||
|
store.set('userId', userId);
|
||||||
|
|
||||||
if (loginWindow) {
|
if (loginWindow) {
|
||||||
loginWindow.close();
|
loginWindow.close();
|
||||||
@@ -141,6 +152,8 @@ ipcMain.on('login-success', (_event, token: string) => {
|
|||||||
|
|
||||||
ipcMain.on('logout', () => {
|
ipcMain.on('logout', () => {
|
||||||
store.delete('authToken');
|
store.delete('authToken');
|
||||||
|
store.delete('userId');
|
||||||
|
store.delete('userLang');
|
||||||
|
|
||||||
// Close database connection
|
// Close database connection
|
||||||
const db = getDatabaseService();
|
const db = getDatabaseService();
|
||||||
|
|||||||
@@ -9,8 +9,12 @@ contextBridge.exposeInMainWorld('electron', {
|
|||||||
setToken: (token: string) => ipcRenderer.invoke('set-token', token),
|
setToken: (token: string) => ipcRenderer.invoke('set-token', token),
|
||||||
removeToken: () => ipcRenderer.invoke('remove-token'),
|
removeToken: () => ipcRenderer.invoke('remove-token'),
|
||||||
|
|
||||||
|
// Language management
|
||||||
|
getLang: () => ipcRenderer.invoke('get-lang'),
|
||||||
|
setLang: (lang: 'fr' | 'en') => ipcRenderer.invoke('set-lang', lang),
|
||||||
|
|
||||||
// Auth events
|
// Auth events
|
||||||
loginSuccess: (token: string) => ipcRenderer.send('login-success', token),
|
loginSuccess: (token: string, userId: string) => ipcRenderer.send('login-success', token, userId),
|
||||||
logout: () => ipcRenderer.send('logout'),
|
logout: () => ipcRenderer.send('logout'),
|
||||||
|
|
||||||
// Database operations
|
// Database operations
|
||||||
|
|||||||
Reference in New Issue
Block a user