From d9bf089e32d0875067817ee3829b655a4d5d075a Mon Sep 17 00:00:00 2001 From: natreex Date: Mon, 12 Jan 2026 09:57:37 -0500 Subject: [PATCH] Refactor `BookRepo` methods and improve error handling - Add JSDoc comments for better maintainability and code clarity in `BookRepo` methods. - Streamline query definitions using variables to improve readability. - Consolidate error handling logic across all methods. - Ensure multilingual support in error messages for consistent user feedback. - Remove redundant error branches and simplify unknown error processing. --- .../database/repositories/act.repository.ts | 184 +++-- .../database/repositories/book.repository.ts | 387 ++++++---- .../repositories/chapter.repository.ts | 720 +++++++++++------- .../repositories/chaptercontent.repository.ts | 304 ++++++-- .../repositories/character.repository.ts | 444 +++++++---- .../repositories/guideline.repository.ts | 420 ++++++++-- .../repositories/incident.repository.ts | 219 ++++-- .../database/repositories/issue.repository.ts | 220 ++++-- .../repositories/location.repository.ts | 702 ++++++++++++----- .../repositories/plotpoint.repository.ts | 224 ++++-- .../database/repositories/user.repository.ts | 168 +++- .../database/repositories/world.repository.ts | 462 +++++++---- 12 files changed, 3202 insertions(+), 1252 deletions(-) diff --git a/electron/database/repositories/act.repository.ts b/electron/database/repositories/act.repository.ts index 77f3465..0a0456d 100644 --- a/electron/database/repositories/act.repository.ts +++ b/electron/database/repositories/act.repository.ts @@ -22,13 +22,23 @@ export interface ActQuery extends Record { } export default class ActRepository { + /** + * Fetches all acts for a specific book and user. + * @param userId - The unique identifier of the user. + * @param bookId - The unique identifier of the book. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns An array of ActQuery objects containing act index and summary. + * @throws Error if the database operation fails. + */ public static fetchAllActs(userId: string, bookId: string, lang: 'fr' | 'en'): ActQuery[] { try { const db: Database = System.getDb(); - return db.all('SELECT act_index, summary FROM book_act_summaries WHERE book_id=? AND user_id=?', [bookId, userId]) as ActQuery[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT act_index, summary FROM book_act_summaries WHERE book_id=? AND user_id=?'; + const params: SQLiteValue[] = [bookId, userId]; + return db.all(query, params) as ActQuery[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les actes.` : `Unable to retrieve acts.`); } else { console.error("An unknown error occurred."); @@ -36,14 +46,28 @@ export default class ActRepository { } } } + + /** + * Updates the summary of an existing act. + * @param userId - The unique identifier of the user. + * @param bookId - The unique identifier of the book. + * @param actId - The unique identifier of the act summary. + * @param summary - The new summary text. + * @param lastUpdate - The timestamp of the last update in seconds. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the update was successful, false otherwise. + * @throws Error if the database operation fails. + */ public static updateActSummary(userId: string, bookId: string, actId: number, summary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_act_summaries SET summary=?, last_update=? WHERE user_id=? AND book_id=? AND act_sum_id=?', [summary, lastUpdate, userId, bookId, actId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'UPDATE book_act_summaries SET summary=?, last_update=? WHERE user_id=? AND book_id=? AND act_sum_id=?'; + const params: SQLiteValue[] = [summary, lastUpdate, userId, bookId, actId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le résumé de l'acte.` : `Unable to update act summary.`); } else { console.error("An unknown error occurred."); @@ -51,45 +75,81 @@ export default class ActRepository { } } } + + /** + * Inserts a new act summary into the database. + * @param actSummaryId - The unique identifier for the new act summary. + * @param userId - The unique identifier of the user. + * @param bookId - The unique identifier of the book. + * @param actId - The act index number. + * @param actSummary - The summary text for the act. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns The act summary ID if insertion was successful. + * @throws Error if the database operation fails. + */ static insertActSummary(actSummaryId: string, userId: string, bookId: string, actId: number, actSummary: string, lang: 'fr' | 'en'): string { - let result:RunResult + let insertResult: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?,?,?,?,?,?)', [actSummaryId, bookId, userId, actId, actSummary, System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?,?,?,?,?,?)'; + const params: SQLiteValue[] = [actSummaryId, bookId, userId, actId, actSummary, System.timeStampInSeconds()]; + insertResult = db.run(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le résumé de l'acte.` : `Unable to add act summary.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result) { + if (!insertResult) { throw new Error(lang === 'fr' ? `Erreur lors de l'ajout du résumé de l'acte.` : `Error adding act summary.`); } return actSummaryId; } + + /** + * Fetches all act summaries for a specific book. + * @param userId - The unique identifier of the user. + * @param bookId - The unique identifier of the book. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns A promise resolving to an array of BookActSummariesTable objects. + * @throws Error if the database operation fails. + */ static async fetchBookActSummaries(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT act_sum_id, book_id, user_id, act_index, summary, last_update FROM book_act_summaries WHERE user_id=? AND book_id=?', [userId, bookId]) as BookActSummariesTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT act_sum_id, book_id, user_id, act_index, summary, last_update FROM book_act_summaries WHERE user_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + return db.all(query, params) as BookActSummariesTable[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les résumés des actes.` : `Unable to retrieve act summaries.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Fetches all synced act summaries for a user. + * @param userId - The unique identifier of the user. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns An array of SyncedActSummaryResult objects containing sync metadata. + * @throws Error if the database operation fails. + */ static fetchSyncedActSummaries(userId: string, lang: 'fr' | 'en'): SyncedActSummaryResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT act_sum_id, book_id, last_update FROM book_act_summaries WHERE user_id = ?', [userId]) as SyncedActSummaryResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT act_sum_id, book_id, last_update FROM book_act_summaries WHERE user_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedActSummaries: SyncedActSummaryResult[] = db.all(query, params) as SyncedActSummaryResult[]; + return syncedActSummaries; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les résumés d'actes synchronisés.` : `Unable to retrieve synced act summaries.`); } else { console.error("An unknown error occurred."); @@ -97,52 +157,84 @@ export default class ActRepository { } } } + + /** + * Inserts a synced act summary from remote data. + * @param actSumId - The unique identifier of the act summary. + * @param bookId - The unique identifier of the book. + * @param userId - The unique identifier of the user. + * @param actIndex - The act index number. + * @param summary - The summary text (can be null). + * @param lastUpdate - The timestamp of the last update in seconds. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the insertion was successful, false otherwise. + * @throws Error if the database operation fails. + */ static insertSyncActSummary(actSumId: string, bookId: string, userId: string, actIndex: number, summary: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?, ?, ?, ?, ?, ?)`, - [actSumId, bookId, userId, actIndex, summary, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?, ?, ?, ?, ?, ?)'; + const params: SQLiteValue[] = [actSumId, bookId, userId, actIndex, summary, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer le résumé d'acte.` : `Unable to insert act summary.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static async fetchCompleteActSummaryById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches a complete act summary by its unique identifier. + * @param id - The unique identifier of the act summary. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns A promise resolving to an array of BookActSummariesTable objects. + * @throws Error if the database operation fails. + */ + static async fetchCompleteActSummaryById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT act_sum_id, book_id, user_id, act_index, summary, last_update + const query: string = `SELECT act_sum_id, book_id, user_id, act_index, summary, last_update FROM book_act_summaries - WHERE act_sum_id = ?`, - [id] - ) as BookActSummariesTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + WHERE act_sum_id = ?`; + const params: SQLiteValue[] = [id]; + const actSummary: BookActSummariesTable[] = db.all(query, params) as BookActSummariesTable[]; + return actSummary; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le résumé d'acte complet.` : `Unable to retrieve complete act summary.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static actSummarizeExist(userId: string, bookId: string, act_index: number,lang: "fr" | "en"): boolean { + + /** + * Checks if an act summary exists for a given user, book, and act index. + * @param userId - The unique identifier of the user. + * @param bookId - The unique identifier of the book. + * @param actIndex - The act index number to check. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the act summary exists, false otherwise. + * @throws Error if the database operation fails. + */ + static actSummarizeExist(userId: string, bookId: string, actIndex: number, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result:QueryResult|null = db.get('SELECT 1 FROM book_act_summaries WHERE user_id =? AND book_id =? AND act_index = ?', [userId, bookId, act_index]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM book_act_summaries WHERE user_id =? AND book_id =? AND act_index = ?'; + const params: SQLiteValue[] = [userId, bookId, actIndex]; + const existenceCheck: QueryResult | null = db.get(query, params) || null; + return existenceCheck !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du résumé de l'acte.` : `Unable to check act summary existence.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } -} \ No newline at end of file +} diff --git a/electron/database/repositories/book.repository.ts b/electron/database/repositories/book.repository.ts index c931472..7dcf4f3 100644 --- a/electron/database/repositories/book.repository.ts +++ b/electron/database/repositories/book.repository.ts @@ -47,197 +47,318 @@ export interface BookCoverQuery extends Record { } export default class BookRepo { + /** + * Retrieves all books for a user. + * @param userId - The user identifier + * @param lang - The language for error messages + * @returns List of user's books + */ public static fetchBooks(userId: string, lang: 'fr' | 'en'): BookQuery[] { try { const db: Database = System.getDb(); - return db.all('SELECT book_id, type, author_id, title, sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image FROM erit_books WHERE author_id = ? ORDER BY book_id DESC', [userId]) as BookQuery[]; + const query: string = 'SELECT book_id, type, author_id, title, sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image FROM erit_books WHERE author_id = ? ORDER BY book_id DESC'; + const params: SQLiteValue[] = [userId]; + return db.all(query, params) as BookQuery[]; } catch (error: unknown) { if (error instanceof Error) { console.error(error.message); throw new Error(lang === 'fr' ? 'Impossible de récupérer la liste des livres.' : 'Unable to retrieve book list.'); - } else { - console.error(error); - throw new Error(lang === 'fr' ? 'Une erreur inconnue est survenue.' : 'An unknown error occurred.'); } + console.error(error); + throw new Error(lang === 'fr' ? 'Une erreur inconnue est survenue.' : 'An unknown error occurred.'); } } - - public static updateBookCover(bookId:string,coverImageName:string,userId:string, lang: 'fr' | 'en'):boolean{ + + /** + * Updates a book's cover image. + * @param bookId - The book identifier + * @param coverImageName - The cover image file name + * @param userId - The user identifier + * @param lang - The language for error messages + * @returns true if the update was successful + */ + public static updateBookCover(bookId: string, coverImageName: string, userId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result:RunResult = db.run('UPDATE `erit_books` SET cover_image=?, last_update=? WHERE `book_id`=? AND author_id=?', [coverImageName, System.timeStampInSeconds(), bookId, userId]); - return result.changes>0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de mettre à jour la couverture du livre.` : `Unable to update book cover.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); - } - } - } - - public static fetchBook(bookId: string, userId: string, lang: 'fr' | 'en'): BookQuery { - let result: BookQuery; - try { - const db: Database = System.getDb(); - result = db.get('SELECT book_id, author_id, title, summary, sub_title, cover_image, desired_release_date, desired_word_count, words_count FROM erit_books WHERE book_id=? AND author_id=?', [bookId, userId]) as BookQuery; + const query: string = 'UPDATE erit_books SET cover_image=?, last_update=? WHERE book_id=? AND author_id=?'; + const params: SQLiteValue[] = [coverImageName, System.timeStampInSeconds(), bookId, userId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; } catch (error: unknown) { if (error instanceof Error) { console.error(`DB Error: ${error.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les informations du livre.` : `Unable to retrieve book information.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + throw new Error(lang === 'fr' ? 'Impossible de mettre à jour la couverture du livre.' : 'Unable to update book cover.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } - if (!result) { - throw new Error(lang === 'fr' ? `Livre non trouvé.` : `Book not found.`); - } - return result; } - public static verifyBookExist(hashedTitle:string,hashedSubTitle:string,userId:string, lang: 'fr' | 'en'):boolean{ + + /** + * Retrieves a book by its identifier. + * @param bookId - The book identifier + * @param userId - The user identifier + * @param lang - The language for error messages + * @returns The book information + */ + public static fetchBook(bookId: string, userId: string, lang: 'fr' | 'en'): BookQuery { + let book: BookQuery; try { const db: Database = System.getDb(); - const result:QueryResult|null = db.get('SELECT book_id FROM erit_books WHERE hashed_title=? AND author_id=? AND erit_books.hashed_sub_title=?', [hashedTitle,userId,hashedSubTitle]); - return result!==null; - } catch (err: unknown) { - if (err instanceof Error) { - console.error(`DB Error: ${err.message}`); - throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du livre.` : `Unable to verify book existence.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT book_id, author_id, title, summary, sub_title, cover_image, desired_release_date, desired_word_count, words_count FROM erit_books WHERE book_id=? AND author_id=?'; + const params: SQLiteValue[] = [bookId, userId]; + book = db.get(query, params) as BookQuery; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les informations du livre.' : 'Unable to retrieve book information.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + } + if (!book) { + throw new Error(lang === 'fr' ? 'Livre non trouvé.' : 'Book not found.'); + } + return book; + } + + /** + * Verifies if a book already exists for a user. + * @param hashedTitle - The hashed book title + * @param hashedSubTitle - The hashed book subtitle + * @param userId - The user identifier + * @param lang - The language for error messages + * @returns true if the book exists + */ + public static verifyBookExist(hashedTitle: string, hashedSubTitle: string, userId: string, lang: 'fr' | 'en'): boolean { + try { + const db: Database = System.getDb(); + const query: string = 'SELECT book_id FROM erit_books WHERE hashed_title=? AND author_id=? AND hashed_sub_title=?'; + const params: SQLiteValue[] = [hashedTitle, userId, hashedSubTitle]; + const book: QueryResult | null = db.get(query, params); + return book !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence du livre." : 'Unable to verify book existence.'); + } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - + + /** + * Inserts a new book into the database. + * @param bookId - The book identifier + * @param userId - The user identifier + * @param encryptedTitle - The encrypted title + * @param hashedTitle - The hashed title + * @param encryptedSubTitle - The encrypted subtitle + * @param hashedSubTitle - The hashed subtitle + * @param encryptedSummary - The encrypted summary + * @param type - The book type + * @param serie - The series identifier + * @param publicationDate - The desired publication date + * @param desiredWordCount - The desired word count + * @param lang - The language for error messages + * @returns The created book identifier + */ public static insertBook(bookId: string, userId: string, encryptedTitle: string, hashedTitle: string, encryptedSubTitle: string, hashedSubTitle: string, encryptedSummary: string, type: string, serie: number, publicationDate: string, desiredWordCount: number, lang: 'fr' | 'en'): string { - let result: RunResult; + let insertResult: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO erit_books (book_id, type, author_id, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [bookId, type, userId, encryptedTitle, hashedTitle, encryptedSubTitle, hashedSubTitle, encryptedSummary, serie, publicationDate ? publicationDate : null, desiredWordCount, System.timeStampInSeconds()]); - } catch (err: unknown) { - if (err instanceof Error) { - console.error(`DB Error: ${err.message}`); - throw new Error(lang === 'fr' ? `Impossible d'ajouter le livre.` : `Unable to add book.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'INSERT INTO erit_books (book_id, type, author_id, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'; + const params: SQLiteValue[] = [bookId, type, userId, encryptedTitle, hashedTitle, encryptedSubTitle, hashedSubTitle, encryptedSummary, serie, publicationDate ? publicationDate : null, desiredWordCount, System.timeStampInSeconds()]; + insertResult = db.run(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible d'ajouter le livre." : 'Unable to add book.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } - if (!result || result.changes === 0) { - throw new Error(lang === 'fr' ? `Erreur lors de l'ajout du livre.` : `Error adding book.`); + if (!insertResult || insertResult.changes === 0) { + throw new Error(lang === 'fr' ? "Erreur lors de l'ajout du livre." : 'Error adding book.'); } return bookId; } - public static fetchBookCover(userId:string,bookId:string, lang: 'fr' | 'en'):BookCoverQuery{ + + /** + * Retrieves a book's cover image. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages + * @returns The cover information + */ + public static fetchBookCover(userId: string, bookId: string, lang: 'fr' | 'en'): BookCoverQuery { try { const db: Database = System.getDb(); - return db.get('SELECT cover_image FROM erit_books WHERE author_id=? AND book_id=?', [userId, bookId]) as BookCoverQuery; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer la couverture du livre.` : `Unable to retrieve book cover.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT cover_image FROM erit_books WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + return db.get(query, params) as BookCoverQuery; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer la couverture du livre.' : 'Unable to retrieve book cover.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - + + /** + * Updates a book's basic information. + * @param userId - The user identifier + * @param title - The new title + * @param hashedTitle - The hashed title + * @param subTitle - The new subtitle + * @param hashedSubTitle - The hashed subtitle + * @param summary - The new summary + * @param publicationDate - The new publication date + * @param wordCount - The new desired word count + * @param bookId - The book identifier + * @param lang - The language for error messages + * @returns true if the update was successful + */ static updateBookBasicInformation(userId: string, title: string, hashedTitle: string, subTitle: string, hashedSubTitle: string, summary: string, publicationDate: string, wordCount: number, bookId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE erit_books SET title=?, hashed_title=?, sub_title=?, hashed_sub_title=?, summary=?, serie_id=?, desired_release_date=?, desired_word_count=?, last_update=? WHERE author_id=? AND book_id=?', - [title, hashedTitle, subTitle, hashedSubTitle, summary, 0, publicationDate ? System.dateToMySqlDate(publicationDate) : null, wordCount, System.timeStampInSeconds(), userId, bookId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de mettre à jour les informations du livre.` : `Unable to update book information.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'UPDATE erit_books SET title=?, hashed_title=?, sub_title=?, hashed_sub_title=?, summary=?, serie_id=?, desired_release_date=?, desired_word_count=?, last_update=? WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [title, hashedTitle, subTitle, hashedSubTitle, summary, 0, publicationDate ? System.dateToMySqlDate(publicationDate) : null, wordCount, System.timeStampInSeconds(), userId, bookId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de mettre à jour les informations du livre.' : 'Unable to update book information.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + /** + * Deletes a book from the database. + * @param userId - The user identifier + * @param bookId - The book identifier to delete + * @param lang - The language for error messages + * @returns true if the deletion was successful + */ public static deleteBook(userId: string, bookId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM erit_books WHERE author_id=? AND book_id=?', [userId,bookId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de supprimer le livre.` : `Unable to delete book.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); - } - } - } - static async fetchEritBooksTable(userId:string,bookId:string, lang: 'fr' | 'en'):Promise{ - try { - const db: Database = System.getDb(); - return db.all('SELECT book_id, type, author_id, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image, last_update FROM erit_books WHERE book_id=? AND author_id=?', [bookId, userId]) as EritBooksTable[]; - } catch (e:unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les informations du livre.` : `Unable to retrieve book information.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); - } - } - } - - static fetchSyncedBooks(userId: string, lang: 'fr' | 'en'): SyncedBookResult[] { - try { - const db: Database = System.getDb(); - return db.all('SELECT book_id, type, title, sub_title, last_update FROM erit_books WHERE author_id = ?', [userId]) as SyncedBookResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les livres synchronisés.` : `Unable to retrieve synced books.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'DELETE FROM erit_books WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de supprimer le livre.' : 'Unable to delete book.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + /** + * Retrieves all columns from erit_books table for a book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages + * @returns The complete book data + */ + static async fetchEritBooksTable(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { + try { + const db: Database = System.getDb(); + const query: string = 'SELECT book_id, type, author_id, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image, last_update FROM erit_books WHERE book_id=? AND author_id=?'; + const params: SQLiteValue[] = [bookId, userId]; + return db.all(query, params) as EritBooksTable[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les informations du livre.' : 'Unable to retrieve book information.'); + } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + } + } + + /** + * Retrieves synced books for a user. + * @param userId - The user identifier + * @param lang - The language for error messages + * @returns List of books with sync information + */ + static fetchSyncedBooks(userId: string, lang: 'fr' | 'en'): SyncedBookResult[] { + try { + const db: Database = System.getDb(); + const query: string = 'SELECT book_id, type, title, sub_title, last_update FROM erit_books WHERE author_id = ?'; + const params: SQLiteValue[] = [userId]; + return db.all(query, params) as SyncedBookResult[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les livres synchronisés.' : 'Unable to retrieve synced books.'); + } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + } + } + + /** + * Inserts a synced book from the server. + * @param bookId - The book identifier + * @param userId - The user identifier + * @param type - The book type + * @param title - The encrypted title + * @param hashedTitle - The hashed title + * @param subTitle - The encrypted subtitle + * @param hashedSubTitle - The hashed subtitle + * @param summary - The encrypted summary + * @param serieId - The series identifier + * @param desiredReleaseDate - The desired release date + * @param desiredWordCount - The desired word count + * @param wordsCount - The current word count + * @param coverImage - The cover image file name + * @param lastUpdate - The last update timestamp + * @param lang - The language for error messages + * @returns true if the insertion was successful + */ static insertSyncBook(bookId: string, userId: string, type: string, title: string, hashedTitle: string, subTitle: string | null, hashedSubTitle: string | null, summary: string | null, serieId: number | null, desiredReleaseDate: string | null, desiredWordCount: number | null, wordsCount: number | null, coverImage: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO erit_books (book_id, author_id, type, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [bookId, userId, type, title, hashedTitle, subTitle, hashedSubTitle, summary, serieId, desiredReleaseDate, desiredWordCount, wordsCount, coverImage, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible d'insérer le livre synchronisé.` : `Unable to insert synced book.`); - } else { - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'INSERT INTO erit_books (book_id, author_id, type, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; + const params: SQLiteValue[] = [bookId, userId, type, title, hashedTitle, subTitle, hashedSubTitle, summary, serieId, desiredReleaseDate, desiredWordCount, wordsCount, coverImage, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible d'insérer le livre synchronisé." : 'Unable to insert synced book.'); } + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - - static async fetchCompleteBookById(bookId: string, lang: "fr" | "en") { + + /** + * Retrieves a complete book by its identifier (without author verification). + * @param bookId - The book identifier + * @param lang - The language for error messages + * @returns The complete book data + */ + static async fetchCompleteBookById(bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all(`SELECT * FROM erit_books WHERE book_id = ?`, [bookId]) as EritBooksTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - throw new Error(lang === 'fr' ? `Impossible de récupérer le livre complet.` : `Unable to retrieve complete book.`); - } else { - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT * FROM erit_books WHERE book_id = ?'; + const params: SQLiteValue[] = [bookId]; + return db.all(query, params) as EritBooksTable[]; + } catch (error: unknown) { + if (error instanceof Error) { + throw new Error(lang === 'fr' ? 'Impossible de récupérer le livre complet.' : 'Unable to retrieve complete book.'); } + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } -} +} \ No newline at end of file diff --git a/electron/database/repositories/chapter.repository.ts b/electron/database/repositories/chapter.repository.ts index 683da14..254cd54 100644 --- a/electron/database/repositories/chapter.repository.ts +++ b/electron/database/repositories/chapter.repository.ts @@ -1,14 +1,13 @@ import {Database, QueryResult, RunResult, SQLiteValue} from 'node-sqlite3-wasm'; import System from "../System.js"; -import {BookQuery, EritBooksTable, SyncedBookResult} from "@/electron/database/repositories/book.repository"; -export interface ChapterQueryResult extends Record{ +export interface ChapterQueryResult extends Record { chapter_id: string; title: string; chapter_order: number; } -export interface ActChapterQuery extends Record{ +export interface ActChapterQuery extends Record { chapter_info_id: number; chapter_id: string; title: string; @@ -20,7 +19,7 @@ export interface ActChapterQuery extends Record{ goal: string; } -export interface ChapterStoryQueryResult extends Record{ +export interface ChapterStoryQueryResult extends Record { chapter_info_id: number; act_id: number; summary: string; @@ -34,7 +33,7 @@ export interface ChapterStoryQueryResult extends Record{ plot_summary: string; } -export interface LastChapterResult extends Record{ +export interface LastChapterResult extends Record { chapter_id: string; version: number; } @@ -83,413 +82,620 @@ export interface ChapterBookResult extends Record { content: string | null; } -export default class ChapterRepo{ - public static checkNameDuplication(userId:string,bookId:string,hashedTitle:string, lang: 'fr' | 'en' = 'fr'):boolean{ +export default class ChapterRepo { + /** + * Checks if a chapter name already exists for a book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param hashedTitle - The hashed chapter title + * @param lang - The language for error messages + * @returns true if a chapter with this name exists + */ + public static checkNameDuplication(userId: string, bookId: string, hashedTitle: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result = db.get('SELECT chapter_id FROM book_chapters WHERE author_id=? AND book_id=? AND hashed_title=?', [userId,bookId,hashedTitle]); - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de vérifier la duplication du nom.` : `Unable to verify name duplication.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_id FROM book_chapters WHERE author_id=? AND book_id=? AND hashed_title=?'; + const params: SQLiteValue[] = [userId, bookId, hashedTitle]; + const chapter: QueryResult | null = db.get(query, params); + return chapter !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de vérifier la duplication du nom.' : 'Unable to verify name duplication.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - + + /** + * Inserts a new chapter into the database. + * @param chapterId - The chapter identifier + * @param userId - The user identifier + * @param bookId - The book identifier + * @param title - The encrypted chapter title + * @param hashedTitle - The hashed chapter title + * @param wordsCount - The word count + * @param chapterOrder - The chapter order position + * @param lang - The language for error messages + * @returns The created chapter identifier + */ public static insertChapter(chapterId: string, userId: string, bookId: string, title: string, hashedTitle: string, wordsCount: number, chapterOrder: number, lang: 'fr' | 'en' = 'fr'): string { - let result: RunResult; + let insertResult: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO book_chapters (chapter_id, author_id, book_id, title, hashed_title, words_count, chapter_order, last_update) VALUES (?,?,?,?,?,?,?,?)', [chapterId, userId, bookId, title, hashedTitle, wordsCount, chapterOrder, System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible d'ajouter le chapitre.` : `Unable to add chapter.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'INSERT INTO book_chapters (chapter_id, author_id, book_id, title, hashed_title, words_count, chapter_order, last_update) VALUES (?,?,?,?,?,?,?,?)'; + const params: SQLiteValue[] = [chapterId, userId, bookId, title, hashedTitle, wordsCount, chapterOrder, System.timeStampInSeconds()]; + insertResult = db.run(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible d'ajouter le chapitre." : 'Unable to add chapter.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } - if (!result || result.changes === 0) { - throw new Error(lang === 'fr' ? `Une erreur s'est passé lors de l'ajout du chapitre.` : `Error adding chapter.`); + if (!insertResult || insertResult.changes === 0) { + throw new Error(lang === 'fr' ? "Une erreur s'est passé lors de l'ajout du chapitre." : 'Error adding chapter.'); } return chapterId; } - public static fetchAllChapterForActs(userId:string,bookId:string, lang: 'fr' | 'en' = 'fr'):ActChapterQuery[]{ + + /** + * Retrieves all chapters with their act information for a book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages + * @returns List of chapters with act information + */ + public static fetchAllChapterForActs(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ActChapterQuery[] { try { const db: Database = System.getDb(); - return db.all('SELECT ci.chapter_info_id AS chapter_info_id, ci.chapter_id AS chapter_id, chapter.title, chapter.chapter_order, ci.act_id, ci.incident_id AS incident_id, ci.plot_point_id AS plot_point_id, ci.summary, ci.goal FROM book_chapter_infos AS ci INNER JOIN book_chapters AS chapter ON chapter.chapter_id = ci.chapter_id WHERE ci.book_id = ? AND ci.author_id = ?', [bookId, userId]) as ActChapterQuery[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres pour les actes.` : `Unable to retrieve chapters for acts.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT ci.chapter_info_id AS chapter_info_id, ci.chapter_id AS chapter_id, chapter.title, chapter.chapter_order, ci.act_id, ci.incident_id AS incident_id, ci.plot_point_id AS plot_point_id, ci.summary, ci.goal FROM book_chapter_infos AS ci INNER JOIN book_chapters AS chapter ON chapter.chapter_id = ci.chapter_id WHERE ci.book_id = ? AND ci.author_id = ?'; + const params: SQLiteValue[] = [bookId, userId]; + return db.all(query, params) as ActChapterQuery[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres pour les actes.' : 'Unable to retrieve chapters for acts.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + /** + * Retrieves all chapters from a book ordered by chapter order. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages + * @returns List of chapters + */ public static fetchAllChapterFromABook(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterQueryResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT chapter_id, title, chapter_order FROM book_chapters WHERE book_id=? AND author_id=? ORDER BY chapter_order', [bookId, userId]) as ChapterQueryResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres.` : `Unable to retrieve chapters.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_id, title, chapter_order FROM book_chapters WHERE book_id=? AND author_id=? ORDER BY chapter_order'; + const params: SQLiteValue[] = [bookId, userId]; + return db.all(query, params) as ChapterQueryResult[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres.' : 'Unable to retrieve chapters.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + /** + * Deletes a chapter from the database. + * @param userId - The user identifier + * @param chapterId - The chapter identifier to delete + * @param lang - The language for error messages + * @returns true if the deletion was successful + */ public static deleteChapter(userId: string, chapterId: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM book_chapters WHERE author_id=? AND chapter_id=?', [userId, chapterId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de supprimer le chapitre.` : `Unable to delete chapter.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'DELETE FROM book_chapters WHERE author_id=? AND chapter_id=?'; + const params: SQLiteValue[] = [userId, chapterId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de supprimer le chapitre.' : 'Unable to delete chapter.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - + + /** + * Inserts chapter information linking a chapter to an act. + * @param chapterInfoId - The chapter info identifier + * @param userId - The user identifier + * @param chapterId - The chapter identifier + * @param actId - The act identifier + * @param bookId - The book identifier + * @param plotId - The plot point identifier (optional) + * @param incidentId - The incident identifier (optional) + * @param lang - The language for error messages + * @returns The created chapter info identifier + */ static insertChapterInformation(chapterInfoId: string, userId: string, chapterId: string, actId: number, bookId: string, plotId: string | null, incidentId: string | null, lang: 'fr' | 'en' = 'fr'): string { - let existResult; - let result: RunResult; + let existingChapter: QueryResult | null; + let insertResult: RunResult; try { const db: Database = System.getDb(); - existResult = db.get('SELECT chapter_info_id FROM book_chapter_infos WHERE chapter_id=? AND act_id=? AND book_id=? AND plot_point_id=? AND incident_id=? AND author_id=?', [chapterId, actId, bookId, plotId, incidentId, userId]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'information du chapitre.` : `Unable to verify chapter information existence.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const checkQuery: string = 'SELECT chapter_info_id FROM book_chapter_infos WHERE chapter_id=? AND act_id=? AND book_id=? AND plot_point_id=? AND incident_id=? AND author_id=?'; + const checkParams: SQLiteValue[] = [chapterId, actId, bookId, plotId, incidentId, userId]; + existingChapter = db.get(checkQuery, checkParams); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence de l'information du chapitre." : 'Unable to verify chapter information existence.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } - if (existResult !== null) { - throw new Error(lang === 'fr' ? `Le chapitre est déjà lié.` : `Chapter is already linked.`); + if (existingChapter !== null) { + throw new Error(lang === 'fr' ? 'Le chapitre est déjà lié.' : 'Chapter is already linked.'); } try { const db: Database = System.getDb(); - result = db.run('INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, book_id, author_id, incident_id, plot_point_id, summary, goal, last_update) VALUES (?,?,?,?,?,?,?,?,?,?)', [chapterInfoId, chapterId, actId, bookId, userId, incidentId, plotId, '', '', System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible d'ajouter l'information du chapitre.` : `Unable to add chapter information.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, book_id, author_id, incident_id, plot_point_id, summary, goal, last_update) VALUES (?,?,?,?,?,?,?,?,?,?)'; + const params: SQLiteValue[] = [chapterInfoId, chapterId, actId, bookId, userId, incidentId, plotId, '', '', System.timeStampInSeconds()]; + insertResult = db.run(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible d'ajouter l'information du chapitre." : 'Unable to add chapter information.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } - if (!result || result.changes === 0) { - throw new Error(lang === 'fr' ? `Une erreur s'est produite pendant la liaison du chapitre.` : `Error linking chapter.`); + if (!insertResult || insertResult.changes === 0) { + throw new Error(lang === 'fr' ? "Une erreur s'est produite pendant la liaison du chapitre." : 'Error linking chapter.'); } return chapterInfoId; } - - public static updateChapter(userId: string, chapterId: string, encryptedTitle: string, hashTitle: string, chapterOrder: number, lastUpdate:number, lang: 'fr' | 'en' = 'fr'): boolean { + + /** + * Updates a chapter's basic information. + * @param userId - The user identifier + * @param chapterId - The chapter identifier + * @param encryptedTitle - The encrypted title + * @param hashTitle - The hashed title + * @param chapterOrder - The chapter order position + * @param lastUpdate - The last update timestamp + * @param lang - The language for error messages + * @returns true if the update was successful + */ + public static updateChapter(userId: string, chapterId: string, encryptedTitle: string, hashTitle: string, chapterOrder: number, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=?, last_update=? WHERE author_id=? AND chapter_id=?', [encryptedTitle, hashTitle, chapterOrder, lastUpdate, userId, chapterId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de mettre à jour le chapitre.` : `Unable to update chapter.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=?, last_update=? WHERE author_id=? AND chapter_id=?'; + const params: SQLiteValue[] = [encryptedTitle, hashTitle, chapterOrder, lastUpdate, userId, chapterId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de mettre à jour le chapitre.' : 'Unable to update chapter.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + /** + * Updates chapter information (summary and goal). + * @param userId - The user identifier + * @param chapterId - The chapter identifier + * @param actId - The act identifier + * @param bookId - The book identifier + * @param incidentId - The incident identifier (optional) + * @param plotId - The plot point identifier (optional) + * @param summary - The chapter summary + * @param goal - The chapter goal + * @param lastUpdate - The last update timestamp + * @param lang - The language for error messages + * @returns true if the update was successful + */ public static updateChapterInfos(userId: string, chapterId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, summary: string, goal: string | null, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - let sql: string = `UPDATE book_chapter_infos SET summary=?,goal=?,last_update=? WHERE chapter_id = ? AND act_id = ? AND book_id = ?`; - const params: (string|null|number)[] = [summary, goal, lastUpdate, chapterId, actId, bookId]; + let query: string = 'UPDATE book_chapter_infos SET summary=?,goal=?,last_update=? WHERE chapter_id = ? AND act_id = ? AND book_id = ?'; + const params: SQLiteValue[] = [summary, goal, lastUpdate, chapterId, actId, bookId]; if (incidentId) { - sql += ` AND incident_id=?`; + query += ' AND incident_id=?'; params.push(incidentId); } else { - sql += ` AND incident_id IS NULL`; + query += ' AND incident_id IS NULL'; } if (plotId) { - sql += ` AND plot_point_id=?`; + query += ' AND plot_point_id=?'; params.push(plotId); } else { - sql += ` AND plot_point_id IS NULL`; + query += ' AND plot_point_id IS NULL'; } - sql += ` AND author_id=?`; + query += ' AND author_id=?'; params.push(userId); - const result: RunResult = db.run(sql, params); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de mettre à jour les informations du chapitre.` : `Unable to update chapter information.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de mettre à jour les informations du chapitre.' : 'Unable to update chapter information.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + /** + * Retrieves the last opened chapter for a book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages + * @returns The last chapter information or null + */ public static fetchLastChapter(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LastChapterResult | null { try { const db: Database = System.getDb(); - const result = db.get('SELECT chapter_id as chapter_id,version FROM user_last_chapter WHERE user_id=? AND book_id=?', [userId, bookId]) as LastChapterResult | null; - return result; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer le dernier chapitre ouvert.` : `Unable to retrieve last opened chapter.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_id as chapter_id,version FROM user_last_chapter WHERE user_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + return db.get(query, params) as LastChapterResult | null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer le dernier chapitre ouvert.' : 'Unable to retrieve last opened chapter.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + /** + * Updates or inserts the last chapter record for a user. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param chapterId - The chapter identifier + * @param version - The chapter version + * @param lang - The language for error messages + * @returns true if the operation was successful + */ public static updateLastChapterRecord(userId: string, bookId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE user_last_chapter SET chapter_id=?, version=? WHERE user_id=? AND book_id=?', [chapterId, version, userId, bookId]); - if (result.changes > 0) { + const updateQuery: string = 'UPDATE user_last_chapter SET chapter_id=?, version=? WHERE user_id=? AND book_id=?'; + const updateParams: SQLiteValue[] = [chapterId, version, userId, bookId]; + const updateResult: RunResult = db.run(updateQuery, updateParams); + if (updateResult.changes > 0) { return true; - } else { - const insertResult: RunResult = db.run('INSERT INTO user_last_chapter (user_id, book_id, chapter_id, version) VALUES (?,?,?,?)', [userId, bookId, chapterId, version]); - return insertResult.changes > 0; } - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible d'enregistrer le dernier chapitre.` : `Unable to save last chapter.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const insertQuery: string = 'INSERT INTO user_last_chapter (user_id, book_id, chapter_id, version) VALUES (?,?,?,?)'; + const insertParams: SQLiteValue[] = [userId, bookId, chapterId, version]; + const insertResult: RunResult = db.run(insertQuery, insertParams); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible d'enregistrer le dernier chapitre." : 'Unable to save last chapter.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - public static fetchChapterStory(userId: string, chapterId: string, lang: 'fr' | 'en' = 'fr'):ChapterStoryQueryResult[] { + /** + * Retrieves chapter story information including act, incident, and plot point data. + * @param userId - The user identifier + * @param chapterId - The chapter identifier + * @param lang - The language for error messages + * @returns List of chapter story information + */ + public static fetchChapterStory(userId: string, chapterId: string, lang: 'fr' | 'en' = 'fr'): ChapterStoryQueryResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT chapter_info_id, chapter.act_id, act_sum.summary, chapter.summary AS chapter_summary, chapter.goal AS chapter_goal, chapter.incident_id, incident.title AS incident_title, incident.summary AS incident_summary, chapter.plot_point_id, plot.title AS plot_title, plot.summary AS plot_summary FROM book_chapter_infos AS chapter LEFT JOIN book_incidents AS incident ON chapter.incident_id=incident.incident_id LEFT JOIN book_plot_points AS plot ON chapter.plot_point_id=plot.plot_point_id LEFT JOIN book_act_summaries AS act_sum ON chapter.act_id=act_sum.act_sum_id AND chapter.book_id=act_sum.book_id WHERE chapter.chapter_id=? AND chapter.author_id=?', [chapterId, userId]) as ChapterStoryQueryResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer l'histoire du chapitre.` : `Unable to retrieve chapter story.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_info_id, chapter.act_id, act_sum.summary, chapter.summary AS chapter_summary, chapter.goal AS chapter_goal, chapter.incident_id, incident.title AS incident_title, incident.summary AS incident_summary, chapter.plot_point_id, plot.title AS plot_title, plot.summary AS plot_summary FROM book_chapter_infos AS chapter LEFT JOIN book_incidents AS incident ON chapter.incident_id=incident.incident_id LEFT JOIN book_plot_points AS plot ON chapter.plot_point_id=plot.plot_point_id LEFT JOIN book_act_summaries AS act_sum ON chapter.act_id=act_sum.act_sum_id AND chapter.book_id=act_sum.book_id WHERE chapter.chapter_id=? AND chapter.author_id=?'; + const params: SQLiteValue[] = [chapterId, userId]; + return db.all(query, params) as ChapterStoryQueryResult[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible de récupérer l'histoire du chapitre." : 'Unable to retrieve chapter story.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + /** + * Deletes chapter information by its identifier. + * @param userId - The user identifier + * @param chapterInfoId - The chapter info identifier to delete + * @param lang - The language for error messages + * @returns true if the deletion was successful + */ static deleteChapterInformation(userId: string, chapterInfoId: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM book_chapter_infos WHERE chapter_info_id=? AND author_id=?', [chapterInfoId, userId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de supprimer les informations du chapitre.` : `Unable to delete chapter information.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'DELETE FROM book_chapter_infos WHERE chapter_info_id=? AND author_id=?'; + const params: SQLiteValue[] = [chapterInfoId, userId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de supprimer les informations du chapitre.' : 'Unable to delete chapter information.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - static isChapterExist(userId: string, chapter_id: string, lang: "fr" | "en"): boolean { + + /** + * Checks if a chapter exists. + * @param userId - The user identifier + * @param chapterId - The chapter identifier + * @param lang - The language for error messages + * @returns true if the chapter exists + */ + static isChapterExist(userId: string, chapterId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM book_chapters WHERE chapter_id=? AND author_id=?', [chapter_id, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du chapitre.` : `Unable to check chapter existence.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT 1 FROM book_chapters WHERE chapter_id=? AND author_id=?'; + const params: SQLiteValue[] = [chapterId, userId]; + const chapter: QueryResult | null = db.get(query, params) || null; + return chapter !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence du chapitre." : 'Unable to check chapter existence.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - - static isChapterInfoExist(userId: string, chapter_id: string, lang: "fr" | "en"): boolean { + + /** + * Checks if chapter info exists. + * @param userId - The user identifier + * @param chapterId - The chapter identifier + * @param lang - The language for error messages + * @returns true if the chapter info exists + */ + static isChapterInfoExist(userId: string, chapterId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `book_chapter_infos` WHERE `chapter_id`=? AND `author_id`=?', [chapter_id, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence des informations du chapitre.` : `Unable to check chapter info existence.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT 1 FROM book_chapter_infos WHERE chapter_id=? AND author_id=?'; + const params: SQLiteValue[] = [chapterId, userId]; + const chapterInfo: QueryResult | null = db.get(query, params) || null; + return chapterInfo !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence des informations du chapitre." : 'Unable to check chapter info existence.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - - static fetchCompleteBookChapters(id: string, lang: 'fr' | 'en'): ChapterBookResult[] { - let result: ChapterBookResult[]; + + /** + * Retrieves complete book chapters with their content. + * @param bookId - The book identifier + * @param lang - The language for error messages + * @returns List of chapters with content + */ + static fetchCompleteBookChapters(bookId: string, lang: 'fr' | 'en'): ChapterBookResult[] { + let chapters: ChapterBookResult[]; try { const db: Database = System.getDb(); - result = db.all('SELECT title, chapter_order, content.content FROM book_chapters AS chapter LEFT JOIN book_chapter_content AS content ON chapter.chapter_id = content.chapter_id AND content.version = (SELECT MAX(version) FROM book_chapter_content WHERE chapter_id = chapter.chapter_id AND version > 1) WHERE chapter.book_id = ? ORDER BY chapter.chapter_order', [id]) as ChapterBookResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres.` : `Unable to retrieve chapters.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT title, chapter_order, content.content FROM book_chapters AS chapter LEFT JOIN book_chapter_content AS content ON chapter.chapter_id = content.chapter_id AND content.version = (SELECT MAX(version) FROM book_chapter_content WHERE chapter_id = chapter.chapter_id AND version > 1) WHERE chapter.book_id = ? ORDER BY chapter.chapter_order'; + const params: SQLiteValue[] = [bookId]; + chapters = db.all(query, params) as ChapterBookResult[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres.' : 'Unable to retrieve chapters.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } - if (result.length === 0) { - throw new Error(lang === 'fr' ? `Aucun chapitre trouvé.` : `No chapters found.`); + if (chapters.length === 0) { + throw new Error(lang === 'fr' ? 'Aucun chapitre trouvé.' : 'No chapters found.'); } - return result; + return chapters; } + + /** + * Retrieves all chapters for a book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages + * @returns List of book chapters + */ static async fetchBookChapters(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update FROM book_chapters WHERE author_id=? AND book_id=?', [userId, bookId]) as BookChaptersTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres.` : `Unable to retrieve chapters.`); - } else { - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update FROM book_chapters WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + return db.all(query, params) as BookChaptersTable[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres.' : 'Unable to retrieve chapters.'); } + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - + + /** + * Retrieves chapter information for a specific chapter. + * @param userId - The user identifier + * @param chapterId - The chapter identifier + * @param lang - The language for error messages + * @returns List of chapter info records + */ static async fetchBookChapterInfos(userId: string, chapterId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update FROM book_chapter_infos WHERE author_id=? AND chapter_id=?', [userId, chapterId]) as BookChapterInfosTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les infos des chapitres.` : `Unable to retrieve chapter infos.`); - } else { - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update FROM book_chapter_infos WHERE author_id=? AND chapter_id=?'; + const params: SQLiteValue[] = [userId, chapterId]; + return db.all(query, params) as BookChapterInfosTable[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les infos des chapitres.' : 'Unable to retrieve chapter infos.'); } + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + + /** + * Retrieves synced chapters for a user. + * @param userId - The user identifier + * @param lang - The language for error messages + * @returns List of synced chapters + */ static fetchSyncedChapters(userId: string, lang: 'fr' | 'en'): SyncedChapterResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT chapter_id, book_id, title, last_update FROM book_chapters WHERE author_id = ?', [userId]) as SyncedChapterResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres synchronisés.` : `Unable to retrieve synced chapters.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_id, book_id, title, last_update FROM book_chapters WHERE author_id = ?'; + const params: SQLiteValue[] = [userId]; + return db.all(query, params) as SyncedChapterResult[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres synchronisés.' : 'Unable to retrieve synced chapters.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - + + /** + * Retrieves synced chapter infos for a user. + * @param userId - The user identifier + * @param lang - The language for error messages + * @returns List of synced chapter infos + */ static fetchSyncedChapterInfos(userId: string, lang: 'fr' | 'en'): SyncedChapterInfoResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT chapter_info_id, chapter_id, book_id, last_update FROM book_chapter_infos WHERE author_id = ?', [userId]) as SyncedChapterInfoResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de récupérer les infos des chapitres synchronisés.` : `Unable to retrieve synced chapter infos.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_info_id, chapter_id, book_id, last_update FROM book_chapter_infos WHERE author_id = ?'; + const params: SQLiteValue[] = [userId]; + return db.all(query, params) as SyncedChapterInfoResult[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? 'Impossible de récupérer les infos des chapitres synchronisés.' : 'Unable to retrieve synced chapter infos.'); } + console.error("An unknown error occurred."); + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } + + /** + * Inserts a synced chapter from the server. + * @param chapterId - The chapter identifier + * @param bookId - The book identifier + * @param authorId - The author identifier + * @param title - The encrypted title + * @param hashedTitle - The hashed title + * @param wordsCount - The word count + * @param chapterOrder - The chapter order + * @param lastUpdate - The last update timestamp + * @param lang - The language for error messages + * @returns true if the insertion was successful + */ static insertSyncChapter(chapterId: string, bookId: string, authorId: string, title: string, hashedTitle: string | null, wordsCount: number | null, chapterOrder: number | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_chapters (chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [chapterId, bookId, authorId, title, hashedTitle, wordsCount, chapterOrder, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible d'insérer le chapitre.` : `Unable to insert chapter.`); - } else { - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'INSERT INTO book_chapters (chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'; + const params: SQLiteValue[] = [chapterId, bookId, authorId, title, hashedTitle, wordsCount, chapterOrder, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible d'insérer le chapitre." : 'Unable to insert chapter.'); } + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - + + /** + * Inserts synced chapter info from the server. + * @param chapterInfoId - The chapter info identifier + * @param chapterId - The chapter identifier + * @param actId - The act identifier + * @param incidentId - The incident identifier + * @param plotPointId - The plot point identifier + * @param bookId - The book identifier + * @param authorId - The author identifier + * @param summary - The chapter summary + * @param goal - The chapter goal + * @param lastUpdate - The last update timestamp + * @param lang - The language for error messages + * @returns true if the insertion was successful + */ static insertSyncChapterInfo(chapterInfoId: string, chapterId: string, actId: number | null, incidentId: string | null, plotPointId: string | null, bookId: string, authorId: string, summary: string | null, goal: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run(`INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [chapterInfoId, chapterId, actId, incidentId, plotPointId, bookId, authorId, summary, goal, lastUpdate]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible d'insérer les infos du chapitre.` : `Unable to insert chapter info.`); - } else { - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; + const params: SQLiteValue[] = [chapterInfoId, chapterId, actId, incidentId, plotPointId, bookId, authorId, summary, goal, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); + throw new Error(lang === 'fr' ? "Impossible d'insérer les infos du chapitre." : 'Unable to insert chapter info.'); } + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - - static async fetchCompleteChapterById(id: string, lang: "fr" | "en"):Promise { + + /** + * Retrieves a complete chapter by its identifier. + * @param chapterId - The chapter identifier + * @param lang - The language for error messages + * @returns The complete chapter data + */ + static async fetchCompleteChapterById(chapterId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update - FROM book_chapters - WHERE chapter_id = ?`, - [id] - ) as BookChaptersTable[]; - } catch (e:unknown){ - if (e instanceof Error) { - throw new Error(lang === 'fr' ? `Impossible de récupérer le chapitre complet.` : `Unable to retrieve complete chapter.`); - } else { - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update FROM book_chapters WHERE chapter_id = ?'; + const params: SQLiteValue[] = [chapterId]; + return db.all(query, params) as BookChaptersTable[]; + } catch (error: unknown) { + if (error instanceof Error) { + throw new Error(lang === 'fr' ? 'Impossible de récupérer le chapitre complet.' : 'Unable to retrieve complete chapter.'); } + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - - static async fetchCompleteChapterInfoById(id: string, lang: "fr" | "en"):Promise { + + /** + * Retrieves complete chapter info by its identifier. + * @param chapterInfoId - The chapter info identifier + * @param lang - The language for error messages + * @returns The complete chapter info data + */ + static async fetchCompleteChapterInfoById(chapterInfoId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update - FROM book_chapter_infos - WHERE chapter_info_id = ?`, - [id] - ) as BookChapterInfosTable[]; - } catch (e:unknown){ - if (e instanceof Error) { - throw new Error(lang === 'fr' ? `Impossible de récupérer les informations de chapitre complètes.` : `Unable to retrieve complete chapter info.`); - } else { - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); + const query: string = 'SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update FROM book_chapter_infos WHERE chapter_info_id = ?'; + const params: SQLiteValue[] = [chapterInfoId]; + return db.all(query, params) as BookChapterInfosTable[]; + } catch (error: unknown) { + if (error instanceof Error) { + throw new Error(lang === 'fr' ? 'Impossible de récupérer les informations de chapitre complètes.' : 'Unable to retrieve complete chapter info.'); } + throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } diff --git a/electron/database/repositories/chaptercontent.repository.ts b/electron/database/repositories/chaptercontent.repository.ts index 2889e01..62b0a04 100644 --- a/electron/database/repositories/chaptercontent.repository.ts +++ b/electron/database/repositories/chaptercontent.repository.ts @@ -1,7 +1,7 @@ -import {Database, QueryResult, RunResult, SQLiteValue} from "node-sqlite3-wasm"; +import { Database, QueryResult, RunResult, SQLiteValue } from "node-sqlite3-wasm"; import System from "@/electron/database/System"; -export interface ChapterContentQueryResult extends Record{ +export interface ChapterContentQueryResult extends Record { chapter_id: string; version: number; content: string; @@ -14,7 +14,7 @@ export interface ContentQueryResult extends Record { content: string; } -export interface CompanionContentQueryResult extends Record{ +export interface CompanionContentQueryResult extends Record { version: number; content: string; words_count: number; @@ -38,14 +38,36 @@ export interface SyncedChapterContentResult extends Record } export default class ChapterContentRepository { + /** + * Fetches the last chapter content for a given book. + * @param userId - The ID of the user/author. + * @param bookId - The ID of the book. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns An array of chapter content results ordered by chapter order and version descending. + */ public static fetchLastChapterContent(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterContentQueryResult[] { try { const db: Database = System.getDb(); - const query: string = `SELECT book_chapters.chapter_id as chapter_id, COALESCE(book_chapter_content.version, 2) AS version, COALESCE(book_chapter_content.content, '') AS content, COALESCE(book_chapter_content.words_count, 0) AS words_count, book_chapters.title, book_chapters.chapter_order FROM book_chapters LEFT JOIN book_chapter_content ON book_chapters.chapter_id = book_chapter_content.chapter_id WHERE book_chapters.author_id = ? AND book_chapters.book_id = ? ORDER BY book_chapters.chapter_order DESC, book_chapter_content.version DESC LIMIT 1`; - return db.all(query, [userId, bookId]) as ChapterContentQueryResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + SELECT + book_chapters.chapter_id as chapter_id, + COALESCE(book_chapter_content.version, 2) AS version, + COALESCE(book_chapter_content.content, '') AS content, + COALESCE(book_chapter_content.words_count, 0) AS words_count, + book_chapters.title, + book_chapters.chapter_order + FROM book_chapters + LEFT JOIN book_chapter_content ON book_chapters.chapter_id = book_chapter_content.chapter_id + WHERE book_chapters.author_id = ? AND book_chapters.book_id = ? + ORDER BY book_chapters.chapter_order DESC, book_chapter_content.version DESC + LIMIT 1 + `; + const params: SQLiteValue[] = [userId, bookId]; + const chapterContents: ChapterContentQueryResult[] = db.all(query, params) as ChapterContentQueryResult[]; + return chapterContents; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer le dernier chapitre.` : `Unable to retrieve last chapter.`); } else { console.error("An unknown error occurred."); @@ -53,20 +75,37 @@ export default class ChapterContentRepository { } } } + + /** + * Updates the content of a chapter. If no existing content is found, inserts a new record. + * @param userId - The ID of the user/author. + * @param chapterId - The ID of the chapter. + * @param version - The version number of the content. + * @param encryptContent - The encrypted content string. + * @param wordsCount - The word count of the content. + * @param lastUpdate - The timestamp of the last update. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the update or insert was successful. + */ public static updateChapterContent(userId: string, chapterId: string, version: number, encryptContent: string, wordsCount: number, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_chapter_content SET content=?, words_count=?, last_update=? WHERE chapter_id=? AND author_id=? AND version=?', [encryptContent, wordsCount, lastUpdate, chapterId, userId, version]); - if (result.changes > 0) { + const updateQuery: string = 'UPDATE book_chapter_content SET content=?, words_count=?, last_update=? WHERE chapter_id=? AND author_id=? AND version=?'; + const updateParams: SQLiteValue[] = [encryptContent, wordsCount, lastUpdate, chapterId, userId, version]; + const updateResult: RunResult = db.run(updateQuery, updateParams); + + if (updateResult.changes > 0) { return true; } else { - const contentId:string = System.createUniqueId(); - const insertResult: RunResult = db.run('INSERT INTO book_chapter_content (content_id,chapter_id, author_id, version, content, words_count, last_update) VALUES (?,?,?,?,?,?,?)', [contentId, chapterId, userId, version, encryptContent, wordsCount, lastUpdate]); + const contentId: string = System.createUniqueId(); + const insertQuery: string = 'INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, last_update) VALUES (?,?,?,?,?,?,?)'; + const insertParams: SQLiteValue[] = [contentId, chapterId, userId, version, encryptContent, wordsCount, lastUpdate]; + const insertResult: RunResult = db.run(insertQuery, insertParams); return insertResult.changes > 0; } - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le contenu du chapitre.` : `Unable to update chapter content.`); } else { console.error("An unknown error occurred."); @@ -74,14 +113,25 @@ export default class ChapterContentRepository { } } } - - static fetchCompanionContent(userId: string, chapterIdNum: string, versionNum: number, lang: 'fr' | 'en' = 'fr'): CompanionContentQueryResult[] { + + /** + * Fetches companion content for a specific chapter and version. + * @param userId - The ID of the user/author. + * @param chapterId - The ID of the chapter. + * @param version - The version number to fetch. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns An array of companion content results. + */ + static fetchCompanionContent(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): CompanionContentQueryResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT version, content, words_count FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?', [userId, chapterIdNum, versionNum]) as CompanionContentQueryResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT version, content, words_count FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?'; + const params: SQLiteValue[] = [userId, chapterId, version]; + const companionContents: CompanionContentQueryResult[] = db.all(query, params) as CompanionContentQueryResult[]; + return companionContents; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu compagnon.` : `Unable to retrieve companion content.`); } else { console.error("An unknown error occurred."); @@ -89,53 +139,91 @@ export default class ChapterContentRepository { } } } + + /** + * Fetches chapter content by its order position within a book. + * @param userId - The ID of the user/author. + * @param chapterOrder - The order position of the chapter. + * @param bookId - The ID of the book. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns The content query result for the specified chapter. + * @throws Error if no chapter is found with the specified order. + */ static fetchChapterContentByChapterOrder(userId: string, chapterOrder: number, bookId: string, lang: 'fr' | 'en' = 'fr'): ContentQueryResult { - let result: ContentQueryResult | null; + let chapterContent: ContentQueryResult | null; try { const db: Database = System.getDb(); - result = db.get('SELECT content.content FROM book_chapters as chapter INNER JOIN book_chapter_content AS content ON chapter.chapter_id=content.chapter_id WHERE chapter.chapter_order=? AND content.version=2 AND chapter.book_id=? AND chapter.author_id=?', [chapterOrder, bookId, userId]) as ContentQueryResult | null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + SELECT content.content + FROM book_chapters as chapter + INNER JOIN book_chapter_content AS content ON chapter.chapter_id=content.chapter_id + WHERE chapter.chapter_order=? AND content.version=2 AND chapter.book_id=? AND chapter.author_id=? + `; + const params: SQLiteValue[] = [chapterOrder, bookId, userId]; + chapterContent = db.get(query, params) as ContentQueryResult | null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu du chapitre.` : `Unable to retrieve chapter content.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result) { + if (!chapterContent) { throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cet ordre.` : `No chapter found with this order.`); } - return result; + return chapterContent; } - - static fetchChapterContentByVersion(userId: string, chapterid: string, version: number, lang: 'fr' | 'en' = 'fr'): ContentQueryResult { - let result: ContentQueryResult | null; + + /** + * Fetches chapter content by chapter ID and version number. + * @param userId - The ID of the user/author. + * @param chapterId - The ID of the chapter. + * @param version - The version number to fetch. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns The content query result for the specified version. + * @throws Error if no chapter is found with the specified version. + */ + static fetchChapterContentByVersion(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): ContentQueryResult { + let chapterContent: ContentQueryResult | null; try { const db: Database = System.getDb(); - result = db.get('SELECT content FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?', [userId, chapterid, version]) as ContentQueryResult | null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT content FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?'; + const params: SQLiteValue[] = [userId, chapterId, version]; + chapterContent = db.get(query, params) as ContentQueryResult | null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu du chapitre.` : `Unable to retrieve chapter content.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result) { + if (!chapterContent) { throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cette version.` : `No chapter found with this version.`); } - return result; + return chapterContent; } - static isChapterContentExist(userId: string, content_id: string, lang: "fr" | "en"): boolean { + + /** + * Checks whether chapter content exists for a given content ID and user. + * @param userId - The ID of the user/author. + * @param contentId - The ID of the content to check. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the chapter content exists, false otherwise. + */ + static isChapterContentExist(userId: string, contentId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `book_chapter_content` WHERE `content_id`=? AND `author_id`=?', [content_id, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM `book_chapter_content` WHERE `content_id`=? AND `author_id`=?'; + const params: SQLiteValue[] = [contentId, userId]; + const existenceCheck: QueryResult | null = db.get(query, params) || null; + return existenceCheck !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du contenu du chapitre.` : `Unable to check chapter content existence.`); } else { console.error("An unknown error occurred."); @@ -143,26 +231,47 @@ export default class ChapterContentRepository { } } } - static async fetchBookChapterContents(userId: string,chapterId:string, lang: 'fr' | 'en'): Promise { + + /** + * Fetches all chapter contents for a specific chapter belonging to a user. + * @param userId - The ID of the user/author. + * @param chapterId - The ID of the chapter. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns A promise resolving to an array of book chapter content records. + */ + static async fetchBookChapterContents(userId: string, chapterId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE author_id=? AND chapter_id=?', [userId, chapterId]) as BookChapterContentTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE author_id=? AND chapter_id=?'; + const params: SQLiteValue[] = [userId, chapterId]; + const bookChapterContents: BookChapterContentTable[] = db.all(query, params) as BookChapterContentTable[]; + return bookChapterContents; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu des chapitres.` : `Unable to retrieve chapter contents.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Fetches all synced chapter contents for a user (content ID, chapter ID, and last update timestamp). + * @param userId - The ID of the user/author. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns An array of synced chapter content results. + */ static fetchSyncedChapterContents(userId: string, lang: 'fr' | 'en'): SyncedChapterContentResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT content_id, chapter_id, last_update FROM book_chapter_content WHERE author_id = ?', [userId]) as SyncedChapterContentResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT content_id, chapter_id, last_update FROM book_chapter_content WHERE author_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedChapterContents: SyncedChapterContentResult[] = db.all(query, params) as SyncedChapterContentResult[]; + return syncedChapterContents; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu des chapitres synchronisés.` : `Unable to retrieve synced chapter contents.`); } else { console.error("An unknown error occurred."); @@ -170,54 +279,101 @@ export default class ChapterContentRepository { } } } + + /** + * Inserts a new chapter content record during synchronization. + * @param contentId - The unique ID for the content. + * @param chapterId - The ID of the chapter. + * @param authorId - The ID of the author. + * @param version - The version number of the content. + * @param content - The content string (can be null). + * @param wordsCount - The word count of the content. + * @param timeOnIt - The time spent on this content. + * @param lastUpdate - The timestamp of the last update. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the insert was successful. + */ static insertSyncChapterContent(contentId: string, chapterId: string, authorId: string, version: number, content: string | null, wordsCount: number, timeOnIt: number, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [contentId, chapterId, authorId, version, content, wordsCount, timeOnIt, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + `; + const params: SQLiteValue[] = [contentId, chapterId, authorId, version, content, wordsCount, timeOnIt, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer le contenu du chapitre.` : `Unable to insert chapter content.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static async fetchCompleteChapterContentById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches the complete chapter content record by its content ID. + * @param contentId - The ID of the content to fetch. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns A promise resolving to an array of book chapter content records. + */ + static async fetchCompleteChapterContentById(contentId: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all(`SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE content_id = ?`, [id]) as BookChapterContentTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + const query: string = 'SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE content_id = ?'; + const params: SQLiteValue[] = [contentId]; + const completeChapterContent: BookChapterContentTable[] = db.all(query, params) as BookChapterContentTable[]; + return completeChapterContent; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu de chapitre complet.` : `Unable to retrieve complete chapter content.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Fetches a complete chapter with its content by joining chapters and chapter content tables. + * @param userId - The ID of the user/author. + * @param chapterId - The ID of the chapter. + * @param version - The version number of the content to fetch. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns The chapter content query result with chapter metadata. + * @throws Error if no chapter is found with the specified ID. + */ public static fetchWholeChapter(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): ChapterContentQueryResult { - let result: ChapterContentQueryResult | null; + let wholeChapter: ChapterContentQueryResult | null; try { const db: Database = System.getDb(); - const query: string = 'SELECT chapter.chapter_id as chapter_id, chapter.title as title, chapter.chapter_order, chapter.words_count, content.content AS content, content.version as version FROM book_chapters AS chapter LEFT JOIN book_chapter_content AS content ON content.chapter_id = chapter.chapter_id AND content.version = ? WHERE chapter.chapter_id = ? AND chapter.author_id = ?'; - result = db.get(query, [version, chapterId, userId]) as ChapterContentQueryResult | null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + SELECT + chapter.chapter_id as chapter_id, + chapter.title as title, + chapter.chapter_order, + chapter.words_count, + content.content AS content, + content.version as version + FROM book_chapters AS chapter + LEFT JOIN book_chapter_content AS content ON content.chapter_id = chapter.chapter_id AND content.version = ? + WHERE chapter.chapter_id = ? AND chapter.author_id = ? + `; + const params: SQLiteValue[] = [version, chapterId, userId]; + wholeChapter = db.get(query, params) as ChapterContentQueryResult | null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer le chapitre.` : `Unable to retrieve chapter.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result) { + if (!wholeChapter) { throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cet ID.` : `No chapter found with this ID.`); } - return result; + return wholeChapter; } -} \ No newline at end of file +} diff --git a/electron/database/repositories/character.repository.ts b/electron/database/repositories/character.repository.ts index 906da64..ed3ca49 100644 --- a/electron/database/repositories/character.repository.ts +++ b/electron/database/repositories/character.repository.ts @@ -1,4 +1,4 @@ -import {Database, QueryResult, RunResult, SQLiteValue} from 'node-sqlite3-wasm'; +import { Database, QueryResult, RunResult, SQLiteValue } from 'node-sqlite3-wasm'; import System from "../System.js"; export interface BookCharactersTable extends Record { @@ -71,71 +71,125 @@ export interface CompleteCharacterResult extends Record { } export default class CharacterRepo { + /** + * Fetches all characters for a specific book and user. + * @param userId - The unique identifier of the user + * @param bookId - The unique identifier of the book + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of character results + */ public static fetchCharacters(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): CharacterResult[] { - let result: CharacterResult[]; try { const db: Database = System.getDb(); - result = db.all('SELECT character_id, first_name, last_name, title, category, image, role, biography, history FROM book_characters WHERE book_id=? AND user_id=?', [bookId, userId]) as CharacterResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT character_id, first_name, last_name, title, category, image, role, biography, history FROM book_characters WHERE book_id=? AND user_id=?'; + const params: SQLiteValue[] = [bookId, userId]; + const characters: CharacterResult[] = db.all(query, params) as CharacterResult[]; + return characters; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages.` : `Unable to retrieve characters.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - return result; } + /** + * Adds a new character to the database. + * @param userId - The unique identifier of the user + * @param characterId - The unique identifier for the new character + * @param encryptedName - The encrypted first name of the character + * @param encryptedLastName - The encrypted last name of the character + * @param encryptedTitle - The encrypted title of the character + * @param encryptedCategory - The encrypted category of the character + * @param encryptedImage - The encrypted image path of the character + * @param encryptedRole - The encrypted role of the character + * @param encryptedBiography - The encrypted biography of the character + * @param encryptedHistory - The encrypted history of the character + * @param bookId - The unique identifier of the book + * @param lang - The language for error messages ('fr' or 'en') + * @returns The character ID if successful + */ public static addNewCharacter(userId: string, characterId: string, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, bookId: string, lang: 'fr' | 'en' = 'fr'): string { - let result: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO `book_characters` (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'INSERT INTO `book_characters` (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'; + const params: SQLiteValue[] = [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds()]; + const insertResult: RunResult = db.run(query, params); + if (!insertResult || insertResult.changes === 0) { + throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du personnage.` : `Error adding character.`); + } + return characterId; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le personnage.` : `Unable to add character.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result || result.changes === 0) { - throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du personnage.` : `Error adding character.`); - } - return characterId; } + /** + * Inserts a new attribute for a character. + * @param attributeId - The unique identifier for the new attribute + * @param characterId - The unique identifier of the character + * @param userId - The unique identifier of the user + * @param type - The attribute name/type + * @param name - The attribute value + * @param lang - The language for error messages ('fr' or 'en') + * @returns The attribute ID if successful + */ static insertAttribute(attributeId: string, characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string { - let result: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?,?,?,?,?,?)', [attributeId, characterId, userId, type, name, System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?,?,?,?,?,?)'; + const params: SQLiteValue[] = [attributeId, characterId, userId, type, name, System.timeStampInSeconds()]; + const insertResult: RunResult = db.run(query, params); + if (!insertResult || insertResult.changes === 0) { + throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'attribut.` : `Error adding attribute.`); + } + return attributeId; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter l'attribut.` : `Unable to add attribute.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result || result.changes === 0) { - throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'attribut.` : `Error adding attribute.`); - } - return attributeId; } + /** + * Updates an existing character's information. + * @param userId - The unique identifier of the user + * @param id - The unique identifier of the character to update + * @param encryptedName - The encrypted first name of the character + * @param encryptedLastName - The encrypted last name of the character + * @param encryptedTitle - The encrypted title of the character + * @param encryptedCategory - The encrypted category of the character + * @param encryptedImage - The encrypted image path of the character + * @param encryptedRole - The encrypted role of the character + * @param encryptedBiography - The encrypted biography of the character + * @param encryptedHistory - The encrypted history of the character + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the update was successful, false otherwise + */ static updateCharacter(userId: string, id: string, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=?,`last_update`=? WHERE `character_id`=? AND `user_id`=?', [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, lastUpdate, id, userId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=?,`last_update`=? WHERE `character_id`=? AND `user_id`=?'; + const params: SQLiteValue[] = [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, lastUpdate, id, userId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le personnage.` : `Unable to update character.`); } else { console.error("An unknown error occurred."); @@ -144,14 +198,23 @@ export default class CharacterRepo { } } + /** + * Deletes a character attribute from the database. + * @param userId - The unique identifier of the user + * @param attributeId - The unique identifier of the attribute to delete + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the deletion was successful, false otherwise + */ static deleteAttribute(userId: string, attributeId: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?', [attributeId, userId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'DELETE FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?'; + const params: SQLiteValue[] = [attributeId, userId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de supprimer l'attribut.` : `Unable to delete attribute.`); } else { console.error("An unknown error occurred."); @@ -160,57 +223,85 @@ export default class CharacterRepo { } } + /** + * Fetches all attributes for a specific character. + * @param characterId - The unique identifier of the character + * @param userId - The unique identifier of the user + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of attribute results + */ static fetchAttributes(characterId: string, userId: string, lang: 'fr' | 'en' = 'fr'): AttributeResult[] { - let result: AttributeResult[]; try { const db: Database = System.getDb(); - result = db.all('SELECT attr_id, attribute_name, attribute_value FROM book_characters_attributes WHERE character_id=? AND user_id=?', [characterId, userId]) as AttributeResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT attr_id, attribute_name, attribute_value FROM book_characters_attributes WHERE character_id=? AND user_id=?'; + const params: SQLiteValue[] = [characterId, userId]; + const attributes: AttributeResult[] = db.all(query, params) as AttributeResult[]; + return attributes; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs.` : `Unable to retrieve attributes.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - return result; } + /** + * Fetches complete character information including attributes, optionally filtered by character IDs. + * @param userId - The unique identifier of the user + * @param bookId - The unique identifier of the book + * @param tags - An optional array of character IDs to filter by + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of complete character results with attributes + */ static fetchCompleteCharacters(userId: string, bookId: string, tags: string[], lang: 'fr' | 'en' = 'fr'): CompleteCharacterResult[] { - let result: CompleteCharacterResult[]; try { const db: Database = System.getDb(); let query: string = 'SELECT charac.character_id, first_name, last_name, category, title, role, biography, history, attribute_name, attribute_value FROM book_characters AS charac LEFT JOIN book_characters_attributes AS attr ON charac.character_id=attr.character_id WHERE charac.user_id=? AND charac.book_id=?'; - let values: any[] = [userId, bookId]; + let params: SQLiteValue[] = [userId, bookId]; if (tags && tags.length > 0) { const placeholders: string = tags.map((): string => '?').join(','); query += ` AND charac.character_id IN (${placeholders})`; - values.push(...tags); + params.push(...tags); } - result = db.all(query, values) as CompleteCharacterResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const characters: CompleteCharacterResult[] = db.all(query, params) as CompleteCharacterResult[]; + if (characters.length === 0) { + throw new Error(lang === 'fr' ? `Aucun personnage complet trouvé.` : `No complete characters found.`); + } + return characters; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages complets.` : `Unable to retrieve complete characters.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (result.length === 0) { - throw new Error(lang === 'fr' ? `Aucun personnage complet trouvé.` : `No complete characters found.`); - } - return result; } - static updateCharacterAttribute(userId: string, characterAttributeId: string, attributeName: string, attributeValue: string, lastUpdate: number,lang: "fr" | "en"):boolean { + + /** + * Updates an existing character attribute. + * @param userId - The unique identifier of the user + * @param characterAttributeId - The unique identifier of the attribute to update + * @param attributeName - The new attribute name + * @param attributeValue - The new attribute value + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the update was successful, false otherwise + */ + static updateCharacterAttribute(userId: string, characterAttributeId: string, attributeName: string, attributeValue: string, lastUpdate: number, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result:RunResult = db.run('UPDATE `book_characters_attributes` SET `attribute_name`=?,`attribute_value`=?, last_update=FROM_UNIXTIME(?) WHERE `attr_id`=UUID_TO_BIN(?) AND `user_id`=UUID_TO_BIN(?)', [attributeName, attributeValue, lastUpdate, characterAttributeId, userId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'UPDATE `book_characters_attributes` SET `attribute_name`=?,`attribute_value`=?, last_update=FROM_UNIXTIME(?) WHERE `attr_id`=UUID_TO_BIN(?) AND `user_id`=UUID_TO_BIN(?)'; + const params: SQLiteValue[] = [attributeName, attributeValue, lastUpdate, characterAttributeId, userId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'attribut du personnage.` : `Unable to update character attribute.`); } else { console.error("An unknown error occurred."); @@ -218,14 +309,24 @@ export default class CharacterRepo { } } } - static isCharacterExist(userId: string, characterId: string,lang: "fr" | "en"): boolean { + + /** + * Checks if a character exists in the database. + * @param userId - The unique identifier of the user + * @param characterId - The unique identifier of the character to check + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the character exists, false otherwise + */ + static isCharacterExist(userId: string, characterId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `book_characters` WHERE `character_id`=? AND `user_id`=?', [characterId, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM `book_characters` WHERE `character_id`=? AND `user_id`=?'; + const params: SQLiteValue[] = [characterId, userId]; + const character: QueryResult | null = db.get(query, params) || null; + return character !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du personnage.` : `Unable to check character existence.`); } else { console.error("An unknown error occurred."); @@ -233,15 +334,24 @@ export default class CharacterRepo { } } } - - static isCharacterAttributeExist(userId: string, characterAttributeId: string,lang: "fr" | "en"): boolean { + + /** + * Checks if a character attribute exists in the database. + * @param userId - The unique identifier of the user + * @param characterAttributeId - The unique identifier of the attribute to check + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the attribute exists, false otherwise + */ + static isCharacterAttributeExist(userId: string, characterAttributeId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?', [characterAttributeId, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?'; + const params: SQLiteValue[] = [characterAttributeId, userId]; + const attribute: QueryResult | null = db.get(query, params) || null; + return attribute !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'attribut du personnage.` : `Unable to check character attribute existence.`); } else { console.error("An unknown error occurred."); @@ -249,41 +359,71 @@ export default class CharacterRepo { } } } + + /** + * Fetches all characters for a specific book asynchronously. + * @param userId - The unique identifier of the user + * @param bookId - The unique identifier of the book + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book characters + */ static async fetchBookCharacters(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update FROM book_characters WHERE user_id=? AND book_id=?', [userId, bookId]) as BookCharactersTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update FROM book_characters WHERE user_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const characters: BookCharactersTable[] = db.all(query, params) as BookCharactersTable[]; + return characters; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages.` : `Unable to retrieve characters.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - - static async fetchBookCharactersAttributes(userId: string, characterId:string, lang: 'fr' | 'en'): Promise { + + /** + * Fetches all attributes for a specific character asynchronously. + * @param userId - The unique identifier of the user + * @param characterId - The unique identifier of the character + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of character attributes + */ + static async fetchBookCharactersAttributes(userId: string, characterId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update FROM book_characters_attributes WHERE user_id=? AND character_id=?', [userId, characterId]) as BookCharactersAttributesTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update FROM book_characters_attributes WHERE user_id=? AND character_id=?'; + const params: SQLiteValue[] = [userId, characterId]; + const attributes: BookCharactersAttributesTable[] = db.all(query, params) as BookCharactersAttributesTable[]; + return attributes; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs des personnages.` : `Unable to retrieve character attributes.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - + + /** + * Fetches all synced characters for a user. + * @param userId - The unique identifier of the user + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced character results + */ static fetchSyncedCharacters(userId: string, lang: 'fr' | 'en'): SyncedCharacterResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT character_id, book_id, first_name, last_update FROM book_characters WHERE user_id = ?', [userId]) as SyncedCharacterResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT character_id, book_id, first_name, last_update FROM book_characters WHERE user_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedCharacters: SyncedCharacterResult[] = db.all(query, params) as SyncedCharacterResult[]; + return syncedCharacters; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages synchronisés.` : `Unable to retrieve synced characters.`); } else { console.error("An unknown error occurred."); @@ -291,14 +431,23 @@ export default class CharacterRepo { } } } - + + /** + * Fetches all synced character attributes for a user. + * @param userId - The unique identifier of the user + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced character attribute results + */ static fetchSyncedCharacterAttributes(userId: string, lang: 'fr' | 'en'): SyncedCharacterAttributeResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT attr_id, character_id, attribute_name, last_update FROM book_characters_attributes WHERE user_id = ?', [userId]) as SyncedCharacterAttributeResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT attr_id, character_id, attribute_name, last_update FROM book_characters_attributes WHERE user_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedAttributes: SyncedCharacterAttributeResult[] = db.all(query, params) as SyncedCharacterAttributeResult[]; + return syncedAttributes; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs des personnages synchronisés.` : `Unable to retrieve synced character attributes.`); } else { console.error("An unknown error occurred."); @@ -306,73 +455,112 @@ export default class CharacterRepo { } } } - + + /** + * Inserts a synced character into the database. + * @param characterId - The unique identifier of the character + * @param bookId - The unique identifier of the book + * @param userId - The unique identifier of the user + * @param firstName - The first name of the character + * @param lastName - The last name of the character (nullable) + * @param category - The category of the character + * @param title - The title of the character (nullable) + * @param image - The image path of the character (nullable) + * @param role - The role of the character (nullable) + * @param biography - The biography of the character (nullable) + * @param history - The history of the character (nullable) + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the insertion was successful, false otherwise + */ static insertSyncCharacter(characterId: string, bookId: string, userId: string, firstName: string, lastName: string | null, category: string, title: string | null, image: string | null, role: string | null, biography: string | null, history: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_characters (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [characterId, bookId, userId, firstName, lastName, category, title, image, role, biography, history, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `INSERT INTO book_characters (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + const params: SQLiteValue[] = [characterId, bookId, userId, firstName, lastName, category, title, image, role, biography, history, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer le personnage.` : `Unable to insert character.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - + + /** + * Inserts a synced character attribute into the database. + * @param attrId - The unique identifier of the attribute + * @param characterId - The unique identifier of the character + * @param userId - The unique identifier of the user + * @param attributeName - The name of the attribute + * @param attributeValue - The value of the attribute + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the insertion was successful, false otherwise + */ static insertSyncCharacterAttribute(attrId: string, characterId: string, userId: string, attributeName: string, attributeValue: string, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) - VALUES (?, ?, ?, ?, ?, ?)`, - [attrId, characterId, userId, attributeName, attributeValue, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `INSERT INTO book_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) + VALUES (?, ?, ?, ?, ?, ?)`; + const params: SQLiteValue[] = [attrId, characterId, userId, attributeName, attributeValue, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer l'attribut du personnage.` : `Unable to insert character attribute.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static async fetchCompleteCharacterById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches a complete character by its ID. + * @param id - The unique identifier of the character + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book characters (typically one) + */ + static async fetchCompleteCharacterById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update + const query: string = `SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update FROM book_characters - WHERE character_id = ?`, - [id] - ) as BookCharactersTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + WHERE character_id = ?`; + const params: SQLiteValue[] = [id]; + const character: BookCharactersTable[] = db.all(query, params) as BookCharactersTable[]; + return character; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le personnage complet.` : `Unable to retrieve complete character.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - - static async fetchCompleteCharacterAttributeById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches a complete character attribute by its ID. + * @param id - The unique identifier of the attribute + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of character attributes (typically one) + */ + static async fetchCompleteCharacterAttributeById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update + const query: string = `SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update FROM book_characters_attributes - WHERE attr_id = ?`, - [id] - ) as BookCharactersAttributesTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + WHERE attr_id = ?`; + const params: SQLiteValue[] = [id]; + const attribute: BookCharactersAttributesTable[] = db.all(query, params) as BookCharactersAttributesTable[]; + return attribute; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer l'attribut de personnage complet.` : `Unable to retrieve complete character attribute.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); diff --git a/electron/database/repositories/guideline.repository.ts b/electron/database/repositories/guideline.repository.ts index e97278d..25410ee 100644 --- a/electron/database/repositories/guideline.repository.ts +++ b/electron/database/repositories/guideline.repository.ts @@ -1,6 +1,5 @@ -import {Database, RunResult, SQLiteValue} from "node-sqlite3-wasm"; +import { Database, RunResult, SQLiteValue } from "node-sqlite3-wasm"; import System from "@/electron/database/System"; -import {ChapterBookResult, EritBooksTable} from "@/electron/database/repositories/book.repository"; export interface BookAIGuideLineTable extends Record { user_id: string; @@ -72,35 +71,111 @@ export interface GuideLineAIQuery extends Record { } export default class GuidelineRepo { + /** + * Fetches the guideline for a specific book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of guideline query results + * @throws Error if the guideline cannot be retrieved + */ public static fetchGuideLine(userId: string, bookId: string, lang: 'fr' | 'en'): GuideLineQuery[] { - let result: GuideLineQuery[]; + let guidelines: GuideLineQuery[]; try { const db: Database = System.getDb(); - result = db.all('SELECT * FROM book_guide_line WHERE book_id=? AND user_id=?', [bookId, userId]) as GuideLineQuery[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT * FROM book_guide_line WHERE book_id=? AND user_id=?'; + const params: SQLiteValue[] = [bookId, userId]; + guidelines = db.all(query, params) as GuideLineQuery[]; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice.` : `Unable to retrieve guideline.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - return result; + return guidelines; } - static updateGuideLine(userId: string, bookId: string, encryptedTone: string, encryptedAtmosphere: string, encryptedWritingStyle: string, encryptedThemes: string, encryptedSymbolism: string, encryptedMotifs: string, encryptedNarrativeVoice: string, encryptedPacing: string, encryptedKeyMessages: string, encryptedIntendedAudience: string, lang: 'fr' | 'en'): boolean { + + /** + * Updates or inserts a guideline for a specific book. + * If the guideline exists, it updates it; otherwise, it inserts a new one. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param encryptedTone - The encrypted tone value + * @param encryptedAtmosphere - The encrypted atmosphere value + * @param encryptedWritingStyle - The encrypted writing style value + * @param encryptedThemes - The encrypted themes value + * @param encryptedSymbolism - The encrypted symbolism value + * @param encryptedMotifs - The encrypted motifs value + * @param encryptedNarrativeVoice - The encrypted narrative voice value + * @param encryptedPacing - The encrypted pacing value + * @param encryptedKeyMessages - The encrypted key messages value + * @param encryptedIntendedAudience - The encrypted intended audience value + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the operation was successful + * @throws Error if the guideline cannot be updated or inserted + */ + static updateGuideLine( + userId: string, + bookId: string, + encryptedTone: string, + encryptedAtmosphere: string, + encryptedWritingStyle: string, + encryptedThemes: string, + encryptedSymbolism: string, + encryptedMotifs: string, + encryptedNarrativeVoice: string, + encryptedPacing: string, + encryptedKeyMessages: string, + encryptedIntendedAudience: string, + lang: 'fr' | 'en' + ): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_guide_line SET tone=?, atmosphere=?, writing_style=?, themes=?, symbolism=?, motifs=?, narrative_voice=?, pacing=?, key_messages=?, last_update=? WHERE user_id=? AND book_id=?', [encryptedTone, encryptedAtmosphere, encryptedWritingStyle, encryptedThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedKeyMessages, System.timeStampInSeconds(), userId, bookId]); - if (result.changes > 0) { + const updateQuery: string = 'UPDATE book_guide_line SET tone=?, atmosphere=?, writing_style=?, themes=?, symbolism=?, motifs=?, narrative_voice=?, pacing=?, key_messages=?, last_update=? WHERE user_id=? AND book_id=?'; + const updateParams: SQLiteValue[] = [ + encryptedTone, + encryptedAtmosphere, + encryptedWritingStyle, + encryptedThemes, + encryptedSymbolism, + encryptedMotifs, + encryptedNarrativeVoice, + encryptedPacing, + encryptedKeyMessages, + System.timeStampInSeconds(), + userId, + bookId + ]; + const updateResult: RunResult = db.run(updateQuery, updateParams); + + if (updateResult.changes > 0) { return true; } else { - const insert:RunResult = db.run('INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)', [userId, bookId, encryptedTone, encryptedAtmosphere, encryptedWritingStyle, encryptedThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedIntendedAudience, encryptedKeyMessages, System.timeStampInSeconds()]); - return insert.changes > 0; + const insertQuery: string = 'INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)'; + const insertParams: SQLiteValue[] = [ + userId, + bookId, + encryptedTone, + encryptedAtmosphere, + encryptedWritingStyle, + encryptedThemes, + encryptedSymbolism, + encryptedMotifs, + encryptedNarrativeVoice, + encryptedPacing, + encryptedIntendedAudience, + encryptedKeyMessages, + System.timeStampInSeconds() + ]; + const insertResult: RunResult = db.run(insertQuery, insertParams); + return insertResult.changes > 0; } - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour la ligne directrice.` : `Unable to update guideline.`); } else { console.error("An unknown error occurred."); @@ -108,19 +183,76 @@ export default class GuidelineRepo { } } } - static insertAIGuideLine(userId: string, bookId: string, narrativeType: number, dialogueType: number, encryptedPlotSummary: string, encryptedToneAtmosphere: string, verbTense: number, language: number, encryptedThemes: string, lang: 'fr' | 'en'): boolean { + + /** + * Inserts or updates an AI guideline for a specific book. + * If the AI guideline exists, it updates it; otherwise, it inserts a new one. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param narrativeType - The narrative type identifier + * @param dialogueType - The dialogue type identifier + * @param encryptedPlotSummary - The encrypted plot summary + * @param encryptedToneAtmosphere - The encrypted tone and atmosphere value + * @param verbTense - The verb tense identifier + * @param language - The language identifier + * @param encryptedThemes - The encrypted themes value + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the operation was successful + * @throws Error if the AI guideline cannot be inserted or updated + */ + static insertAIGuideLine( + userId: string, + bookId: string, + narrativeType: number, + dialogueType: number, + encryptedPlotSummary: string, + encryptedToneAtmosphere: string, + verbTense: number, + language: number, + encryptedThemes: string, + lang: 'fr' | 'en' + ): boolean { try { const db: Database = System.getDb(); - let result: RunResult = db.run('UPDATE book_ai_guide_line SET narrative_type=?, dialogue_type=?, global_resume=?, atmosphere=?, verbe_tense=?, langue=?, themes=?, last_update=? WHERE user_id=? AND book_id=?', [narrativeType ? narrativeType : null, dialogueType ? dialogueType : null, encryptedPlotSummary, encryptedToneAtmosphere, verbTense ? verbTense : null, language ? language : null, encryptedThemes, System.timeStampInSeconds(), userId, bookId]); - if (result.changes > 0) { + const updateQuery: string = 'UPDATE book_ai_guide_line SET narrative_type=?, dialogue_type=?, global_resume=?, atmosphere=?, verbe_tense=?, langue=?, themes=?, last_update=? WHERE user_id=? AND book_id=?'; + const updateParams: SQLiteValue[] = [ + narrativeType ? narrativeType : null, + dialogueType ? dialogueType : null, + encryptedPlotSummary, + encryptedToneAtmosphere, + verbTense ? verbTense : null, + language ? language : null, + encryptedThemes, + System.timeStampInSeconds(), + userId, + bookId + ]; + const updateResult: RunResult = db.run(updateQuery, updateParams); + + if (updateResult.changes > 0) { return true; } else { - result = db.run('INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [userId, bookId, encryptedPlotSummary, encryptedThemes, verbTense ? verbTense : null, narrativeType ? narrativeType : null, language ? language : null, dialogueType ? dialogueType : null, encryptedToneAtmosphere, encryptedToneAtmosphere, encryptedPlotSummary, System.timeStampInSeconds()]); - return result.changes > 0; + const insertQuery: string = 'INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'; + const insertParams: SQLiteValue[] = [ + userId, + bookId, + encryptedPlotSummary, + encryptedThemes, + verbTense ? verbTense : null, + narrativeType ? narrativeType : null, + language ? language : null, + dialogueType ? dialogueType : null, + encryptedToneAtmosphere, + encryptedToneAtmosphere, + encryptedPlotSummary, + System.timeStampInSeconds() + ]; + const insertResult: RunResult = db.run(insertQuery, insertParams); + return insertResult.changes > 0; } - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice IA.` : `Unable to insert AI guideline.`); } else { console.error("An unknown error occurred."); @@ -128,59 +260,104 @@ export default class GuidelineRepo { } } } - + + /** + * Fetches the AI guideline for a specific book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns The AI guideline query result + * @throws Error if the AI guideline cannot be retrieved or is not found + */ static fetchGuideLineAI(userId: string, bookId: string, lang: 'fr' | 'en'): GuideLineAIQuery { - let result: GuideLineAIQuery | null; + let aiGuideline: GuideLineAIQuery | null; try { const db: Database = System.getDb(); - result = db.get('SELECT narrative_type, dialogue_type, global_resume, atmosphere, verbe_tense, langue, themes, current_resume FROM book_ai_guide_line WHERE user_id=? AND book_id=?', [userId, bookId]) as GuideLineAIQuery | null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT narrative_type, dialogue_type, global_resume, atmosphere, verbe_tense, langue, themes, current_resume FROM book_ai_guide_line WHERE user_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + aiGuideline = db.get(query, params) as GuideLineAIQuery | null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice IA.` : `Unable to retrieve AI guideline.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result) { + if (!aiGuideline) { throw new Error(lang === 'fr' ? `Ligne directrice IA non trouvée.` : `AI guideline not found.`); } - return result; + return aiGuideline; } + + /** + * Fetches the book AI guideline table data for a specific book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book AI guideline table entries + * @throws Error if the AI guideline cannot be retrieved + */ static async fetchBookAIGuideLine(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update FROM book_ai_guide_line WHERE user_id=? AND book_id=?', [userId, bookId]) as BookAIGuideLineTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update FROM book_ai_guide_line WHERE user_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const aiGuidelines: BookAIGuideLineTable[] = db.all(query, params) as BookAIGuideLineTable[]; + return aiGuidelines; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice IA.` : `Unable to retrieve AI guideline.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Fetches the book guideline table data for a specific book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book guideline table entries + * @throws Error if the guideline cannot be retrieved + */ static async fetchBookGuideLineTable(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update FROM book_guide_line WHERE user_id=? AND book_id=?', [userId, bookId]) as BookGuideLineTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update FROM book_guide_line WHERE user_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const guidelines: BookGuideLineTable[] = db.all(query, params) as BookGuideLineTable[]; + return guidelines; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice.` : `Unable to retrieve guideline.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Fetches all synced guidelines for a specific user. + * @param userId - The user identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced guideline results containing book_id and last_update + * @throws Error if the synced guidelines cannot be retrieved + */ static fetchSyncedGuideLine(userId: string, lang: 'fr' | 'en'): SyncedGuideLineResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT book_id, last_update FROM book_guide_line WHERE user_id = ?', [userId]) as SyncedGuideLineResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT book_id, last_update FROM book_guide_line WHERE user_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedGuidelines: SyncedGuideLineResult[] = db.all(query, params) as SyncedGuideLineResult[]; + return syncedGuidelines; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lignes directrices synchronisées.` : `Unable to retrieve synced guidelines.`); } else { console.error("An unknown error occurred."); @@ -188,14 +365,24 @@ export default class GuidelineRepo { } } } - + + /** + * Fetches all synced AI guidelines for a specific user. + * @param userId - The user identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced AI guideline results containing book_id and last_update + * @throws Error if the synced AI guidelines cannot be retrieved + */ static fetchSyncedAIGuideLine(userId: string, lang: 'fr' | 'en'): SyncedAIGuideLineResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT book_id, last_update FROM book_ai_guide_line WHERE user_id = ?', [userId]) as SyncedAIGuideLineResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT book_id, last_update FROM book_ai_guide_line WHERE user_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedAIGuidelines: SyncedAIGuideLineResult[] = db.all(query, params) as SyncedAIGuideLineResult[]; + return syncedAIGuidelines; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lignes directrices IA synchronisées.` : `Unable to retrieve synced AI guidelines.`); } else { console.error("An unknown error occurred."); @@ -203,40 +390,133 @@ export default class GuidelineRepo { } } } - static insertSyncAIGuideLine(userId: string, bookId: string, globalResume: string | null, themes: string | null, verbeTense: number | null, narrativeType: number | null, langue: number | null, dialogueType: number | null, tone: string | null, atmosphere: string | null, currentResume: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { + + /** + * Inserts a synced AI guideline for a specific book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param globalResume - The global resume value (nullable) + * @param themes - The themes value (nullable) + * @param verbeTense - The verb tense identifier (nullable) + * @param narrativeType - The narrative type identifier (nullable) + * @param langue - The language identifier (nullable) + * @param dialogueType - The dialogue type identifier (nullable) + * @param tone - The tone value (nullable) + * @param atmosphere - The atmosphere value (nullable) + * @param currentResume - The current resume value (nullable) + * @param lastUpdate - The last update timestamp + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the insertion was successful + * @throws Error if the AI guideline cannot be inserted + */ + static insertSyncAIGuideLine( + userId: string, + bookId: string, + globalResume: string | null, + themes: string | null, + verbeTense: number | null, + narrativeType: number | null, + langue: number | null, + dialogueType: number | null, + tone: string | null, + atmosphere: string | null, + currentResume: string | null, + lastUpdate: number, + lang: 'fr' | 'en' + ): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [userId, bookId, globalResume, themes, verbeTense, narrativeType, langue, dialogueType, tone, atmosphere, currentResume, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + const params: SQLiteValue[] = [ + userId, + bookId, + globalResume, + themes, + verbeTense, + narrativeType, + langue, + dialogueType, + tone, + atmosphere, + currentResume, + lastUpdate + ]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice IA.` : `Unable to insert AI guideline.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static insertSyncGuideLine(userId: string, 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, intendedAudience: string | null, keyMessages: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { + + /** + * Inserts a synced guideline for a specific book. + * @param userId - The user identifier + * @param bookId - The book identifier + * @param tone - The tone value (nullable) + * @param atmosphere - The atmosphere value (nullable) + * @param writingStyle - The writing style value (nullable) + * @param themes - The themes value (nullable) + * @param symbolism - The symbolism value (nullable) + * @param motifs - The motifs value (nullable) + * @param narrativeVoice - The narrative voice value (nullable) + * @param pacing - The pacing value (nullable) + * @param intendedAudience - The intended audience value (nullable) + * @param keyMessages - The key messages value (nullable) + * @param lastUpdate - The last update timestamp + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the insertion was successful + * @throws Error if the guideline cannot be inserted + */ + static insertSyncGuideLine( + userId: string, + 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, + intendedAudience: string | null, + keyMessages: string | null, + lastUpdate: number, + lang: 'fr' | 'en' + ): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [userId, bookId, tone, atmosphere, writingStyle, themes, symbolism, motifs, narrativeVoice, pacing, intendedAudience, keyMessages, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + const params: SQLiteValue[] = [ + userId, + bookId, + tone, + atmosphere, + writingStyle, + themes, + symbolism, + motifs, + narrativeVoice, + pacing, + intendedAudience, + keyMessages, + lastUpdate + ]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice.` : `Unable to insert guideline.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } -} \ No newline at end of file +} diff --git a/electron/database/repositories/incident.repository.ts b/electron/database/repositories/incident.repository.ts index 595b0cc..7a93baa 100644 --- a/electron/database/repositories/incident.repository.ts +++ b/electron/database/repositories/incident.repository.ts @@ -1,4 +1,4 @@ -import {Database, QueryResult, RunResult, SQLiteValue} from "node-sqlite3-wasm"; +import { Database, QueryResult, RunResult, SQLiteValue } from "node-sqlite3-wasm"; import System from "@/electron/database/System"; export interface BookIncidentsTable extends Record { @@ -25,13 +25,24 @@ export interface IncidentQuery extends Record { } export default class IncidentRepository { - public static fetchAllIncitentIncidents(userId:string,bookId:string, lang: 'fr' | 'en'):IncidentQuery[]{ + /** + * Fetches all incidents for a specific book belonging to a user. + * @param userId - The ID of the user (author) + * @param bookId - The ID of the book + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of incidents with their ID, title, and summary + * @throws Error if the database query fails + */ + public static fetchAllIncidents(userId: string, bookId: string, lang: 'fr' | 'en'): IncidentQuery[] { try { const db: Database = System.getDb(); - return db.all('SELECT incident_id, title, summary FROM book_incidents WHERE author_id=? AND book_id=?', [userId, bookId]) as IncidentQuery[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT incident_id, title, summary FROM book_incidents WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const incidents: IncidentQuery[] = db.all(query, params) as IncidentQuery[]; + return incidents; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les incidents.` : `Unable to retrieve incidents.`); } else { console.error("An unknown error occurred."); @@ -39,35 +50,59 @@ export default class IncidentRepository { } } } - + + /** + * Inserts a new incident into the database. + * @param incidentId - The unique ID for the new incident + * @param userId - The ID of the user (author) + * @param bookId - The ID of the book + * @param encryptedName - The encrypted title of the incident + * @param hashedName - The hashed title of the incident + * @param lang - The language for error messages ('fr' or 'en') + * @returns The incident ID if insertion was successful + * @throws Error if the database insertion fails + */ public static insertNewIncident(incidentId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string { - let result: RunResult; + let insertResult: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO book_incidents (incident_id,author_id, book_id, title, hashed_title, last_update) VALUES (?,?,?,?,?,?)', [incidentId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'INSERT INTO book_incidents (incident_id,author_id, book_id, title, hashed_title, last_update) VALUES (?,?,?,?,?,?)'; + const params: SQLiteValue[] = [incidentId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()]; + insertResult = db.run(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter l'élément déclencheur.` : `Unable to add incident.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result || result.changes === 0) { + if (!insertResult || insertResult.changes === 0) { throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'élément déclencheur.` : `Error adding incident.`); } return incidentId; } - + + /** + * Deletes an incident from the database. + * @param userId - The ID of the user (author) + * @param bookId - The ID of the book + * @param incidentId - The ID of the incident to delete + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the incident was deleted, false otherwise + * @throws Error if the database deletion fails + */ public static deleteIncident(userId: string, bookId: string, incidentId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM book_incidents WHERE author_id=? AND book_id=? AND incident_id=?', [userId, bookId, incidentId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'DELETE FROM book_incidents WHERE author_id=? AND book_id=? AND incident_id=?'; + const params: SQLiteValue[] = [userId, bookId, incidentId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de supprimer l'élément déclencheur.` : `Unable to delete incident.`); } else { console.error("An unknown error occurred."); @@ -75,14 +110,30 @@ export default class IncidentRepository { } } } + + /** + * Updates an existing incident in the database. + * @param userId - The ID of the user (author) + * @param bookId - The ID of the book + * @param incidentId - The ID of the incident to update + * @param encryptedIncidentName - The new encrypted title + * @param incidentHashedName - The new hashed title + * @param incidentSummary - The new summary + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the incident was updated, false otherwise + * @throws Error if the database update fails + */ public static updateIncident(userId: string, bookId: string, incidentId: string, encryptedIncidentName: string, incidentHashedName: string, incidentSummary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_incidents SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND incident_id=?', [encryptedIncidentName, incidentHashedName, incidentSummary, lastUpdate, userId, bookId, incidentId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'UPDATE book_incidents SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND incident_id=?'; + const params: SQLiteValue[] = [encryptedIncidentName, incidentHashedName, incidentSummary, lastUpdate, userId, bookId, incidentId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'incident.` : `Unable to update incident.`); } else { console.error("An unknown error occurred."); @@ -90,26 +141,49 @@ export default class IncidentRepository { } } } + + /** + * Fetches all incidents for a book with complete information. + * @param userId - The ID of the user (author) + * @param bookId - The ID of the book + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of complete incident records + * @throws Error if the database query fails + */ static async fetchBookIncidents(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update FROM book_incidents WHERE author_id=? AND book_id=?', [userId, bookId]) as BookIncidentsTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update FROM book_incidents WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const incidents: BookIncidentsTable[] = db.all(query, params) as BookIncidentsTable[]; + return incidents; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les incidents.` : `Unable to retrieve incidents.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Fetches all synced incidents for a user across all books. + * @param userId - The ID of the user (author) + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced incident records with minimal information + * @throws Error if the database query fails + */ static fetchSyncedIncidents(userId: string, lang: 'fr' | 'en'): SyncedIncidentResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT incident_id, book_id, title, last_update FROM book_incidents WHERE author_id = ?', [userId]) as SyncedIncidentResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT incident_id, book_id, title, last_update FROM book_incidents WHERE author_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedIncidents: SyncedIncidentResult[] = db.all(query, params) as SyncedIncidentResult[]; + return syncedIncidents; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les incidents synchronisés.` : `Unable to retrieve synced incidents.`); } else { console.error("An unknown error occurred."); @@ -117,53 +191,86 @@ export default class IncidentRepository { } } } + + /** + * Inserts a synced incident into the database. + * @param incidentId - The unique ID for the incident + * @param authorId - The ID of the author + * @param bookId - The ID of the book + * @param title - The encrypted title + * @param hashedTitle - The hashed title + * @param summary - The encrypted summary (can be null) + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the incident was inserted, false otherwise + * @throws Error if the database insertion fails + */ static insertSyncIncident(incidentId: string, authorId: string, bookId: string, title: string, hashedTitle: string, summary: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_incidents (incident_id, author_id, book_id, title, hashed_title, summary, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?)`, - [incidentId, authorId, bookId, title, hashedTitle, summary, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `INSERT INTO book_incidents (incident_id, author_id, book_id, title, hashed_title, summary, last_update) + VALUES (?, ?, ?, ?, ?, ?, ?)`; + const params: SQLiteValue[] = [incidentId, authorId, bookId, title, hashedTitle, summary, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer l'incident.` : `Unable to insert incident.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static async fetchCompleteIncidentById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches complete incident information by its ID. + * @param id - The ID of the incident to fetch + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array containing the incident record (empty if not found) + * @throws Error if the database query fails + */ + static async fetchCompleteIncidentById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update + const query: string = `SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update FROM book_incidents - WHERE incident_id = ?`, - [id] - ) as BookIncidentsTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + WHERE incident_id = ?`; + const params: SQLiteValue[] = [id]; + const incident: BookIncidentsTable[] = db.all(query, params) as BookIncidentsTable[]; + return incident; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer l'incident complet.` : `Unable to retrieve complete incident.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static incidentExist(userId: string, bookId: string, incident_id: string,lang: "fr" | "en"): boolean { + + /** + * Checks if an incident exists in the database. + * @param userId - The ID of the user (author) + * @param bookId - The ID of the book + * @param incidentId - The ID of the incident to check + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the incident exists, false otherwise + * @throws Error if the database query fails + */ + static incidentExist(userId: string, bookId: string, incidentId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM book_incidents WHERE book_id=? AND incident_id=? AND author_id=?', [bookId, incident_id, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM book_incidents WHERE book_id=? AND incident_id=? AND author_id=?'; + const params: SQLiteValue[] = [bookId, incidentId, userId]; + const existingIncident: QueryResult | null = db.get(query, params) || null; + return existingIncident !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'incident.` : `Unable to check incident existence.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } -} \ No newline at end of file +} diff --git a/electron/database/repositories/issue.repository.ts b/electron/database/repositories/issue.repository.ts index 5bd2b75..04a76e8 100644 --- a/electron/database/repositories/issue.repository.ts +++ b/electron/database/repositories/issue.repository.ts @@ -1,4 +1,4 @@ -import {Database, QueryResult, RunResult, SQLiteValue} from "node-sqlite3-wasm"; +import { Database, QueryResult, RunResult, SQLiteValue } from "node-sqlite3-wasm"; import System from "@/electron/database/System"; export interface BookIssuesTable extends Record { @@ -23,13 +23,23 @@ export interface IssueQuery extends Record { } export default class IssueRepository { - public static fetchIssuesFromBook(userId:string,bookId:string, lang: 'fr' | 'en'):IssueQuery[]{ + /** + * Fetches all issues associated with a specific book. + * @param userId - The unique identifier of the user/author. + * @param bookId - The unique identifier of the book. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns An array of issues with their IDs and names. + */ + public static fetchIssuesFromBook(userId: string, bookId: string, lang: 'fr' | 'en'): IssueQuery[] { try { const db: Database = System.getDb(); - return db.all('SELECT issue_id, name FROM book_issues WHERE author_id=? AND book_id=?', [userId, bookId]) as IssueQuery[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT issue_id, name FROM book_issues WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const issues: IssueQuery[] = db.all(query, params) as IssueQuery[]; + return issues; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les problématiques.` : `Unable to retrieve issues.`); } else { console.error("An unknown error occurred."); @@ -37,30 +47,45 @@ export default class IssueRepository { } } } + + /** + * Inserts a new issue into the database after verifying it doesn't already exist. + * @param issueId - The unique identifier for the new issue. + * @param userId - The unique identifier of the user/author. + * @param bookId - The unique identifier of the book. + * @param encryptedName - The encrypted name of the issue. + * @param hashedName - The hashed name of the issue for duplicate checking. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns The issue ID if successfully inserted. + */ public static insertNewIssue(issueId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string { - let existingResult: QueryResult | null; + let existingIssue: QueryResult | null; let insertResult: RunResult; try { const db: Database = System.getDb(); - existingResult = db.get('SELECT issue_id FROM book_issues WHERE hashed_issue_name=? AND book_id=? AND author_id=?', [hashedName, bookId, userId]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const checkQuery: string = 'SELECT issue_id FROM book_issues WHERE hashed_issue_name=? AND book_id=? AND author_id=?'; + const checkParams: SQLiteValue[] = [hashedName, bookId, userId]; + existingIssue = db.get(checkQuery, checkParams); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de la problématique.` : `Unable to verify issue existence.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (existingResult !== null) { + if (existingIssue !== null) { throw new Error(lang === 'fr' ? `La problématique existe déjà.` : `This issue already exists.`); } try { const db: Database = System.getDb(); - insertResult = db.run('INSERT INTO book_issues (issue_id,author_id, book_id, name, hashed_issue_name, last_update) VALUES (?,?,?,?,?,?)', [issueId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const insertQuery: string = 'INSERT INTO book_issues (issue_id, author_id, book_id, name, hashed_issue_name, last_update) VALUES (?, ?, ?, ?, ?, ?)'; + const insertParams: SQLiteValue[] = [issueId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()]; + insertResult = db.run(insertQuery, insertParams); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter la problématique.` : `Unable to add issue.`); } else { console.error("An unknown error occurred."); @@ -72,15 +97,24 @@ export default class IssueRepository { } return issueId; } - + + /** + * Deletes an issue from the database. + * @param userId - The unique identifier of the user/author. + * @param issueId - The unique identifier of the issue to delete. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the issue was successfully deleted, false otherwise. + */ public static deleteIssue(userId: string, issueId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM book_issues WHERE author_id=? AND issue_id=?', [userId, issueId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'DELETE FROM book_issues WHERE author_id=? AND issue_id=?'; + const params: SQLiteValue[] = [userId, issueId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de supprimer la problématique.` : `Unable to delete issue.`); } else { console.error("An unknown error occurred."); @@ -88,26 +122,47 @@ export default class IssueRepository { } } } + + /** + * Fetches all complete issue records for a specific book. + * @param userId - The unique identifier of the user/author. + * @param bookId - The unique identifier of the book. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns A promise resolving to an array of complete issue records. + */ static async fetchBookIssues(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update FROM book_issues WHERE author_id=? AND book_id=?', [userId, bookId]) as BookIssuesTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update FROM book_issues WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const issues: BookIssuesTable[] = db.all(query, params) as BookIssuesTable[]; + return issues; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les problématiques.` : `Unable to retrieve issues.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Fetches all synced issues for a specific user. + * @param userId - The unique identifier of the user/author. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns An array of synced issue records. + */ static fetchSyncedIssues(userId: string, lang: 'fr' | 'en'): SyncedIssueResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT issue_id, book_id, name, last_update FROM book_issues WHERE author_id = ?', [userId]) as SyncedIssueResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT issue_id, book_id, name, last_update FROM book_issues WHERE author_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedIssues: SyncedIssueResult[] = db.all(query, params) as SyncedIssueResult[]; + return syncedIssues; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les problématiques synchronisées.` : `Unable to retrieve synced issues.`); } else { console.error("An unknown error occurred."); @@ -115,52 +170,78 @@ export default class IssueRepository { } } } - + + /** + * Inserts a synced issue from remote into the local database. + * @param issueId - The unique identifier of the issue. + * @param authorId - The unique identifier of the author. + * @param bookId - The unique identifier of the book. + * @param name - The encrypted name of the issue. + * @param hashedIssueName - The hashed name of the issue. + * @param lastUpdate - The timestamp of the last update. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the issue was successfully inserted, false otherwise. + */ static insertSyncIssue(issueId: string, authorId: string, bookId: string, name: string, hashedIssueName: string, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_issues (issue_id, author_id, book_id, name, hashed_issue_name, last_update) - VALUES (?, ?, ?, ?, ?, ?)`, - [issueId, authorId, bookId, name, hashedIssueName, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `INSERT INTO book_issues (issue_id, author_id, book_id, name, hashed_issue_name, last_update) VALUES (?, ?, ?, ?, ?, ?)`; + const params: SQLiteValue[] = [issueId, authorId, bookId, name, hashedIssueName, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer la problématique.` : `Unable to insert issue.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static async fetchCompleteIssueById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches a complete issue record by its ID. + * @param id - The unique identifier of the issue. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns A promise resolving to an array of complete issue records. + */ + static async fetchCompleteIssueById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update - FROM book_issues - WHERE issue_id = ?`, - [id] - ) as BookIssuesTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + const query: string = `SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update FROM book_issues WHERE issue_id = ?`; + const params: SQLiteValue[] = [id]; + const issues: BookIssuesTable[] = db.all(query, params) as BookIssuesTable[]; + return issues; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le problème complet.` : `Unable to retrieve complete issue.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static updateIssue(userId: string, bookId: string, issueId: string, name: string, hashedName: string, lastUpdate: number, lang: "fr" | "en"):boolean { + + /** + * Updates an existing issue in the database. + * @param userId - The unique identifier of the user/author. + * @param bookId - The unique identifier of the book. + * @param issueId - The unique identifier of the issue to update. + * @param name - The new encrypted name of the issue. + * @param hashedName - The new hashed name of the issue. + * @param lastUpdate - The timestamp of the update. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the issue was successfully updated, false otherwise. + */ + static updateIssue(userId: string, bookId: string, issueId: string, name: string, hashedName: string, lastUpdate: number, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const query:string = `UPDATE book_issues SET name = ?, hashed_issue_name = ?, last_update = FROM_UNIXTIME(?) WHERE issue_id = UUID_TO_BIN(?) AND author_id = UUID_TO_BIN(?) AND book_id = UUID_TO_BIN(?)`; - const params:(string|number)[] = [name, hashedName, lastUpdate, issueId, userId, bookId]; - const result:RunResult = db.run(query, params); - return result.changes > 0; - } catch (e:unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `UPDATE book_issues SET name = ?, hashed_issue_name = ?, last_update = ? WHERE issue_id = ? AND author_id = ? AND book_id = ?`; + const params: SQLiteValue[] = [name, hashedName, lastUpdate, issueId, userId, bookId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour la problématique.` : `Unable to update issue.`); } else { console.error("An unknown error occurred."); @@ -168,18 +249,29 @@ export default class IssueRepository { } } } - static issueExist(userId: string, bookId: string, issue_id: string,lang: "fr" | "en"): boolean { + + /** + * Checks if an issue exists in the database. + * @param userId - The unique identifier of the user/author. + * @param bookId - The unique identifier of the book. + * @param issueId - The unique identifier of the issue to check. + * @param lang - The language for error messages ('fr' or 'en'). + * @returns True if the issue exists, false otherwise. + */ + static issueExist(userId: string, bookId: string, issueId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `book_issues` WHERE `issue_id`=? AND `author_id`=? AND `book_id`=?', [issue_id, userId, bookId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM book_issues WHERE issue_id=? AND author_id=? AND book_id=?'; + const params: SQLiteValue[] = [issueId, userId, bookId]; + const existingIssue: QueryResult | null = db.get(query, params) || null; + return existingIssue !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du problème.` : `Unable to check issue existence.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } -} \ No newline at end of file +} diff --git a/electron/database/repositories/location.repository.ts b/electron/database/repositories/location.repository.ts index ccd69ff..33a9d2b 100644 --- a/electron/database/repositories/location.repository.ts +++ b/electron/database/repositories/location.repository.ts @@ -1,4 +1,4 @@ -import {Database, QueryResult, RunResult, SQLiteValue} from 'node-sqlite3-wasm'; +import { Database, QueryResult, RunResult, SQLiteValue } from 'node-sqlite3-wasm'; import System from "../System.js"; export interface LocationQueryResult extends Record { @@ -79,92 +79,166 @@ export interface SyncedLocationSubElementResult extends Record 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + UPDATE location_sub_element + SET sub_elem_name = ?, original_name = ?, sub_elem_description = ?, last_update = ? + WHERE sub_element_id = ? AND user_id = ? + `; + const params: SQLiteValue[] = [encryptedName, originalName, encryptDescription, lastUpdate, id, userId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le sous-élément d'emplacement.` : `Unable to update location sub-element.`); } else { console.error("An unknown error occurred."); @@ -173,14 +247,31 @@ export default class LocationRepo { } } + /** + * Updates an existing location element's name and description. + * @param userId - The user's unique identifier + * @param id - The element's unique identifier + * @param encryptedName - The new encrypted element name + * @param originalName - The new original (unencrypted) element name + * @param encryptedDescription - The new encrypted description + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the update affected at least one row + */ static updateLocationElement(userId: string, id: string, encryptedName: string, originalName: string, encryptedDescription: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE location_element SET element_name=?, original_name=?, element_description=?, last_update=? WHERE element_id=? AND user_id=?', [encryptedName, originalName, encryptedDescription, lastUpdate, id, userId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + UPDATE location_element + SET element_name = ?, original_name = ?, element_description = ?, last_update = ? + WHERE element_id = ? AND user_id = ? + `; + const params: SQLiteValue[] = [encryptedName, originalName, encryptedDescription, lastUpdate, id, userId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'élément d'emplacement.` : `Unable to update location element.`); } else { console.error("An unknown error occurred."); @@ -189,14 +280,30 @@ export default class LocationRepo { } } + /** + * Updates an existing location section's name. + * @param userId - The user's unique identifier + * @param id - The location section's unique identifier + * @param encryptedName - The new encrypted location name + * @param originalName - The new original (unencrypted) location name + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the update affected at least one row + */ static updateLocationSection(userId: string, id: string, encryptedName: string, originalName: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_location SET loc_name=?, loc_original_name=?, last_update=? WHERE loc_id=? AND user_id=?', [encryptedName, originalName, lastUpdate, id, userId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + UPDATE book_location + SET loc_name = ?, loc_original_name = ?, last_update = ? + WHERE loc_id = ? AND user_id = ? + `; + const params: SQLiteValue[] = [encryptedName, originalName, lastUpdate, id, userId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour la section d'emplacement.` : `Unable to update location section.`); } else { console.error("An unknown error occurred."); @@ -205,14 +312,23 @@ export default class LocationRepo { } } + /** + * Deletes a location section by its ID. + * @param userId - The user's unique identifier + * @param locationId - The location section's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the deletion affected at least one row + */ static deleteLocationSection(userId: string, locationId: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM book_location WHERE loc_id=? AND user_id=?', [locationId, userId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'DELETE FROM book_location WHERE loc_id = ? AND user_id = ?'; + const params: SQLiteValue[] = [locationId, userId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de supprimer la section d'emplacement.` : `Unable to delete location section.`); } else { console.error("An unknown error occurred."); @@ -221,14 +337,23 @@ export default class LocationRepo { } } + /** + * Deletes a location element by its ID. + * @param userId - The user's unique identifier + * @param elementId - The element's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the deletion affected at least one row + */ static deleteLocationElement(userId: string, elementId: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM location_element WHERE element_id=? AND user_id=?', [elementId, userId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'DELETE FROM location_element WHERE element_id = ? AND user_id = ?'; + const params: SQLiteValue[] = [elementId, userId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de supprimer l'élément d'emplacement.` : `Unable to delete location element.`); } else { console.error("An unknown error occurred."); @@ -237,14 +362,23 @@ export default class LocationRepo { } } + /** + * Deletes a location sub-element by its ID. + * @param userId - The user's unique identifier + * @param subElementId - The sub-element's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the deletion affected at least one row + */ static deleteLocationSubElement(userId: string, subElementId: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM location_sub_element WHERE sub_element_id=? AND user_id=?', [subElementId, userId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'DELETE FROM location_sub_element WHERE sub_element_id = ? AND user_id = ?'; + const params: SQLiteValue[] = [subElementId, userId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de supprimer le sous-élément d'emplacement.` : `Unable to delete location sub-element.`); } else { console.error("An unknown error occurred."); @@ -253,66 +387,97 @@ export default class LocationRepo { } } + /** + * Fetches all location elements and sub-elements for tagging purposes. + * @param userId - The user's unique identifier + * @param bookId - The book's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of location elements with their sub-elements + */ static fetchLocationTags(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationElementQueryResult[] { - let result: LocationElementQueryResult[]; try { const db: Database = System.getDb(); - const query = 'SELECT se.sub_element_id AS sub_element_id, se.sub_elem_name, se.sub_elem_description, el.element_id AS element_id, el.element_name, el.element_description FROM location_sub_element AS se RIGHT JOIN location_element AS el ON se.element_id = el.element_id LEFT JOIN book_location AS lo ON el.location = lo.loc_id WHERE lo.book_id = ? AND lo.user_id = ?'; - result = db.all(query, [bookId, userId]) as LocationElementQueryResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + SELECT se.sub_element_id AS sub_element_id, se.sub_elem_name, se.sub_elem_description, + el.element_id AS element_id, el.element_name, el.element_description + FROM location_sub_element AS se + RIGHT JOIN location_element AS el ON se.element_id = el.element_id + LEFT JOIN book_location AS lo ON el.location = lo.loc_id + WHERE lo.book_id = ? AND lo.user_id = ? + `; + const params: SQLiteValue[] = [bookId, userId]; + const locationTags: LocationElementQueryResult[] = db.all(query, params) as LocationElementQueryResult[]; + return locationTags; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les tags d'emplacement.` : `Unable to retrieve location tags.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - return result; } + /** + * Fetches locations by their tag IDs (element or sub-element IDs). + * @param userId - The user's unique identifier + * @param locations - An array of location tag IDs to search for + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of locations matching the provided tags + * @throws Error if no tags are provided or no locations are found + */ static fetchLocationsByTags(userId: string, locations: string[], lang: 'fr' | 'en' = 'fr'): LocationByTagResult[] { if (locations.length === 0) { throw new Error(lang === 'fr' ? `Aucun tag fourni.` : `No tags provided.`); } - let result: LocationByTagResult[]; try { const db: Database = System.getDb(); - const locationIds: string = locations.map((): string => '?').join(','); + const locationPlaceholders: string = locations.map((): string => '?').join(','); const query: string = ` SELECT el.element_name, el.element_description, se.sub_elem_name, se.sub_elem_description FROM location_element AS el - LEFT JOIN location_sub_element AS se ON el.element_id = se.element_id + LEFT JOIN location_sub_element AS se ON el.element_id = se.element_id WHERE el.user_id = ? - AND (el.element_id IN (${locationIds}) OR se.sub_element_id IN (${locationIds})) + AND (el.element_id IN (${locationPlaceholders}) OR se.sub_element_id IN (${locationPlaceholders})) `; - const values: any[] = [userId, ...locations, ...locations]; - result = db.all(query, values) as LocationByTagResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const params: SQLiteValue[] = [userId, ...locations, ...locations]; + const locationsByTags: LocationByTagResult[] = db.all(query, params) as LocationByTagResult[]; + if (locationsByTags.length === 0) { + throw new Error(lang === 'fr' ? `Aucun emplacement trouvé avec ces tags.` : `No locations found with these tags.`); + } + return locationsByTags; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les emplacements par tags.` : `Unable to retrieve locations by tags.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (result.length === 0) { - throw new Error(lang === 'fr' ? `Aucun emplacement trouvé avec ces tags.` : `No locations found with these tags.`); - } - return result; } - static isLocationExist(userId: string, locId: string,lang: "fr" | "en"): boolean { + + /** + * Checks if a location exists in the database. + * @param userId - The user's unique identifier + * @param locId - The location's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the location exists, false otherwise + */ + static isLocationExist(userId: string, locId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `book_location` WHERE `loc_id`=? AND `user_id`=?', [locId, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM `book_location` WHERE `loc_id` = ? AND `user_id` = ?'; + const params: SQLiteValue[] = [locId, userId]; + const existingLocation: QueryResult | null = db.get(query, params) || null; + return existingLocation !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'emplacement.` : `Unable to check location existence.`); } else { console.error("An unknown error occurred."); @@ -320,15 +485,24 @@ export default class LocationRepo { } } } - - static isLocationElementExist(userId: string, elementId: string,lang: "fr" | "en"): boolean { + + /** + * Checks if a location element exists in the database. + * @param userId - The user's unique identifier + * @param elementId - The element's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the location element exists, false otherwise + */ + static isLocationElementExist(userId: string, elementId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `location_element` WHERE `element_id`=? AND `user_id`=?', [elementId, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM `location_element` WHERE `element_id` = ? AND `user_id` = ?'; + const params: SQLiteValue[] = [elementId, userId]; + const existingElement: QueryResult | null = db.get(query, params) || null; + return existingElement !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément d'emplacement.` : `Unable to check location element existence.`); } else { console.error("An unknown error occurred."); @@ -336,15 +510,24 @@ export default class LocationRepo { } } } - - static isLocationSubElementExist(userId: string, subElementId: string,lang: "fr" | "en"): boolean { + + /** + * Checks if a location sub-element exists in the database. + * @param userId - The user's unique identifier + * @param subElementId - The sub-element's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the location sub-element exists, false otherwise + */ + static isLocationSubElementExist(userId: string, subElementId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `location_sub_element` WHERE `sub_element_id`=? AND `user_id`=?', [subElementId, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM `location_sub_element` WHERE `sub_element_id` = ? AND `user_id` = ?'; + const params: SQLiteValue[] = [subElementId, userId]; + const existingSubElement: QueryResult | null = db.get(query, params) || null; + return existingSubElement !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du sous-élément d'emplacement.` : `Unable to check location sub-element existence.`); } else { console.error("An unknown error occurred."); @@ -352,53 +535,107 @@ export default class LocationRepo { } } } + + /** + * Fetches all locations for a specific book. + * @param userId - The user's unique identifier + * @param bookId - The book's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book location records + */ static async fetchBookLocations(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update FROM book_location WHERE user_id=? AND book_id=?', [userId, bookId]) as BookLocationTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update + FROM book_location + WHERE user_id = ? AND book_id = ? + `; + const params: SQLiteValue[] = [userId, bookId]; + const bookLocations: BookLocationTable[] = db.all(query, params) as BookLocationTable[]; + return bookLocations; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lieux.` : `Unable to retrieve locations.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static async fetchLocationElements(userId: string,locationId:string, lang: 'fr' | 'en'): Promise { + + /** + * Fetches all elements for a specific location. + * @param userId - The user's unique identifier + * @param locationId - The location's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of location element records + */ + static async fetchLocationElements(userId: string, locationId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT element_id, location, user_id, element_name, original_name, element_description, last_update FROM location_element WHERE user_id=? AND location=?', [userId, locationId]) as LocationElementTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + SELECT element_id, location, user_id, element_name, original_name, element_description, last_update + FROM location_element + WHERE user_id = ? AND location = ? + `; + const params: SQLiteValue[] = [userId, locationId]; + const locationElements: LocationElementTable[] = db.all(query, params) as LocationElementTable[]; + return locationElements; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de lieu.` : `Unable to retrieve location elements.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - - static async fetchLocationSubElements(userId: string,elementId:string, lang: 'fr' | 'en'): Promise { + + /** + * Fetches all sub-elements for a specific location element. + * @param userId - The user's unique identifier + * @param elementId - The element's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of location sub-element records + */ + static async fetchLocationSubElements(userId: string, elementId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update FROM location_sub_element WHERE user_id=? AND element_id=?', [userId, elementId]) as LocationSubElementTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update + FROM location_sub_element + WHERE user_id = ? AND element_id = ? + `; + const params: SQLiteValue[] = [userId, elementId]; + const locationSubElements: LocationSubElementTable[] = db.all(query, params) as LocationSubElementTable[]; + return locationSubElements; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les sous-éléments de lieu.` : `Unable to retrieve location sub-elements.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Fetches all synced locations for a user (used for synchronization). + * @param userId - The user's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced location records + */ static fetchSyncedLocations(userId: string, lang: 'fr' | 'en'): SyncedLocationResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT loc_id, book_id, loc_name, last_update FROM book_location WHERE user_id = ?', [userId]) as SyncedLocationResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT loc_id, book_id, loc_name, last_update FROM book_location WHERE user_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedLocations: SyncedLocationResult[] = db.all(query, params) as SyncedLocationResult[]; + return syncedLocations; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lieux synchronisés.` : `Unable to retrieve synced locations.`); } else { console.error("An unknown error occurred."); @@ -406,14 +643,23 @@ export default class LocationRepo { } } } - + + /** + * Fetches all synced location elements for a user (used for synchronization). + * @param userId - The user's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced location element records + */ static fetchSyncedLocationElements(userId: string, lang: 'fr' | 'en'): SyncedLocationElementResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT element_id, location, element_name, last_update FROM location_element WHERE user_id = ?', [userId]) as SyncedLocationElementResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT element_id, location, element_name, last_update FROM location_element WHERE user_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedLocationElements: SyncedLocationElementResult[] = db.all(query, params) as SyncedLocationElementResult[]; + return syncedLocationElements; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de lieu synchronisés.` : `Unable to retrieve synced location elements.`); } else { console.error("An unknown error occurred."); @@ -421,14 +667,23 @@ export default class LocationRepo { } } } - + + /** + * Fetches all synced location sub-elements for a user (used for synchronization). + * @param userId - The user's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced location sub-element records + */ static fetchSyncedLocationSubElements(userId: string, lang: 'fr' | 'en'): SyncedLocationSubElementResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT sub_element_id, element_id, sub_elem_name, last_update FROM location_sub_element WHERE user_id = ?', [userId]) as SyncedLocationSubElementResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT sub_element_id, element_id, sub_elem_name, last_update FROM location_sub_element WHERE user_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedLocationSubElements: SyncedLocationSubElementResult[] = db.all(query, params) as SyncedLocationSubElementResult[]; + return syncedLocationSubElements; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les sous-éléments de lieu synchronisés.` : `Unable to retrieve synced location sub-elements.`); } else { console.error("An unknown error occurred."); @@ -436,108 +691,173 @@ export default class LocationRepo { } } } + + /** + * Inserts a synced location from the remote server. + * @param locId - The location's unique identifier + * @param bookId - The book's unique identifier + * @param userId - The user's unique identifier + * @param locName - The encrypted location name + * @param locOriginalName - The original (unencrypted) location name + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the insertion affected at least one row + */ static insertSyncLocation(locId: string, bookId: string, userId: string, locName: string, locOriginalName: string, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) - VALUES (?, ?, ?, ?, ?, ?)`, - [locId, bookId, userId, locName, locOriginalName, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) + VALUES (?, ?, ?, ?, ?, ?) + `; + const params: SQLiteValue[] = [locId, bookId, userId, locName, locOriginalName, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer le lieu.` : `Unable to insert location.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Inserts a synced location element from the remote server. + * @param elementId - The element's unique identifier + * @param location - The parent location's unique identifier + * @param userId - The user's unique identifier + * @param elementName - The encrypted element name + * @param originalName - The original (unencrypted) element name + * @param elementDescription - The encrypted element description (can be null) + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the insertion affected at least one row + */ static insertSyncLocationElement(elementId: string, location: string, userId: string, elementName: string, originalName: string, elementDescription: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?)`, - [elementId, location, userId, elementName, originalName, elementDescription, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) + VALUES (?, ?, ?, ?, ?, ?, ?) + `; + const params: SQLiteValue[] = [elementId, location, userId, elementName, originalName, elementDescription, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer l'élément du lieu.` : `Unable to insert location element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - + + /** + * Inserts a synced location sub-element from the remote server. + * @param subElementId - The sub-element's unique identifier + * @param elementId - The parent element's unique identifier + * @param userId - The user's unique identifier + * @param subElemName - The encrypted sub-element name + * @param originalName - The original (unencrypted) sub-element name + * @param subElemDescription - The encrypted sub-element description (can be null) + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the insertion affected at least one row + */ static insertSyncLocationSubElement(subElementId: string, elementId: string, userId: string, subElemName: string, originalName: string, subElemDescription: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?)`, - [subElementId, elementId, userId, subElemName, originalName, subElemDescription, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) + VALUES (?, ?, ?, ?, ?, ?, ?) + `; + const params: SQLiteValue[] = [subElementId, elementId, userId, subElemName, originalName, subElemDescription, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer le sous-élément du lieu.` : `Unable to insert location sub-element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static async fetchCompleteLocationById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches complete location data by its ID (without user filtering). + * @param id - The location's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book location records + */ + static async fetchCompleteLocationById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update - FROM book_location - WHERE loc_id = ?`, - [id] - ) as BookLocationTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + const query: string = ` + SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update + FROM book_location + WHERE loc_id = ? + `; + const params: SQLiteValue[] = [id]; + const completeLocation: BookLocationTable[] = db.all(query, params) as BookLocationTable[]; + return completeLocation; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le lieu complet.` : `Unable to retrieve complete location.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - - static async fetchCompleteLocationElementById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches complete location element data by its ID (without user filtering). + * @param id - The element's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of location element records + */ + static async fetchCompleteLocationElementById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT element_id, location, user_id, element_name, original_name, element_description, last_update - FROM location_element - WHERE element_id = ?`, - [id] - ) as LocationElementTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + const query: string = ` + SELECT element_id, location, user_id, element_name, original_name, element_description, last_update + FROM location_element + WHERE element_id = ? + `; + const params: SQLiteValue[] = [id]; + const completeLocationElement: LocationElementTable[] = db.all(query, params) as LocationElementTable[]; + return completeLocationElement; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de lieu complet.` : `Unable to retrieve complete location element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - - static async fetchCompleteLocationSubElementById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches complete location sub-element data by its ID (without user filtering). + * @param id - The sub-element's unique identifier + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of location sub-element records + */ + static async fetchCompleteLocationSubElementById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update - FROM location_sub_element - WHERE sub_element_id = ?`, - [id] - ) as LocationSubElementTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + const query: string = ` + SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update + FROM location_sub_element + WHERE sub_element_id = ? + `; + const params: SQLiteValue[] = [id]; + const completeLocationSubElement: LocationSubElementTable[] = db.all(query, params) as LocationSubElementTable[]; + return completeLocationSubElement; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le sous-élément de lieu complet.` : `Unable to retrieve complete location sub-element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); diff --git a/electron/database/repositories/plotpoint.repository.ts b/electron/database/repositories/plotpoint.repository.ts index a0a331b..37011a8 100644 --- a/electron/database/repositories/plotpoint.repository.ts +++ b/electron/database/repositories/plotpoint.repository.ts @@ -1,4 +1,4 @@ -import {Database, QueryResult, RunResult, SQLiteValue} from "node-sqlite3-wasm"; +import { Database, QueryResult, RunResult, SQLiteValue } from "node-sqlite3-wasm"; import System from "@/electron/database/System"; export interface BookPlotPointsTable extends Record { @@ -27,13 +27,23 @@ export interface PlotPointQuery extends Record { } export default class PlotPointRepository { - public static fetchAllPlotPoints(userId:string,bookId:string, lang: 'fr' | 'en'):PlotPointQuery[]{ + /** + * Fetches all plot points for a specific book. + * @param userId - The ID of the user/author + * @param bookId - The ID of the book + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of plot points with their basic information + */ + public static fetchAllPlotPoints(userId: string, bookId: string, lang: 'fr' | 'en'): PlotPointQuery[] { try { const db: Database = System.getDb(); - return db.all('SELECT plot_point_id, title, summary, linked_incident_id FROM book_plot_points WHERE author_id=? AND book_id=?', [userId, bookId]) as PlotPointQuery[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT plot_point_id, title, summary, linked_incident_id FROM book_plot_points WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const plotPoints: PlotPointQuery[] = db.all(query, params) as PlotPointQuery[]; + return plotPoints; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les points d'intrigue.` : `Unable to retrieve plot points.`); } else { console.error("An unknown error occurred."); @@ -41,30 +51,46 @@ export default class PlotPointRepository { } } } + + /** + * Inserts a new plot point into the database. + * @param plotPointId - The unique ID for the new plot point + * @param userId - The ID of the user/author + * @param bookId - The ID of the book + * @param encryptedName - The encrypted title of the plot point + * @param hashedName - The hashed title for duplicate checking + * @param incidentId - The ID of the linked incident (can be empty string) + * @param lang - The language for error messages ('fr' or 'en') + * @returns The ID of the newly created plot point + */ static insertNewPlotPoint(plotPointId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, incidentId: string, lang: 'fr' | 'en'): string { - let existingResult: QueryResult | null; + let existingPlotPoint: QueryResult | null; let insertResult: RunResult; try { const db: Database = System.getDb(); - existingResult = db.get('SELECT plot_point_id FROM book_plot_points WHERE author_id=? AND book_id=? AND hashed_title=?', [userId, bookId, hashedName]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const checkQuery: string = 'SELECT plot_point_id FROM book_plot_points WHERE author_id=? AND book_id=? AND hashed_title=?'; + const checkParams: SQLiteValue[] = [userId, bookId, hashedName]; + existingPlotPoint = db.get(checkQuery, checkParams); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du point d'intrigue.` : `Unable to verify plot point existence.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (existingResult !== null) { + if (existingPlotPoint !== null) { throw new Error(lang === 'fr' ? `Ce point de l'intrigue existe déjà.` : `This plot point already exists.`); } try { const db: Database = System.getDb(); - insertResult = db.run('INSERT INTO book_plot_points (plot_point_id,title,hashed_title,author_id,book_id,linked_incident_id,last_update) VALUES (?,?,?,?,?,?,?)', [plotPointId, encryptedName, hashedName, userId, bookId, incidentId, System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const insertQuery: string = 'INSERT INTO book_plot_points (plot_point_id,title,hashed_title,author_id,book_id,linked_incident_id,last_update) VALUES (?,?,?,?,?,?,?)'; + const insertParams: SQLiteValue[] = [plotPointId, encryptedName, hashedName, userId, bookId, incidentId, System.timeStampInSeconds()]; + insertResult = db.run(insertQuery, insertParams); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le point d'intrigue.` : `Unable to add plot point.`); } else { console.error("An unknown error occurred."); @@ -76,15 +102,24 @@ export default class PlotPointRepository { } return plotPointId; } - - static deletePlotPoint(userId: string, plotNumId: string, lang: 'fr' | 'en'): boolean { + + /** + * Deletes a plot point from the database. + * @param userId - The ID of the user/author + * @param plotPointId - The ID of the plot point to delete + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the plot point was deleted, false otherwise + */ + static deletePlotPoint(userId: string, plotPointId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM book_plot_points WHERE author_id=? AND plot_point_id=?', [userId, plotNumId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'DELETE FROM book_plot_points WHERE author_id=? AND plot_point_id=?'; + const params: SQLiteValue[] = [userId, plotPointId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de supprimer le point d'intrigue.` : `Unable to delete plot point.`); } else { console.error("An unknown error occurred."); @@ -92,14 +127,29 @@ export default class PlotPointRepository { } } } - public static updatePlotPoint(userId: string, bookId: string, plotPointId: string, encryptedPlotPointName: string, plotPointHashedName: string, plotPointSummary: string, lastUpdate:number, lang: 'fr' | 'en'): boolean { + + /** + * Updates an existing plot point in the database. + * @param userId - The ID of the user/author + * @param bookId - The ID of the book + * @param plotPointId - The ID of the plot point to update + * @param encryptedPlotPointName - The new encrypted title + * @param plotPointHashedName - The new hashed title + * @param plotPointSummary - The new summary + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the plot point was updated, false otherwise + */ + public static updatePlotPoint(userId: string, bookId: string, plotPointId: string, encryptedPlotPointName: string, plotPointHashedName: string, plotPointSummary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_plot_points SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND plot_point_id=?', [encryptedPlotPointName, plotPointHashedName, plotPointSummary, lastUpdate, userId, bookId, plotPointId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'UPDATE book_plot_points SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND plot_point_id=?'; + const params: SQLiteValue[] = [encryptedPlotPointName, plotPointHashedName, plotPointSummary, lastUpdate, userId, bookId, plotPointId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le point d'intrigue.` : `Unable to update plot point.`); } else { console.error("An unknown error occurred."); @@ -107,26 +157,47 @@ export default class PlotPointRepository { } } } + + /** + * Fetches all plot points for a book with complete information for synchronization. + * @param userId - The ID of the user/author + * @param bookId - The ID of the book + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of complete plot point records + */ static async fetchBookPlotPoints(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update FROM book_plot_points WHERE author_id=? AND book_id=?', [userId, bookId]) as BookPlotPointsTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update FROM book_plot_points WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const plotPoints: BookPlotPointsTable[] = db.all(query, params) as BookPlotPointsTable[]; + return plotPoints; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les points d'intrigue.` : `Unable to retrieve plot points.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } + + /** + * Fetches all synced plot points for a user across all books. + * @param userId - The ID of the user/author + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced plot point records with minimal information + */ static fetchSyncedPlotPoints(userId: string, lang: 'fr' | 'en'): SyncedPlotPointResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT plot_point_id, book_id, title, last_update FROM book_plot_points WHERE author_id = ?', [userId]) as SyncedPlotPointResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT plot_point_id, book_id, title, last_update FROM book_plot_points WHERE author_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedPlotPoints: SyncedPlotPointResult[] = db.all(query, params) as SyncedPlotPointResult[]; + return syncedPlotPoints; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les points d'intrigue synchronisés.` : `Unable to retrieve synced plot points.`); } else { console.error("An unknown error occurred."); @@ -134,53 +205,84 @@ export default class PlotPointRepository { } } } + + /** + * Inserts a plot point during synchronization from remote data. + * @param plotPointId - The unique ID of the plot point + * @param title - The encrypted title + * @param hashedTitle - The hashed title for duplicate checking + * @param summary - The encrypted summary (can be null) + * @param linkedIncidentId - The ID of the linked incident (can be null) + * @param authorId - The ID of the author + * @param bookId - The ID of the book + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the plot point was inserted, false otherwise + */ static insertSyncPlotPoint(plotPointId: string, title: string, hashedTitle: string, summary: string | null, linkedIncidentId: string | null, authorId: string, bookId: string, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_plot_points (plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [plotPointId, title, hashedTitle, summary, linkedIncidentId, authorId, bookId, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `INSERT INTO book_plot_points (plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`; + const params: SQLiteValue[] = [plotPointId, title, hashedTitle, summary, linkedIncidentId, authorId, bookId, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer le point d'intrigue.` : `Unable to insert plot point.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static async fetchCompletePlotPointById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches complete plot point data by its ID. + * @param plotPointId - The ID of the plot point to retrieve + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array containing the plot point data (empty if not found) + */ + static async fetchCompletePlotPointById(plotPointId: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update + const query: string = `SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update FROM book_plot_points - WHERE plot_point_id = ?`, - [id] - ) as BookPlotPointsTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + WHERE plot_point_id = ?`; + const params: SQLiteValue[] = [plotPointId]; + const plotPoint: BookPlotPointsTable[] = db.all(query, params) as BookPlotPointsTable[]; + return plotPoint; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le point d'intrigue complet.` : `Unable to retrieve complete plot point.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static plotPointExist(userId: string, bookId: string, plot_point_id: string,lang: "fr" | "en"): boolean { + + /** + * Checks if a plot point exists in the database. + * @param userId - The ID of the user/author + * @param bookId - The ID of the book + * @param plotPointId - The ID of the plot point to check + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the plot point exists, false otherwise + */ + static plotPointExist(userId: string, bookId: string, plotPointId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result:QueryResult|null = db.get('SELECT 1 FROM book_plot_points WHERE author_id =? AND book_id =? AND plot_point_id =?', [userId, bookId, plot_point_id]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM book_plot_points WHERE author_id =? AND book_id =? AND plot_point_id =?'; + const params: SQLiteValue[] = [userId, bookId, plotPointId]; + const existingPlotPoint: QueryResult | null = db.get(query, params) || null; + return existingPlotPoint !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du point de intrigue.` : `Unable to check plot point existence.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } -} \ No newline at end of file +} diff --git a/electron/database/repositories/user.repository.ts b/electron/database/repositories/user.repository.ts index a732b46..e78178b 100644 --- a/electron/database/repositories/user.repository.ts +++ b/electron/database/repositories/user.repository.ts @@ -1,4 +1,4 @@ -import {Database, RunResult, SQLiteValue} from 'node-sqlite3-wasm'; +import { Database, RunResult, SQLiteValue } from 'node-sqlite3-wasm'; import System from "../System.js"; export interface UserInfosQueryResponse extends Record { @@ -45,15 +45,39 @@ export interface GuideTourResult extends Record { export default class UserRepo { - public static insertUser(uuId: string, firstName: string, lastName: string, username: string, originUsername: string, email: string, originEmail: string, lang: 'fr' | 'en' = 'fr'): string { - let result: RunResult; + /** + * Inserts a new user into the database. + * @param uuId - The unique identifier for the user + * @param firstName - The user's first name + * @param lastName - The user's last name + * @param username - The user's username + * @param originUsername - The original username from the source platform + * @param email - The user's email address + * @param originEmail - The original email from the source platform + * @param lang - The language for error messages ('fr' or 'en') + * @returns The user's UUID if insertion was successful + * @throws Error if the user cannot be registered + */ + public static insertUser( + uuId: string, + firstName: string, + lastName: string, + username: string, + originUsername: string, + email: string, + originEmail: string, + lang: 'fr' | 'en' = 'fr' + ): string { + let insertResult: RunResult; try { const db: Database = System.getDb(); - const query = `INSERT INTO erit_users (user_id, first_name, last_name, username, email, origin_email, - origin_username, plateform, term_accepted, - account_verified, reg_date) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; - const values: (string | null | number)[] = [ + const query: string = ` + INSERT INTO erit_users ( + user_id, first_name, last_name, username, email, origin_email, + origin_username, plateform, term_accepted, account_verified, reg_date + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `; + const params: SQLiteValue[] = [ uuId, firstName, lastName, @@ -61,56 +85,113 @@ export default class UserRepo { email, originEmail, originUsername, - 'desktop', // plateform - 0, // term_accepted - 1, // account_verified - Date.now() // reg_date (current timestamp) + 'desktop', + 0, + 1, + Date.now() ]; - result = db.run(query, values); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + insertResult = db.run(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'enregistrer l'utilisateur.` : `Unable to register user.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (result.changes > 0) { + if (insertResult.changes > 0) { return uuId; } else { throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'enregistrement de l'utilisateur.` : `Error registering user.`); } } + /** + * Fetches user information from the database. + * @param userId - The unique identifier of the user to fetch + * @param lang - The language for error messages ('fr' or 'en') + * @returns The user information object + * @throws Error if the user is not found or cannot be retrieved + */ public static fetchUserInfos(userId: string, lang: 'fr' | 'en' = 'fr'): UserInfosQueryResponse { - let result; + let userInfo: Record | undefined; try { const db: Database = System.getDb(); - result = db.get('SELECT first_name, last_name, username, email, plateform, term_accepted, account_verified, 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}`); + const query: string = ` + SELECT first_name, last_name, username, email, plateform, term_accepted, + account_verified, author_name, erite_points AS rite_points, user_group + FROM erit_users + WHERE user_id = ? + `; + const params: SQLiteValue[] = [userId]; + userInfo = db.get(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les informations utilisateur.` : `Unable to retrieve user information.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result) { + if (!userInfo) { throw new Error(lang === 'fr' ? `Utilisateur non trouvé.` : `User not found.`); } - return result as UserInfosQueryResponse; + return userInfo as UserInfosQueryResponse; } - public static updateUserInfos(userId: string, firstName: string, lastName: string, username: string, originUsername: string, email: string, originEmail: string, originalAuthorName: string, authorName: string, lang: 'fr' | 'en' = 'fr'): boolean { + /** + * Updates user information in the database. + * @param userId - The unique identifier of the user to update + * @param firstName - The new first name + * @param lastName - The new last name + * @param username - The new username + * @param originUsername - The original username from the source platform + * @param email - The new email address + * @param originEmail - The original email from the source platform + * @param originalAuthorName - The original author name + * @param authorName - The new author name + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the update was successful, false otherwise + * @throws Error if the update fails + */ + public static updateUserInfos( + userId: string, + firstName: string, + lastName: string, + username: string, + originUsername: string, + email: string, + originEmail: string, + originalAuthorName: string, + authorName: string, + lang: 'fr' | 'en' = 'fr' + ): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE `erit_users` SET `first_name`=?, `last_name`=?, `username`=?, email=?,`origin_username`=?, origin_author_name=? ,author_name=? WHERE user_id=? AND `origin_email`=?', [firstName, lastName, username, email, originUsername, originalAuthorName, authorName, userId, originEmail]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + UPDATE erit_users + SET first_name = ?, last_name = ?, username = ?, email = ?, + origin_username = ?, origin_author_name = ?, author_name = ? + WHERE user_id = ? AND origin_email = ? + `; + const params: SQLiteValue[] = [ + firstName, + lastName, + username, + email, + originUsername, + originalAuthorName, + authorName, + userId, + originEmail + ]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour les informations utilisateur.` : `Unable to update user information.`); } else { console.error("An unknown error occurred."); @@ -119,23 +200,36 @@ export default class UserRepo { } } + /** + * Fetches account information for a user. + * @param userId - The unique identifier of the user + * @param lang - The language for error messages ('fr' or 'en') + * @returns The user account information object + * @throws Error if the account is not found or cannot be retrieved + */ public static fetchAccountInformation(userId: string, lang: 'fr' | 'en' = 'fr'): UserAccountQuery { - let result; + let accountInfo: Record | undefined; try { const db: Database = System.getDb(); - result = db.get('SELECT first_name, last_name, username, author_name, email FROM erit_users WHERE user_id=?', [userId]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = ` + SELECT first_name, last_name, username, author_name, email + FROM erit_users + WHERE user_id = ? + `; + const params: SQLiteValue[] = [userId]; + accountInfo = db.get(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les informations du compte.` : `Unable to retrieve account information.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result) { + if (!accountInfo) { throw new Error(lang === 'fr' ? `Compte non trouvé.` : `Account not found.`); } - return result as UserAccountQuery; + return accountInfo as UserAccountQuery; } } diff --git a/electron/database/repositories/world.repository.ts b/electron/database/repositories/world.repository.ts index 7bc2772..9bf4ac0 100644 --- a/electron/database/repositories/world.repository.ts +++ b/electron/database/repositories/world.repository.ts @@ -1,4 +1,4 @@ -import {Database, QueryResult, RunResult, SQLiteValue} from "node-sqlite3-wasm"; +import { Database, QueryResult, RunResult, SQLiteValue } from "node-sqlite3-wasm"; import System from "@/electron/database/System"; export interface BookWorldTable extends Record { @@ -61,15 +61,25 @@ export interface WorldElementValue { type: number; } -export default class WorldRepository { - public static checkWorldExist(userId:string,bookId:string,worldName:string, lang: 'fr' | 'en'):boolean{ +export default class WorldRepository { + /** + * Checks if a world with the given name exists for a specific user and book. + * @param userId - The unique identifier of the user + * @param bookId - The unique identifier of the book + * @param worldName - The hashed name of the world to check + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the world exists, false otherwise + */ + public static checkWorldExist(userId: string, bookId: string, worldName: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result = db.get('SELECT world_id FROM book_world WHERE author_id=? AND book_id=? AND hashed_name=?', [userId,bookId,worldName]); - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT world_id FROM book_world WHERE author_id=? AND book_id=? AND hashed_name=?'; + const params: SQLiteValue[] = [userId, bookId, worldName]; + const world: QueryResult | null = db.get(query, params); + return world !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du monde.` : `Unable to verify world existence.`); } else { console.error("An unknown error occurred."); @@ -77,33 +87,56 @@ export default class WorldRepository { } } } - + + /** + * Inserts a new world into the database. + * @param worldId - The unique identifier for the new world + * @param userId - The unique identifier of the author + * @param bookId - The unique identifier of the book + * @param encryptedName - The encrypted name of the world + * @param hashedName - The hashed name of the world for uniqueness checks + * @param lang - The language for error messages ('fr' or 'en') + * @returns The world ID if insertion was successful + */ public static insertNewWorld(worldId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string { - let result: RunResult; + let insertResult: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO book_world (world_id,author_id, book_id, name, hashed_name, last_update) VALUES (?,?,?,?,?,?)', [worldId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'INSERT INTO book_world (world_id, author_id, book_id, name, hashed_name, last_update) VALUES (?, ?, ?, ?, ?, ?)'; + const params: SQLiteValue[] = [worldId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()]; + insertResult = db.run(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le monde.` : `Unable to add world.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result || result.changes === 0) { + if (!insertResult || insertResult.changes === 0) { throw new Error(lang === 'fr' ? `Erreur lors de l'ajout du monde.` : `Error adding world.`); } return worldId; } - public static fetchWorlds(userId: string, bookId: string, lang: 'fr' | 'en'):WorldQuery[] { + + /** + * Fetches all worlds and their elements for a specific user and book. + * @param userId - The unique identifier of the user + * @param bookId - The unique identifier of the book + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of world query results with joined element data + */ + public static fetchWorlds(userId: string, bookId: string, lang: 'fr' | 'en'): WorldQuery[] { try { const db: Database = System.getDb(); - return db.all('SELECT world.world_id AS world_id, world.name AS world_name, world.history, world.politics, world.economy, world.religion, world.languages, element.element_id AS element_id, element.name AS element_name, element.description AS element_description, element.element_type FROM book_world AS world LEFT JOIN book_world_elements AS element ON world.world_id=element.world_id WHERE world.author_id=? AND world.book_id=?', [userId, bookId]) as WorldQuery[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `SELECT world.world_id AS world_id, world.name AS world_name, world.history, world.politics, world.economy, world.religion, world.languages, element.element_id AS element_id, element.name AS element_name, element.description AS element_description, element.element_type FROM book_world AS world LEFT JOIN book_world_elements AS element ON world.world_id=element.world_id WHERE world.author_id=? AND world.book_id=?`; + const params: SQLiteValue[] = [userId, bookId]; + const worlds: WorldQuery[] = db.all(query, params) as WorldQuery[]; + return worlds; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les mondes.` : `Unable to retrieve worlds.`); } else { console.error("An unknown error occurred."); @@ -111,15 +144,32 @@ export default class WorldRepository { } } } - + + /** + * Updates a world's data in the database. + * @param userId - The unique identifier of the author + * @param worldId - The unique identifier of the world to update + * @param encryptName - The new encrypted name + * @param hashedName - The new hashed name + * @param encryptHistory - The new encrypted history + * @param encryptPolitics - The new encrypted politics + * @param encryptEconomy - The new encrypted economy + * @param encryptReligion - The new encrypted religion + * @param encryptLanguages - The new encrypted languages + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the update was successful, false otherwise + */ public static updateWorld(userId: string, worldId: string, encryptName: string, hashedName: string, encryptHistory: string, encryptPolitics: string, encryptEconomy: string, encryptReligion: string, encryptLanguages: string, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_world SET name=?, hashed_name=?, history=?, politics=?, economy=?, religion=?, languages=?, last_update=? WHERE author_id=? AND world_id=?', [encryptName, hashedName, encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, lastUpdate, userId, worldId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'UPDATE book_world SET name=?, hashed_name=?, history=?, politics=?, economy=?, religion=?, languages=?, last_update=? WHERE author_id=? AND world_id=?'; + const params: SQLiteValue[] = [encryptName, hashedName, encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, lastUpdate, userId, worldId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le monde.` : `Unable to update world.`); } else { console.error("An unknown error occurred."); @@ -127,20 +177,29 @@ export default class WorldRepository { } } } - + + /** + * Updates multiple world elements in the database. + * @param userId - The unique identifier of the user + * @param elements - An array of world element values to update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if all updates were successful, false otherwise + */ public static updateWorldElements(userId: string, elements: WorldElementValue[], lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); + const query: string = 'UPDATE book_world_elements SET name=?, description=?, element_type=?, last_update=? WHERE user_id=? AND element_id=?'; for (const element of elements) { - const result: RunResult = db.run('UPDATE book_world_elements SET name=?, description=?, element_type=?, last_update=? WHERE user_id=? AND element_id=?', [element.name, element.description, element.type, System.timeStampInSeconds(), userId, element.id]); - if (result.changes <= 0) { + const params: SQLiteValue[] = [element.name, element.description, element.type, System.timeStampInSeconds(), userId, element.id]; + const updateResult: RunResult = db.run(query, params); + if (updateResult.changes <= 0) { return false; } } return true; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour les éléments du monde.` : `Unable to update world elements.`); } else { console.error("An unknown error occurred."); @@ -148,15 +207,24 @@ export default class WorldRepository { } } } - + + /** + * Checks if a world element with the given hashed name exists. + * @param worldNumId - The unique identifier of the world + * @param hashedName - The hashed name of the element to check + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the element exists, false otherwise + */ public static checkElementExist(worldNumId: string, hashedName: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result = db.get('SELECT element_id FROM book_world_elements WHERE world_id=? AND original_name=?', [worldNumId, hashedName]); - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT element_id FROM book_world_elements WHERE world_id=? AND original_name=?'; + const params: SQLiteValue[] = [worldNumId, hashedName]; + const element: QueryResult | null = db.get(query, params); + return element !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément.` : `Unable to verify element existence.`); } else { console.error("An unknown error occurred."); @@ -164,35 +232,57 @@ export default class WorldRepository { } } } - + + /** + * Inserts a new world element into the database. + * @param userId - The unique identifier of the user + * @param elementId - The unique identifier for the new element + * @param elementType - The type of the element + * @param worldId - The unique identifier of the parent world + * @param encryptedName - The encrypted name of the element + * @param hashedName - The hashed name of the element for uniqueness checks + * @param lang - The language for error messages ('fr' or 'en') + * @returns The element ID if insertion was successful + */ public static insertNewElement(userId: string, elementId: string, elementType: number, worldId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string { - let result: RunResult; + let insertResult: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO book_world_elements (element_id,world_id,user_id, name, original_name, element_type, last_update) VALUES (?,?,?,?,?,?,?)', [elementId, worldId, userId, encryptedName, hashedName, elementType, System.timeStampInSeconds()]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'INSERT INTO book_world_elements (element_id, world_id, user_id, name, original_name, element_type, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)'; + const params: SQLiteValue[] = [elementId, worldId, userId, encryptedName, hashedName, elementType, System.timeStampInSeconds()]; + insertResult = db.run(query, params); + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'ajouter l'élément.` : `Unable to add element.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } - if (!result || result.changes === 0) { + if (!insertResult || insertResult.changes === 0) { throw new Error(lang === 'fr' ? `Erreur lors de l'ajout de l'élément.` : `Error adding element.`); } return elementId; } - + + /** + * Deletes a world element from the database. + * @param userId - The unique identifier of the user + * @param elementId - The unique identifier of the element to delete + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the deletion was successful, false otherwise + */ public static deleteElement(userId: string, elementId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('DELETE FROM book_world_elements WHERE user_id=? AND element_id=?', [userId, elementId]); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'DELETE FROM book_world_elements WHERE user_id=? AND element_id=?'; + const params: SQLiteValue[] = [userId, elementId]; + const deleteResult: RunResult = db.run(query, params); + return deleteResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de supprimer l'élément.` : `Unable to delete element.`); } else { console.error("An unknown error occurred."); @@ -200,42 +290,71 @@ export default class WorldRepository { } } } - + + /** + * Fetches all worlds for a specific user and book. + * @param userId - The unique identifier of the user + * @param bookId - The unique identifier of the book + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book world table records + */ static async fetchBookWorlds(userId: string, bookId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update FROM book_world WHERE author_id=? AND book_id=?', [userId, bookId]) as BookWorldTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update FROM book_world WHERE author_id=? AND book_id=?'; + const params: SQLiteValue[] = [userId, bookId]; + const worlds: BookWorldTable[] = db.all(query, params) as BookWorldTable[]; + return worlds; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les mondes.` : `Unable to retrieve worlds.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - - static async fetchBookWorldElements(userId: string,worldId:string, lang: 'fr' | 'en'): Promise { + + /** + * Fetches all elements for a specific world. + * @param userId - The unique identifier of the user + * @param worldId - The unique identifier of the world + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book world elements table records + */ + static async fetchBookWorldElements(userId: string, worldId: string, lang: 'fr' | 'en'): Promise { try { const db: Database = System.getDb(); - return db.all('SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update FROM book_world_elements WHERE user_id=? AND world_id=?', [userId, worldId]) as BookWorldElementsTable[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update FROM book_world_elements WHERE user_id=? AND world_id=?'; + const params: SQLiteValue[] = [userId, worldId]; + const elements: BookWorldElementsTable[] = db.all(query, params) as BookWorldElementsTable[]; + return elements; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments du monde.` : `Unable to retrieve world elements.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - + + /** + * Fetches all synced worlds for a specific user. + * @param userId - The unique identifier of the user + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced world results + */ static fetchSyncedWorlds(userId: string, lang: 'fr' | 'en'): SyncedWorldResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT world_id, book_id, name, last_update FROM book_world WHERE author_id = ?', [userId]) as SyncedWorldResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT world_id, book_id, name, last_update FROM book_world WHERE author_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedWorlds: SyncedWorldResult[] = db.all(query, params) as SyncedWorldResult[]; + return syncedWorlds; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les mondes synchronisés.` : `Unable to retrieve synced worlds.`); } else { console.error("An unknown error occurred."); @@ -243,14 +362,23 @@ export default class WorldRepository { } } } - + + /** + * Fetches all synced world elements for a specific user. + * @param userId - The unique identifier of the user + * @param lang - The language for error messages ('fr' or 'en') + * @returns An array of synced world element results + */ static fetchSyncedWorldElements(userId: string, lang: 'fr' | 'en'): SyncedWorldElementResult[] { try { const db: Database = System.getDb(); - return db.all('SELECT element_id, world_id, name, last_update FROM book_world_elements WHERE user_id = ?', [userId]) as SyncedWorldElementResult[]; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT element_id, world_id, name, last_update FROM book_world_elements WHERE user_id = ?'; + const params: SQLiteValue[] = [userId]; + const syncedElements: SyncedWorldElementResult[] = db.all(query, params) as SyncedWorldElementResult[]; + return syncedElements; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de monde synchronisés.` : `Unable to retrieve synced world elements.`); } else { console.error("An unknown error occurred."); @@ -258,90 +386,134 @@ export default class WorldRepository { } } } - + + /** + * Inserts a synced world into the database. + * @param worldId - The unique identifier for the world + * @param name - The encrypted name of the world + * @param hashedName - The hashed name of the world + * @param authorId - The unique identifier of the author + * @param bookId - The unique identifier of the book + * @param history - The encrypted history (optional) + * @param politics - The encrypted politics (optional) + * @param economy - The encrypted economy (optional) + * @param religion - The encrypted religion (optional) + * @param languages - The encrypted languages (optional) + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the insertion was successful, false otherwise + */ static insertSyncWorld(worldId: string, name: string, hashedName: string, authorId: string, bookId: string, history: string | null, politics: string | null, economy: string | null, religion: string | null, languages: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_world (world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [worldId, name, hashedName, authorId, bookId, history, politics, economy, religion, languages, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `INSERT INTO book_world (world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + const params: SQLiteValue[] = [worldId, name, hashedName, authorId, bookId, history, politics, economy, religion, languages, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer le monde.` : `Unable to insert world.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - + + /** + * Inserts a synced world element into the database. + * @param elementId - The unique identifier for the element + * @param worldId - The unique identifier of the parent world + * @param userId - The unique identifier of the user + * @param elementType - The type of the element + * @param name - The encrypted name of the element + * @param originalName - The original hashed name + * @param description - The encrypted description (optional) + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the insertion was successful, false otherwise + */ static insertSyncWorldElement(elementId: string, worldId: string, userId: string, elementType: number, name: string, originalName: string, description: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run( - `INSERT INTO book_world_elements (element_id, world_id, user_id, element_type, name, original_name, description, last_update) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [elementId, worldId, userId, elementType, name, originalName, description, lastUpdate] - ); - return result.changes > 0; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `INSERT INTO book_world_elements (element_id, world_id, user_id, element_type, name, original_name, description, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`; + const params: SQLiteValue[] = [elementId, worldId, userId, elementType, name, originalName, description, lastUpdate]; + const insertResult: RunResult = db.run(query, params); + return insertResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'insérer l'élément du monde.` : `Unable to insert world element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - - static async fetchCompleteWorldById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches a complete world by its ID. + * @param id - The unique identifier of the world + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book world table records + */ + static async fetchCompleteWorldById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update - FROM book_world - WHERE world_id = ?`, - [id] - ) as BookWorldTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + const query: string = `SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update FROM book_world WHERE world_id = ?`; + const params: SQLiteValue[] = [id]; + const worlds: BookWorldTable[] = db.all(query, params) as BookWorldTable[]; + return worlds; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer le monde complet.` : `Unable to retrieve complete world.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - - static async fetchCompleteWorldElementById(id: string, lang: "fr" | "en"):Promise { + + /** + * Fetches a complete world element by its ID. + * @param id - The unique identifier of the element + * @param lang - The language for error messages ('fr' or 'en') + * @returns A promise resolving to an array of book world elements table records + */ + static async fetchCompleteWorldElementById(id: string, lang: "fr" | "en"): Promise { try { const db: Database = System.getDb(); - return db.all( - `SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update - FROM book_world_elements - WHERE element_id = ?`, - [id] - ) as BookWorldElementsTable[]; - } catch (e:unknown){ - if (e instanceof Error) { + const query: string = `SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update FROM book_world_elements WHERE element_id = ?`; + const params: SQLiteValue[] = [id]; + const elements: BookWorldElementsTable[] = db.all(query, params) as BookWorldElementsTable[]; + return elements; + } catch (error: unknown) { + if (error instanceof Error) { throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de monde complet.` : `Unable to retrieve complete world element.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - static updateWorldElement(userId: string, elementId: string, name: string, description: string, lastUpdate: number,lang: "fr" | "en"):boolean { + + /** + * Updates a single world element's name and description. + * @param userId - The unique identifier of the user + * @param elementId - The unique identifier of the element to update + * @param name - The new encrypted name + * @param description - The new encrypted description + * @param lastUpdate - The timestamp of the last update + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the update was successful, false otherwise + */ + static updateWorldElement(userId: string, elementId: string, name: string, description: string, lastUpdate: number, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const query:string = `UPDATE book_world_elements SET name = ?, description = ?, last_update = FROM_UNIXTIME(?) WHERE element_id = UUID_TO_BIN(?) AND user_id = UUID_TO_BIN(?)`; - const params:(string|number)[] = [name, description, lastUpdate, elementId, userId]; - const result:RunResult = db.run(query, params); - return result.changes > 0; - } catch (e:unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = `UPDATE book_world_elements SET name = ?, description = ?, last_update = ? WHERE element_id = ? AND user_id = ?`; + const params: SQLiteValue[] = [name, description, lastUpdate, elementId, userId]; + const updateResult: RunResult = db.run(query, params); + return updateResult.changes > 0; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'élément du monde.` : `Unable to update world element.`); } else { console.error("An unknown error occurred."); @@ -349,34 +521,54 @@ export default class WorldRepository { } } } - - static worldExist(userId: string, bookId: string, world_id: string,lang: "fr" | "en"): boolean { + + /** + * Checks if a world exists for a specific user and book. + * @param userId - The unique identifier of the user + * @param bookId - The unique identifier of the book + * @param worldId - The unique identifier of the world + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the world exists, false otherwise + */ + static worldExist(userId: string, bookId: string, worldId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `book_world` WHERE `world_id`=? AND `author_id`=? AND `book_id`=?', [world_id, userId, bookId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM book_world WHERE world_id=? AND author_id=? AND book_id=?'; + const params: SQLiteValue[] = [worldId, userId, bookId]; + const world: QueryResult | null = db.get(query, params) || null; + return world !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du monde.` : `Unable to check world existence.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } - - static worldElementExist(userId: string, world_id: string, element_id: string, lang: "fr" | "en"): boolean { + + /** + * Checks if a world element exists for a specific user and world. + * @param userId - The unique identifier of the user + * @param worldId - The unique identifier of the world + * @param elementId - The unique identifier of the element + * @param lang - The language for error messages ('fr' or 'en') + * @returns True if the element exists, false otherwise + */ + static worldElementExist(userId: string, worldId: string, elementId: string, lang: "fr" | "en"): boolean { try { const db: Database = System.getDb(); - const result: QueryResult | null = db.get('SELECT 1 FROM `book_world_elements` WHERE `element_id`=? AND `world_id`=? AND `user_id`=?', [element_id, world_id, userId]) || null; - return result !== null; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); + const query: string = 'SELECT 1 FROM book_world_elements WHERE element_id=? AND world_id=? AND user_id=?'; + const params: SQLiteValue[] = [elementId, worldId, userId]; + const element: QueryResult | null = db.get(query, params) || null; + return element !== null; + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément du monde.` : `Unable to check world element existence.`); } else { throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } -} \ No newline at end of file +}