From 0bafaadeccd6e36e3a90fb69ac8755334916644c Mon Sep 17 00:00:00 2001 From: natreex Date: Tue, 18 Nov 2025 22:17:22 -0500 Subject: [PATCH] Add user data synchronization and database IPC handlers - Introduce `db:user:sync` to synchronize user data with the local database during initialization. - Add `db:user:info` and `db:user:update` IPC handlers for fetching and updating user information. - Update `User` model and repository to support extended user fields and improved structure. - Dynamically import user models in main process to avoid circular dependencies. - Refactor database initialization in the app to include user sync logic with error handling. --- app/page.tsx | 16 ++++++- electron/database/models/User.ts | 24 +++++++++- .../database/repositories/user.repository.ts | 24 +++++++--- electron/ipc/user.ipc.ts | 26 ++++++++++ electron/main.ts | 47 +++++++++++++++++++ 5 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 electron/ipc/user.ipc.ts diff --git a/app/page.tsx b/app/page.tsx index 18ff523..4878fa9 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -226,7 +226,21 @@ function ScribeContent() { const dbInitialized = await initializeDatabase(user.id); if (dbInitialized) { console.log('Database initialized successfully'); - // TODO: Sync initial des données du serveur vers la DB locale + + // Sync user to local DB (only if not exists) + try { + await window.electron.invoke('db:user:sync', { + userId: user.id, + firstName: user.name, + lastName: user.lastName, + username: user.username, + email: user.email + }); + console.log('User synced to local DB'); + } catch (syncError) { + console.error('Failed to sync user to local DB:', syncError); + // Non-blocking error, continue anyway + } } } catch (error) { console.error('Failed to initialize database:', error); diff --git a/electron/database/models/User.ts b/electron/database/models/User.ts index d606a54..28c41d6 100644 --- a/electron/database/models/User.ts +++ b/electron/database/models/User.ts @@ -15,6 +15,26 @@ export interface GuideTour { [key: string]: boolean; } +interface BookSummary { + bookId: string; + title: string; + subTitle?: string; +} + +export interface UserInfoResponse { + id: string; + name: string; + lastName: string; + username: string; + email: string; + accountVerified: boolean; + authorName: string; + groupId: number; + termsAccepted: boolean; + guideTour: any[]; + books: BookSummary[]; +} + export default class User{ private readonly id:string; @@ -52,7 +72,7 @@ export default class User{ this.termsAccepted = data.term_accepted === 1; } - public static async returnUserInfos(userId: string) { + public static async returnUserInfos(userId: string):Promise { const user: User = new User(userId); await user.getUserInfos(); const books: BookProps[] = await Book.getBooks(userId); @@ -68,7 +88,7 @@ export default class User{ groupId: user.getGroupId(), termsAccepted: user.isTermsAccepted(), guideTour: guideTour, - books: books.map((book: BookProps) => { + books: books.map((book: BookProps):BookSummary => { return { bookId: book.id, title: book.title, diff --git a/electron/database/repositories/user.repository.ts b/electron/database/repositories/user.repository.ts index c8c64ff..b0cf3c1 100644 --- a/electron/database/repositories/user.repository.ts +++ b/electron/database/repositories/user.repository.ts @@ -15,7 +15,6 @@ export interface UserInfosQueryResponse extends Record { writing_level: number, rite_points: number, user_group: number, - credits_balance: number, } export interface CredentialResponse { @@ -53,10 +52,23 @@ export default class UserRepo { try { const db: Database = System.getDb(); const query = `INSERT INTO erit_users (user_id, first_name, last_name, username, email, origin_email, - origin_username, term_accepted, - account_verified) - VALUES (?, ?, ?, ?, ?, ?, ?, 0, 1)`; - const values: (string | null | number)[] = [uuId, firstName, lastName, username, email, originEmail, originUsername]; + origin_username, plateform, term_accepted, + account_verified, user_meta, reg_date) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + const values: (string | null | number)[] = [ + uuId, + firstName, + lastName, + username, + email, + originEmail, + originUsername, + 'desktop', // plateform + 0, // term_accepted + 1, // account_verified + '{}', // user_meta (JSON empty object) + Date.now() // reg_date (current timestamp) + ]; result = db.run(query, values); } catch (e: unknown) { if (e instanceof Error) { @@ -78,7 +90,7 @@ export default class UserRepo { let result; try { const db: Database = System.getDb(); - result = db.get('SELECT `first_name`, `last_name`, `username`, `email`, `plateform`, `term_accepted`, `account_verified`, user_meta, author_name, erite_points AS rite_points, user_group, credits_balance FROM `erit_users` AS users LEFT JOIN user_preferences AS preference ON users.user_id=preference.user_id WHERE users.user_id=?', [userId]); + result = db.get('SELECT `first_name`, `last_name`, `username`, `email`, `plateform`, `term_accepted`, `account_verified`, user_meta, author_name, erite_points AS rite_points, user_group FROM `erit_users` WHERE user_id=?', [userId]); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); diff --git a/electron/ipc/user.ipc.ts b/electron/ipc/user.ipc.ts new file mode 100644 index 0000000..412e490 --- /dev/null +++ b/electron/ipc/user.ipc.ts @@ -0,0 +1,26 @@ +import { ipcMain } from 'electron'; +import { createHandler } from '../database/LocalSystem.js'; +import User, {UserInfoResponse} from '../database/models/User.js'; + +interface UpdateUserData { + firstName: string; + lastName: string; + username: string; + email: string; + authorName?: string; +} + +// GET /user/info - Get user info from local DB +ipcMain.handle('db:user:info', createHandler( + async function(userId: string, _body: void, _lang: 'fr' | 'en'):Promise { + return await User.returnUserInfos(userId); + } +)); + +// PUT /user/update - Update user info in local DB +ipcMain.handle('db:user:update', createHandler( + async function(userId: string, data: UpdateUserData, lang: 'fr' | 'en'):Promise { + const userKey = ''; + return await User.updateUserInfos(userKey, userId, data.firstName, data.lastName, data.username, data.email, data.authorName, lang); + } +)); diff --git a/electron/main.ts b/electron/main.ts index 4b0fe43..3f27341 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -8,6 +8,7 @@ import { getDatabaseService } from './database/database.service.js'; // Import IPC handlers import './ipc/book.ipc.js'; +import './ipc/user.ipc.js'; // Fix pour __dirname en ES modules const __filename = fileURLToPath(import.meta.url); @@ -169,6 +170,52 @@ ipcMain.on('logout', () => { createLoginWindow(); }); +// ========== USER SYNC (PRE-AUTHENTICATION) ========== + +interface SyncUserData { + userId: string; + firstName: string; + lastName: string; + username: string; + email: string; +} + +ipcMain.handle('db:user:sync', async (_event, data: SyncUserData): Promise => { + try { + // Import User models dynamically to avoid circular dependencies + const { default: User } = await import('./database/models/User.js'); + const { default: UserRepo } = await import('./database/repositories/user.repository.js'); + + const lang: 'fr' | 'en' = 'fr'; + + // Check if user already exists in local DB + try { + UserRepo.fetchUserInfos(data.userId, lang); + // User exists, no need to sync + console.log(`[DB] User ${data.userId} already exists in local DB, skipping sync`); + return true; + } catch (error) { + // User doesn't exist, create it + console.log(`[DB] User ${data.userId} not found, creating in local DB`); + + await User.addUser( + data.userId, + data.firstName, + data.lastName, + data.username, + data.email, + '', // Password not needed for local DB + lang + ); + console.log(`[DB] User ${data.userId} synced successfully`); + return true; + } + } catch (error) { + console.error('[DB] Failed to sync user:', error); + throw error; + } +}); + // ========== DATABASE IPC HANDLERS ========== /**