Add multi-language support and new repository methods for book synchronization

- Extend repository methods to handle API requests for fetching books, chapters, characters, and other entities with multilingual support (`lang: 'fr' | 'en'`).
- Add `uploadBookForSync` logic to consolidate and decrypt book data for synchronization.
- Refactor schema migration logic to remove console logs and streamline table recreation.
- Enhance error handling across database repositories and IPC methods.
This commit is contained in:
natreex
2025-12-22 16:44:12 -05:00
parent ff530f3442
commit 515d469ba7
17 changed files with 666 additions and 113 deletions

View File

@@ -1,4 +1,4 @@
import {app, BrowserWindow, ipcMain, nativeImage, protocol, safeStorage, shell} from 'electron';
import {app, BrowserWindow, ipcMain, IpcMainInvokeEvent, nativeImage, protocol, safeStorage, shell} from 'electron';
import * as path from 'path';
import {fileURLToPath} from 'url';
import * as fs from 'fs';
@@ -152,13 +152,13 @@ ipcMain.handle('set-lang', (_event, lang: 'fr' | 'en') => {
});
// IPC Handler pour initialiser l'utilisateur après récupération depuis le serveur
ipcMain.handle('init-user', async (_event, userId: string) => {
ipcMain.handle('init-user', async (_event:IpcMainInvokeEvent, userId: string) => {
const storage:SecureStorage = getSecureStorage();
storage.set('userId', userId);
storage.set('lastUserId', userId);
try {
let encryptionKey: string | null = null;
let encryptionKey: string | null;
if (!hasUserEncryptionKey(userId)) {
encryptionKey = generateUserEncryptionKey(userId);
@@ -170,14 +170,12 @@ ipcMain.handle('init-user', async (_event, userId: string) => {
setUserEncryptionKey(userId, encryptionKey);
const savedKey:string = getUserEncryptionKey(userId);
console.log('[InitUser] Key verification after save:', savedKey ? `${savedKey.substring(0, 10)}...` : 'UNDEFINED');
if (!savedKey) {
throw new Error('Failed to save encryption key');
}
} else {
encryptionKey = getUserEncryptionKey(userId);
console.log('[InitUser] Using existing encryption key:', encryptionKey ? `${encryptionKey.substring(0, 10)}...` : 'UNDEFINED');
if (!encryptionKey) {
console.error('[InitUser] CRITICAL: Existing key is undefined, regenerating');
@@ -189,14 +187,15 @@ ipcMain.handle('init-user', async (_event, userId: string) => {
if (safeStorage.isEncryptionAvailable()) {
storage.save();
console.log('[InitUser] User ID and lastUserId saved to disk (encrypted)');
} else {
console.error('[InitUser] WARNING: Cannot save user ID - encryption not available');
return {
success: false,
error: 'Encryption is not available on this system'
};
}
return { success: true, keyCreated: !hasUserEncryptionKey(userId) };
} catch (error) {
console.error('[InitUser] Error managing encryption key:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
@@ -205,7 +204,6 @@ ipcMain.handle('init-user', async (_event, userId: string) => {
});
ipcMain.on('login-success', async (_event, token: string) => {
console.log('[Login] Received token, setting in storage');
const storage = getSecureStorage();
storage.set('authToken', token);
@@ -215,7 +213,7 @@ ipcMain.on('login-success', async (_event, token: string) => {
createMainWindow();
setTimeout(async () => {
setTimeout(async ():Promise<void> => {
try {
if (safeStorage.isEncryptionAvailable()) {
storage.save();
@@ -294,7 +292,6 @@ ipcMain.handle('db:user:sync', async (_event, data: SyncUserData): Promise<boole
return true;
}
} catch (error) {
console.error('[DB] Failed to sync user:', error);
throw error;
}
});
@@ -307,7 +304,6 @@ ipcMain.handle('generate-encryption-key', async (_event, userId: string) => {
const key:string = generateUserEncryptionKey(userId);
return { success: true, key };
} catch (error) {
console.error('Failed to generate encryption key:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
@@ -341,7 +337,6 @@ ipcMain.handle('db-initialize', (_event, userId: string, encryptionKey: string)
db.initialize(userId, encryptionKey);
return { success: true };
} catch (error) {
console.error('Failed to initialize database:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
@@ -349,9 +344,9 @@ ipcMain.handle('db-initialize', (_event, userId: string, encryptionKey: string)
}
});
app.whenReady().then(() => {
app.whenReady().then(():void => {
if (!isDev) {
const outPath = path.join(process.resourcesPath, 'app.asar.unpacked/out');
const outPath:string = path.join(process.resourcesPath, 'app.asar.unpacked/out');
protocol.handle('app', async (request) => {
let filePath:string = request.url.replace('app://', '').replace(/^\.\//, '');
@@ -394,17 +389,8 @@ app.whenReady().then(() => {
}
// Vérifier si un token existe (OS-encrypted storage)
const storage = getSecureStorage();
const token = storage.get('authToken');
const userId = storage.get('userId');
const offlineMode = storage.get<boolean>('offlineMode', false);
const lastUserId = storage.get<string>('lastUserId');
const hasPin = !!storage.get<string>(`pin-${lastUserId}`);
console.log('[Startup] Token exists:', !!token);
console.log('[Startup] UserId exists:', !!userId);
console.log('[Startup] Offline mode:', offlineMode);
console.log('[Startup] Has PIN:', hasPin);
const storage: SecureStorage = getSecureStorage();
const token: string | null = storage.get('authToken');
if (token) {
createMainWindow();
@@ -412,10 +398,10 @@ app.whenReady().then(() => {
createLoginWindow();
}
app.on('activate', () => {
app.on('activate', ():void => {
if (BrowserWindow.getAllWindows().length === 0) {
const storage = getSecureStorage();
const token = storage.get('authToken');
const storage: SecureStorage = getSecureStorage();
const token: string | null = storage.get('authToken');
if (token) {
createMainWindow();
} else {
@@ -425,7 +411,6 @@ app.whenReady().then(() => {
});
});
app.on('window-all-closed', () => {
// Quitter l'application quand toutes les fenêtres sont fermées
app.on('window-all-closed', ():void => {
app.quit();
});