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.
This commit is contained in:
natreex
2026-01-12 09:57:37 -05:00
parent 9ad2ee9692
commit d9bf089e32
12 changed files with 3202 additions and 1252 deletions

View File

@@ -22,13 +22,23 @@ export interface ActQuery extends Record<string, SQLiteValue> {
} }
export default class ActRepository { 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[] { public static fetchAllActs(userId: string, bookId: string, lang: 'fr' | 'en'): ActQuery[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT act_index, summary FROM book_act_summaries WHERE book_id=? AND user_id=?';
} catch (e: unknown) { const params: SQLiteValue[] = [bookId, userId];
if (e instanceof Error) { return db.all(query, params) as ActQuery[];
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 récupérer les actes.` : `Unable to retrieve acts.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les actes.` : `Unable to retrieve acts.`);
} else { } else {
console.error("An unknown error occurred."); 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 { public static updateActSummary(userId: string, bookId: string, actId: number, summary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); 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]); const query: string = 'UPDATE book_act_summaries SET summary=?, last_update=? WHERE user_id=? AND book_id=? AND act_sum_id=?';
return result.changes > 0; const params: SQLiteValue[] = [summary, lastUpdate, userId, bookId, actId];
} catch (e: unknown) { const updateResult: RunResult = db.run(query, params);
if (e instanceof Error) { return updateResult.changes > 0;
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 résumé de l'acte.` : `Unable to update act summary.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le résumé de l'acte.` : `Unable to update act summary.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static insertActSummary(actSummaryId: string, userId: string, bookId: string, actId: number, actSummary: string, lang: 'fr' | 'en'): string {
let result:RunResult let insertResult: RunResult;
try { try {
const db: Database = System.getDb(); 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()]); const query: string = 'INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?,?,?,?,?,?)';
} catch (e: unknown) { const params: SQLiteValue[] = [actSummaryId, bookId, userId, actId, actSummary, System.timeStampInSeconds()];
if (e instanceof Error) { insertResult = db.run(query, params);
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'ajouter le résumé de l'acte.` : `Unable to add act summary.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le résumé de l'acte.` : `Unable to add act summary.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); throw new Error(lang === 'fr' ? `Erreur lors de l'ajout du résumé de l'acte.` : `Error adding act summary.`);
} }
return actSummaryId; 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<BookActSummariesTable[]> { static async fetchBookActSummaries(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookActSummariesTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { return db.all(query, params) as BookActSummariesTable[];
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 récupérer les résumés des actes.` : `Unable to retrieve act summaries.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les résumés des actes.` : `Unable to retrieve act summaries.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedActSummaries(userId: string, lang: 'fr' | 'en'): SyncedActSummaryResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT act_sum_id, book_id, last_update FROM book_act_summaries WHERE user_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedActSummaries: SyncedActSummaryResult[] = db.all(query, params) as SyncedActSummaryResult[];
console.error(`DB Error: ${e.message}`); 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.`); 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 { } else {
console.error("An unknown error occurred."); 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 { static insertSyncActSummary(actSumId: string, bookId: string, userId: string, actIndex: number, summary: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = 'INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?, ?, ?, ?, ?, ?)';
`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];
[actSumId, bookId, userId, actIndex, summary, lastUpdate] const insertResult: RunResult = db.run(query, params);
); return insertResult.changes > 0;
return result.changes > 0; } catch (error: unknown) {
} catch (e: unknown) { if (error instanceof Error) {
if (e instanceof Error) { console.error(`DB Error: ${error.message}`);
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le résumé d'acte.` : `Unable to insert act summary.`); throw new Error(lang === 'fr' ? `Impossible d'insérer le résumé d'acte.` : `Unable to insert act summary.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteActSummaryById(id: string, lang: "fr" | "en"):Promise<BookActSummariesTable[]> {
/**
* 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<BookActSummariesTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( const query: string = `SELECT act_sum_id, book_id, user_id, act_index, summary, last_update
`SELECT act_sum_id, book_id, user_id, act_index, summary, last_update
FROM book_act_summaries FROM book_act_summaries
WHERE act_sum_id = ?`, WHERE act_sum_id = ?`;
[id] const params: SQLiteValue[] = [id];
) as BookActSummariesTable[]; const actSummary: BookActSummariesTable[] = db.all(query, params) as BookActSummariesTable[];
} catch (e:unknown){ return actSummary;
if (e instanceof Error) { } 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le résumé d'acte complet.` : `Unable to retrieve complete act summary.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM book_act_summaries WHERE user_id =? AND book_id =? AND act_index = ?';
return result !== null; const params: SQLiteValue[] = [userId, bookId, actIndex];
} catch (e: unknown) { const existenceCheck: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return existenceCheck !== null;
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 vérifier l'existence du résumé de l'acte.` : `Unable to check act summary existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du résumé de l'acte.` : `Unable to check act summary existence.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
} }

View File

@@ -47,197 +47,318 @@ export interface BookCoverQuery extends Record<string, SQLiteValue> {
} }
export default class BookRepo { 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[] { public static fetchBooks(userId: string, lang: 'fr' | 'en'): BookQuery[] {
try { try {
const db: Database = System.getDb(); 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) { } catch (error: unknown) {
if (error instanceof Error) { if (error instanceof Error) {
console.error(error.message); console.error(error.message);
throw new Error(lang === 'fr' ? 'Impossible de récupérer la liste des livres.' : 'Unable to retrieve book list.'); 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 { try {
const db: Database = System.getDb(); 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]); const query: string = 'UPDATE erit_books SET cover_image=?, last_update=? WHERE book_id=? AND author_id=?';
return result.changes>0; const params: SQLiteValue[] = [coverImageName, System.timeStampInSeconds(), bookId, userId];
} catch (e: unknown) { const updateResult: RunResult = db.run(query, params);
if (e instanceof Error) { return updateResult.changes > 0;
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;
} catch (error: unknown) { } catch (error: unknown) {
if (error instanceof Error) { if (error instanceof Error) {
console.error(`DB Error: ${error.message}`); 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.`); 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.");
} }
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 { try {
const db: Database = System.getDb(); 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]); 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=?';
return result!==null; const params: SQLiteValue[] = [bookId, userId];
} catch (err: unknown) { book = db.get(query, params) as BookQuery;
if (err instanceof Error) { } catch (error: unknown) {
console.error(`DB Error: ${err.message}`); if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du livre.` : `Unable to verify book existence.`); console.error(`DB Error: ${error.message}`);
} else { 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.");
} }
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 { 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 { try {
const db: Database = System.getDb(); 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()]); 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 (?,?,?,?,?,?,?,?,?,?,?,?)';
} catch (err: unknown) { const params: SQLiteValue[] = [bookId, type, userId, encryptedTitle, hashedTitle, encryptedSubTitle, hashedSubTitle, encryptedSummary, serie, publicationDate ? publicationDate : null, desiredWordCount, System.timeStampInSeconds()];
if (err instanceof Error) { insertResult = db.run(query, params);
console.error(`DB Error: ${err.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible d'ajouter le livre.` : `Unable to add book.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Impossible d'ajouter le livre." : 'Unable to add book.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 livre.` : `Error adding book.`); throw new Error(lang === 'fr' ? "Erreur lors de l'ajout du livre." : 'Error adding book.');
} }
return bookId; 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.get('SELECT cover_image FROM erit_books WHERE author_id=? AND book_id=?', [userId, bookId]) as BookCoverQuery; const query: string = 'SELECT cover_image FROM erit_books WHERE author_id=? AND book_id=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { return db.get(query, params) as BookCoverQuery;
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de récupérer la couverture du livre.` : `Unable to retrieve book cover.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? 'Impossible de récupérer la couverture du livre.' : 'Unable to retrieve book cover.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 { 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 { try {
const db: Database = System.getDb(); 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=?', 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=?';
[title, hashedTitle, subTitle, hashedSubTitle, summary, 0, publicationDate ? System.dateToMySqlDate(publicationDate) : null, wordCount, System.timeStampInSeconds(), userId, bookId]); const params: SQLiteValue[] = [title, hashedTitle, subTitle, hashedSubTitle, summary, 0, publicationDate ? System.dateToMySqlDate(publicationDate) : null, wordCount, System.timeStampInSeconds(), userId, bookId];
return result.changes > 0; const updateResult: RunResult = db.run(query, params);
} catch (e: unknown) { return updateResult.changes > 0;
if (e instanceof Error) { } catch (error: unknown) {
console.error(`DB Error: ${e.message}`); if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de mettre à jour les informations du livre.` : `Unable to update book information.`); console.error(`DB Error: ${error.message}`);
} else { 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.");
} }
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 { public static deleteBook(userId: string, bookId: string, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM erit_books WHERE author_id=? AND book_id=?', [userId,bookId]); const query: string = 'DELETE FROM erit_books WHERE author_id=? AND book_id=?';
return result.changes > 0; const params: SQLiteValue[] = [userId, bookId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de supprimer le livre.` : `Unable to delete book.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? 'Impossible de supprimer le livre.' : 'Unable to delete book.');
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<EritBooksTable[]>{
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.");
} }
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<EritBooksTable[]> {
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 { 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
`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) const params: SQLiteValue[] = [bookId, userId, type, title, hashedTitle, subTitle, hashedSubTitle, summary, serieId, desiredReleaseDate, desiredWordCount, wordsCount, coverImage, lastUpdate];
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, const insertResult: RunResult = db.run(query, params);
[bookId, userId, type, title, hashedTitle, subTitle, hashedSubTitle, summary, serieId, desiredReleaseDate, desiredWordCount, wordsCount, coverImage, lastUpdate] return insertResult.changes > 0;
); } catch (error: unknown) {
return result.changes > 0; if (error instanceof Error) {
} catch (e: unknown) { console.error(`DB Error: ${error.message}`);
if (e instanceof Error) { throw new Error(lang === 'fr' ? "Impossible d'insérer le livre synchronisé." : 'Unable to insert synced book.');
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.");
} }
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<EritBooksTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all(`SELECT * FROM erit_books WHERE book_id = ?`, [bookId]) as EritBooksTable[]; const query: string = 'SELECT * FROM erit_books WHERE book_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [bookId];
if (e instanceof Error) { return db.all(query, params) as EritBooksTable[];
throw new Error(lang === 'fr' ? `Impossible de récupérer le livre complet.` : `Unable to retrieve complete book.`); } catch (error: unknown) {
} else { if (error instanceof Error) {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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.");
} }
} }
} }

View File

@@ -1,14 +1,13 @@
import {Database, QueryResult, RunResult, SQLiteValue} from 'node-sqlite3-wasm'; import {Database, QueryResult, RunResult, SQLiteValue} from 'node-sqlite3-wasm';
import System from "../System.js"; import System from "../System.js";
import {BookQuery, EritBooksTable, SyncedBookResult} from "@/electron/database/repositories/book.repository";
export interface ChapterQueryResult extends Record<string, SQLiteValue>{ export interface ChapterQueryResult extends Record<string, SQLiteValue> {
chapter_id: string; chapter_id: string;
title: string; title: string;
chapter_order: number; chapter_order: number;
} }
export interface ActChapterQuery extends Record<string, SQLiteValue>{ export interface ActChapterQuery extends Record<string, SQLiteValue> {
chapter_info_id: number; chapter_info_id: number;
chapter_id: string; chapter_id: string;
title: string; title: string;
@@ -20,7 +19,7 @@ export interface ActChapterQuery extends Record<string, SQLiteValue>{
goal: string; goal: string;
} }
export interface ChapterStoryQueryResult extends Record<string, SQLiteValue>{ export interface ChapterStoryQueryResult extends Record<string, SQLiteValue> {
chapter_info_id: number; chapter_info_id: number;
act_id: number; act_id: number;
summary: string; summary: string;
@@ -34,7 +33,7 @@ export interface ChapterStoryQueryResult extends Record<string, SQLiteValue>{
plot_summary: string; plot_summary: string;
} }
export interface LastChapterResult extends Record<string, SQLiteValue>{ export interface LastChapterResult extends Record<string, SQLiteValue> {
chapter_id: string; chapter_id: string;
version: number; version: number;
} }
@@ -83,413 +82,620 @@ export interface ChapterBookResult extends Record<string, SQLiteValue> {
content: string | null; content: string | null;
} }
export default class ChapterRepo{ export default class ChapterRepo {
public static checkNameDuplication(userId:string,bookId:string,hashedTitle:string, lang: 'fr' | 'en' = 'fr'):boolean{ /**
* 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 { try {
const db: Database = System.getDb(); 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]); const query: string = 'SELECT chapter_id FROM book_chapters WHERE author_id=? AND book_id=? AND hashed_title=?';
return result !== null; const params: SQLiteValue[] = [userId, bookId, hashedTitle];
} catch (e: unknown) { const chapter: QueryResult | null = db.get(query, params);
if (e instanceof Error) { return chapter !== null;
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de vérifier la duplication du nom.` : `Unable to verify name duplication.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? 'Impossible de vérifier la duplication du nom.' : 'Unable to verify name duplication.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 { 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 { try {
const db: Database = System.getDb(); 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()]); const query: string = 'INSERT INTO book_chapters (chapter_id, author_id, book_id, title, hashed_title, words_count, chapter_order, last_update) VALUES (?,?,?,?,?,?,?,?)';
} catch (e: unknown) { const params: SQLiteValue[] = [chapterId, userId, bookId, title, hashedTitle, wordsCount, chapterOrder, System.timeStampInSeconds()];
if (e instanceof Error) { insertResult = db.run(query, params);
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible d'ajouter le chapitre.` : `Unable to add chapter.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Impossible d'ajouter le chapitre." : 'Unable to add chapter.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 passé lors de l'ajout du chapitre.` : `Error adding chapter.`); throw new Error(lang === 'fr' ? "Une erreur s'est passé lors de l'ajout du chapitre." : 'Error adding chapter.');
} }
return chapterId; 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 { try {
const db: Database = System.getDb(); 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[]; 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 = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [bookId, userId];
if (e instanceof Error) { return db.all(query, params) as ActChapterQuery[];
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres pour les actes.` : `Unable to retrieve chapters for acts.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres pour les actes.' : 'Unable to retrieve chapters for acts.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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[] { public static fetchAllChapterFromABook(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterQueryResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT chapter_id, title, chapter_order FROM book_chapters WHERE book_id=? AND author_id=? ORDER BY chapter_order';
} catch (e: unknown) { const params: SQLiteValue[] = [bookId, userId];
if (e instanceof Error) { return db.all(query, params) as ChapterQueryResult[];
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres.` : `Unable to retrieve chapters.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); 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.");
} }
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 { public static deleteChapter(userId: string, chapterId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_chapters WHERE author_id=? AND chapter_id=?', [userId, chapterId]); const query: string = 'DELETE FROM book_chapters WHERE author_id=? AND chapter_id=?';
return result.changes > 0; const params: SQLiteValue[] = [userId, chapterId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de supprimer le chapitre.` : `Unable to delete chapter.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? 'Impossible de supprimer le chapitre.' : 'Unable to delete chapter.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 { 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 existingChapter: QueryResult | null;
let result: RunResult; let insertResult: RunResult;
try { try {
const db: Database = System.getDb(); 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]); 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=?';
} catch (e: unknown) { const checkParams: SQLiteValue[] = [chapterId, actId, bookId, plotId, incidentId, userId];
if (e instanceof Error) { existingChapter = db.get(checkQuery, checkParams);
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'information du chapitre.` : `Unable to verify chapter information existence.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence de l'information du chapitre." : 'Unable to verify chapter information existence.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
if (existResult !== null) { if (existingChapter !== null) {
throw new Error(lang === 'fr' ? `Le chapitre est déjà lié.` : `Chapter is already linked.`); throw new Error(lang === 'fr' ? 'Le chapitre est déjà lié.' : 'Chapter is already linked.');
} }
try { try {
const db: Database = System.getDb(); 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()]); 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 (?,?,?,?,?,?,?,?,?,?)';
} catch (e: unknown) { const params: SQLiteValue[] = [chapterInfoId, chapterId, actId, bookId, userId, incidentId, plotId, '', '', System.timeStampInSeconds()];
if (e instanceof Error) { insertResult = db.run(query, params);
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible d'ajouter l'information du chapitre.` : `Unable to add chapter information.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Impossible d'ajouter l'information du chapitre." : 'Unable to add chapter information.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 pendant la liaison du chapitre.` : `Error linking chapter.`); throw new Error(lang === 'fr' ? "Une erreur s'est produite pendant la liaison du chapitre." : 'Error linking chapter.');
} }
return chapterInfoId; 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 { try {
const db: Database = System.getDb(); 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]); const query: string = 'UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=?, last_update=? WHERE author_id=? AND chapter_id=?';
return result.changes > 0; const params: SQLiteValue[] = [encryptedTitle, hashTitle, chapterOrder, lastUpdate, userId, chapterId];
} catch (e: unknown) { const updateResult: RunResult = db.run(query, params);
if (e instanceof Error) { return updateResult.changes > 0;
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de mettre à jour le chapitre.` : `Unable to update chapter.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? 'Impossible de mettre à jour le chapitre.' : 'Unable to update chapter.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 { 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 { try {
const db: Database = System.getDb(); 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 = ?`; let query: 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]; const params: SQLiteValue[] = [summary, goal, lastUpdate, chapterId, actId, bookId];
if (incidentId) { if (incidentId) {
sql += ` AND incident_id=?`; query += ' AND incident_id=?';
params.push(incidentId); params.push(incidentId);
} else { } else {
sql += ` AND incident_id IS NULL`; query += ' AND incident_id IS NULL';
} }
if (plotId) { if (plotId) {
sql += ` AND plot_point_id=?`; query += ' AND plot_point_id=?';
params.push(plotId); params.push(plotId);
} else { } 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); params.push(userId);
const result: RunResult = db.run(sql, params); const updateResult: RunResult = db.run(query, params);
return result.changes > 0; return updateResult.changes > 0;
} catch (e: unknown) { } catch (error: unknown) {
if (e instanceof Error) { if (error instanceof Error) {
console.error(`DB Error: ${e.message}`); console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour les informations du chapitre.` : `Unable to update chapter information.`); 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.");
} }
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 { public static fetchLastChapter(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LastChapterResult | null {
try { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT chapter_id as chapter_id,version FROM user_last_chapter WHERE user_id=? AND book_id=?';
return result; const params: SQLiteValue[] = [userId, bookId];
} catch (e: unknown) { return db.get(query, params) as LastChapterResult | null;
if (e instanceof Error) { } catch (error: unknown) {
console.error(`DB Error: ${e.message}`); if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le dernier chapitre ouvert.` : `Unable to retrieve last opened chapter.`); console.error(`DB Error: ${error.message}`);
} else { 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.");
} }
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 { public static updateLastChapterRecord(userId: string, bookId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); 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]); const updateQuery: string = 'UPDATE user_last_chapter SET chapter_id=?, version=? WHERE user_id=? AND book_id=?';
if (result.changes > 0) { const updateParams: SQLiteValue[] = [chapterId, version, userId, bookId];
const updateResult: RunResult = db.run(updateQuery, updateParams);
if (updateResult.changes > 0) {
return true; 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) { const insertQuery: string = 'INSERT INTO user_last_chapter (user_id, book_id, chapter_id, version) VALUES (?,?,?,?)';
if (e instanceof Error) { const insertParams: SQLiteValue[] = [userId, bookId, chapterId, version];
console.error(`DB Error: ${e.message}`); const insertResult: RunResult = db.run(insertQuery, insertParams);
throw new Error(lang === 'fr' ? `Impossible d'enregistrer le dernier chapitre.` : `Unable to save last chapter.`); return insertResult.changes > 0;
} else { } catch (error: unknown) {
console.error("An unknown error occurred."); if (error instanceof Error) {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [chapterId, userId];
if (e instanceof Error) { return db.all(query, params) as ChapterStoryQueryResult[];
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de récupérer l'histoire du chapitre.` : `Unable to retrieve chapter story.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Impossible de récupérer l'histoire du chapitre." : 'Unable to retrieve chapter story.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 { static deleteChapterInformation(userId: string, chapterInfoId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_chapter_infos WHERE chapter_info_id=? AND author_id=?', [chapterInfoId, userId]); const query: string = 'DELETE FROM book_chapter_infos WHERE chapter_info_id=? AND author_id=?';
return result.changes > 0; const params: SQLiteValue[] = [chapterInfoId, userId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de supprimer les informations du chapitre.` : `Unable to delete chapter information.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? 'Impossible de supprimer les informations du chapitre.' : 'Unable to delete chapter information.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM book_chapters WHERE chapter_id=? AND author_id=?';
return result !== null; const params: SQLiteValue[] = [chapterId, userId];
} catch (e: unknown) { const chapter: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return chapter !== null;
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du chapitre.` : `Unable to check chapter existence.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence du chapitre." : 'Unable to check chapter existence.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM book_chapter_infos WHERE chapter_id=? AND author_id=?';
return result !== null; const params: SQLiteValue[] = [chapterId, userId];
} catch (e: unknown) { const chapterInfo: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return chapterInfo !== null;
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence des informations du chapitre.` : `Unable to check chapter info existence.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence des informations du chapitre." : 'Unable to check chapter info existence.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 { try {
const db: Database = System.getDb(); 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[]; 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';
} catch (e: unknown) { const params: SQLiteValue[] = [bookId];
if (e instanceof Error) { chapters = db.all(query, params) as ChapterBookResult[];
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres.` : `Unable to retrieve chapters.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); 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.");
} }
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) { if (chapters.length === 0) {
throw new Error(lang === 'fr' ? `Aucun chapitre trouvé.` : `No chapters found.`); 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<BookChaptersTable[]> { static async fetchBookChapters(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookChaptersTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { return db.all(query, params) as BookChaptersTable[];
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres.` : `Unable to retrieve chapters.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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<BookChapterInfosTable[]> { static async fetchBookChapterInfos(userId: string, chapterId: string, lang: 'fr' | 'en'): Promise<BookChapterInfosTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, chapterId];
if (e instanceof Error) { return db.all(query, params) as BookChapterInfosTable[];
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de récupérer les infos des chapitres.` : `Unable to retrieve chapter infos.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedChapters(userId: string, lang: 'fr' | 'en'): SyncedChapterResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT chapter_id, book_id, title, last_update FROM book_chapters WHERE author_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { return db.all(query, params) as SyncedChapterResult[];
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres synchronisés.` : `Unable to retrieve synced chapters.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres synchronisés.' : 'Unable to retrieve synced chapters.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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[] { static fetchSyncedChapterInfos(userId: string, lang: 'fr' | 'en'): SyncedChapterInfoResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT chapter_info_id, chapter_id, book_id, last_update FROM book_chapter_infos WHERE author_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { return db.all(query, params) as SyncedChapterInfoResult[];
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible de récupérer les infos des chapitres synchronisés.` : `Unable to retrieve synced chapter infos.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? 'Impossible de récupérer les infos des chapitres synchronisés.' : 'Unable to retrieve synced chapter infos.');
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
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 { 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = 'INSERT INTO book_chapters (chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?)';
`INSERT INTO book_chapters (chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update) const params: SQLiteValue[] = [chapterId, bookId, authorId, title, hashedTitle, wordsCount, chapterOrder, lastUpdate];
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, const insertResult: RunResult = db.run(query, params);
[chapterId, bookId, authorId, title, hashedTitle, wordsCount, chapterOrder, lastUpdate] return insertResult.changes > 0;
); } catch (error: unknown) {
return result.changes > 0; if (error instanceof Error) {
} catch (e: unknown) { console.error(`DB Error: ${error.message}`);
if (e instanceof Error) { throw new Error(lang === 'fr' ? "Impossible d'insérer le chapitre." : 'Unable to insert chapter.');
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.");
} }
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 { 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 { try {
const db: Database = System.getDb(); 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]); 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
return result.changes > 0; const params: SQLiteValue[] = [chapterInfoId, chapterId, actId, incidentId, plotPointId, bookId, authorId, summary, goal, lastUpdate];
} catch (e: unknown) { const insertResult: RunResult = db.run(query, params);
if (e instanceof Error) { return insertResult.changes > 0;
console.error(`DB Error: ${e.message}`); } catch (error: unknown) {
throw new Error(lang === 'fr' ? `Impossible d'insérer les infos du chapitre.` : `Unable to insert chapter info.`); if (error instanceof Error) {
} else { console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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<BookChaptersTable[]> { /**
* 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<BookChaptersTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( 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 = ?';
`SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update const params: SQLiteValue[] = [chapterId];
FROM book_chapters return db.all(query, params) as BookChaptersTable[];
WHERE chapter_id = ?`, } catch (error: unknown) {
[id] if (error instanceof Error) {
) as BookChaptersTable[]; throw new Error(lang === 'fr' ? 'Impossible de récupérer le chapitre complet.' : 'Unable to retrieve complete chapter.');
} 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.");
} }
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
static async fetchCompleteChapterInfoById(id: string, lang: "fr" | "en"):Promise<BookChapterInfosTable[]> { /**
* 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<BookChapterInfosTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( 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 = ?';
`SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update const params: SQLiteValue[] = [chapterInfoId];
FROM book_chapter_infos return db.all(query, params) as BookChapterInfosTable[];
WHERE chapter_info_id = ?`, } catch (error: unknown) {
[id] if (error instanceof Error) {
) as BookChapterInfosTable[]; throw new Error(lang === 'fr' ? 'Impossible de récupérer les informations de chapitre complètes.' : 'Unable to retrieve complete chapter info.');
} 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.");
} }
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }

View File

@@ -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"; import System from "@/electron/database/System";
export interface ChapterContentQueryResult extends Record<string, SQLiteValue>{ export interface ChapterContentQueryResult extends Record<string, SQLiteValue> {
chapter_id: string; chapter_id: string;
version: number; version: number;
content: string; content: string;
@@ -14,7 +14,7 @@ export interface ContentQueryResult extends Record<string, SQLiteValue> {
content: string; content: string;
} }
export interface CompanionContentQueryResult extends Record<string, SQLiteValue>{ export interface CompanionContentQueryResult extends Record<string, SQLiteValue> {
version: number; version: number;
content: string; content: string;
words_count: number; words_count: number;
@@ -38,14 +38,36 @@ export interface SyncedChapterContentResult extends Record<string, SQLiteValue>
} }
export default class ChapterContentRepository { 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[] { public static fetchLastChapterContent(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterContentQueryResult[] {
try { try {
const db: Database = System.getDb(); 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`; const query: string = `
return db.all(query, [userId, bookId]) as ChapterContentQueryResult[]; SELECT
} catch (e: unknown) { book_chapters.chapter_id as chapter_id,
if (e instanceof Error) { COALESCE(book_chapter_content.version, 2) AS version,
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le dernier chapitre.` : `Unable to retrieve last chapter.`);
} else { } else {
console.error("An unknown error occurred."); 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 { public static updateChapterContent(userId: string, chapterId: string, version: number, encryptContent: string, wordsCount: number, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); 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]); const updateQuery: string = 'UPDATE book_chapter_content SET content=?, words_count=?, last_update=? WHERE chapter_id=? AND author_id=? AND version=?';
if (result.changes > 0) { const updateParams: SQLiteValue[] = [encryptContent, wordsCount, lastUpdate, chapterId, userId, version];
const updateResult: RunResult = db.run(updateQuery, updateParams);
if (updateResult.changes > 0) {
return true; return true;
} else { } else {
const contentId:string = System.createUniqueId(); 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 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; return insertResult.changes > 0;
} }
} catch (e: unknown) { } catch (error: unknown) {
if (e instanceof Error) { if (error instanceof Error) {
console.error(`DB Error: ${e.message}`); console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour le contenu du chapitre.` : `Unable to update chapter content.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le contenu du chapitre.` : `Unable to update chapter content.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT version, content, words_count FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, chapterId, version];
if (e instanceof Error) { const companionContents: CompanionContentQueryResult[] = db.all(query, params) as CompanionContentQueryResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu compagnon.` : `Unable to retrieve companion content.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static fetchChapterContentByChapterOrder(userId: string, chapterOrder: number, bookId: string, lang: 'fr' | 'en' = 'fr'): ContentQueryResult {
let result: ContentQueryResult | null; let chapterContent: ContentQueryResult | null;
try { try {
const db: Database = System.getDb(); 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; const query: string = `
} catch (e: unknown) { SELECT content.content
if (e instanceof Error) { FROM book_chapters as chapter
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu du chapitre.` : `Unable to retrieve chapter content.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT content FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, chapterId, version];
if (e instanceof Error) { chapterContent = db.get(query, params) as ContentQueryResult | null;
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 récupérer le contenu du chapitre.` : `Unable to retrieve chapter content.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu du chapitre.` : `Unable to retrieve chapter content.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM `book_chapter_content` WHERE `content_id`=? AND `author_id`=?';
return result !== null; const params: SQLiteValue[] = [contentId, userId];
} catch (e: unknown) { const existenceCheck: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return existenceCheck !== null;
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 vérifier l'existence du contenu du chapitre.` : `Unable to check chapter content existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du contenu du chapitre.` : `Unable to check chapter content existence.`);
} else { } else {
console.error("An unknown error occurred."); 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<BookChapterContentTable[]> {
/**
* 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<BookChapterContentTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, chapterId];
if (e instanceof Error) { const bookChapterContents: BookChapterContentTable[] = db.all(query, params) as BookChapterContentTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu des chapitres.` : `Unable to retrieve chapter contents.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedChapterContents(userId: string, lang: 'fr' | 'en'): SyncedChapterContentResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT content_id, chapter_id, last_update FROM book_chapter_content WHERE author_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedChapterContents: SyncedChapterContentResult[] = db.all(query, params) as SyncedChapterContentResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu des chapitres synchronisés.` : `Unable to retrieve synced chapter contents.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static insertSyncChapterContent(contentId: string, chapterId: string, authorId: string, version: number, content: string | null, wordsCount: number, timeOnIt: number, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = `
`INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update) INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?, ?)
[contentId, chapterId, authorId, version, content, wordsCount, timeOnIt, lastUpdate] `;
); const params: SQLiteValue[] = [contentId, chapterId, authorId, version, content, wordsCount, timeOnIt, lastUpdate];
return result.changes > 0; const insertResult: RunResult = db.run(query, params);
} catch (e: unknown) { return insertResult.changes > 0;
if (e instanceof Error) { } catch (error: unknown) {
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible d'insérer le contenu du chapitre.` : `Unable to insert chapter content.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteChapterContentById(id: string, lang: "fr" | "en"):Promise<BookChapterContentTable[]> {
/**
* 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<BookChapterContentTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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 = ?';
} catch (e:unknown){ const params: SQLiteValue[] = [contentId];
if (e instanceof Error) { 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu de chapitre complet.` : `Unable to retrieve complete chapter content.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { public static fetchWholeChapter(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): ChapterContentQueryResult {
let result: ChapterContentQueryResult | null; let wholeChapter: ChapterContentQueryResult | null;
try { try {
const db: Database = System.getDb(); 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 = ?'; const query: string = `
result = db.get(query, [version, chapterId, userId]) as ChapterContentQueryResult | null; SELECT
} catch (e: unknown) { chapter.chapter_id as chapter_id,
if (e instanceof Error) { chapter.title as title,
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le chapitre.` : `Unable to retrieve chapter.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cet ID.` : `No chapter found with this ID.`);
} }
return result; return wholeChapter;
} }
} }

View File

@@ -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"; import System from "../System.js";
export interface BookCharactersTable extends Record<string, SQLiteValue> { export interface BookCharactersTable extends Record<string, SQLiteValue> {
@@ -71,71 +71,125 @@ export interface CompleteCharacterResult extends Record<string, SQLiteValue> {
} }
export default class CharacterRepo { 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[] { public static fetchCharacters(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): CharacterResult[] {
let result: CharacterResult[];
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [bookId, userId];
if (e instanceof Error) { const characters: CharacterResult[] = db.all(query, params) as CharacterResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages.` : `Unable to retrieve characters.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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 { 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 { try {
const db: Database = System.getDb(); 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()]); 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 (?,?,?,?,?,?,?,?,?,?,?,?)';
} catch (e: unknown) { const params: SQLiteValue[] = [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds()];
if (e instanceof Error) { const insertResult: RunResult = db.run(query, params);
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le personnage.` : `Unable to add character.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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 { static insertAttribute(attributeId: string, characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
try { try {
const db: Database = System.getDb(); 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()]); const query: string = 'INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?,?,?,?,?,?)';
} catch (e: unknown) { const params: SQLiteValue[] = [attributeId, characterId, userId, type, name, System.timeStampInSeconds()];
if (e instanceof Error) { const insertResult: RunResult = db.run(query, params);
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter l'attribut.` : `Unable to add attribute.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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 { 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 { try {
const db: Database = System.getDb(); 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]); 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`=?';
return result.changes > 0; const params: SQLiteValue[] = [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, lastUpdate, id, userId];
} catch (e: unknown) { const updateResult: RunResult = db.run(query, params);
if (e instanceof Error) { return updateResult.changes > 0;
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 personnage.` : `Unable to update character.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le personnage.` : `Unable to update character.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static deleteAttribute(userId: string, attributeId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?', [attributeId, userId]); const query: string = 'DELETE FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?';
return result.changes > 0; const params: SQLiteValue[] = [attributeId, userId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
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 supprimer l'attribut.` : `Unable to delete attribute.`); throw new Error(lang === 'fr' ? `Impossible de supprimer l'attribut.` : `Unable to delete attribute.`);
} else { } else {
console.error("An unknown error occurred."); 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[] { static fetchAttributes(characterId: string, userId: string, lang: 'fr' | 'en' = 'fr'): AttributeResult[] {
let result: AttributeResult[];
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT attr_id, attribute_name, attribute_value FROM book_characters_attributes WHERE character_id=? AND user_id=?';
} catch (e: unknown) { const params: SQLiteValue[] = [characterId, userId];
if (e instanceof Error) { const attributes: AttributeResult[] = db.all(query, params) as AttributeResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs.` : `Unable to retrieve attributes.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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[] { static fetchCompleteCharacters(userId: string, bookId: string, tags: string[], lang: 'fr' | 'en' = 'fr'): CompleteCharacterResult[] {
let result: CompleteCharacterResult[];
try { try {
const db: Database = System.getDb(); 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 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) { if (tags && tags.length > 0) {
const placeholders: string = tags.map((): string => '?').join(','); const placeholders: string = tags.map((): string => '?').join(',');
query += ` AND charac.character_id IN (${placeholders})`; query += ` AND charac.character_id IN (${placeholders})`;
values.push(...tags); params.push(...tags);
} }
result = db.all(query, values) as CompleteCharacterResult[]; const characters: CompleteCharacterResult[] = db.all(query, params) as CompleteCharacterResult[];
} catch (e: unknown) { if (characters.length === 0) {
if (e instanceof Error) { throw new Error(lang === 'fr' ? `Aucun personnage complet trouvé.` : `No complete characters found.`);
console.error(`DB Error: ${e.message}`); }
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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages complets.` : `Unable to retrieve complete characters.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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 { try {
const db: Database = System.getDb(); 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]); 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(?)';
return result.changes > 0; const params: SQLiteValue[] = [attributeName, attributeValue, lastUpdate, characterAttributeId, userId];
} catch (e: unknown) { const updateResult: RunResult = db.run(query, params);
if (e instanceof Error) { return updateResult.changes > 0;
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 l'attribut du personnage.` : `Unable to update character attribute.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'attribut du personnage.` : `Unable to update character attribute.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM `book_characters` WHERE `character_id`=? AND `user_id`=?';
return result !== null; const params: SQLiteValue[] = [characterId, userId];
} catch (e: unknown) { const character: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return character !== null;
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 vérifier l'existence du personnage.` : `Unable to check character existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du personnage.` : `Unable to check character existence.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?';
return result !== null; const params: SQLiteValue[] = [characterAttributeId, userId];
} catch (e: unknown) { const attribute: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return attribute !== null;
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 vérifier l'existence de l'attribut du personnage.` : `Unable to check character attribute existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'attribut du personnage.` : `Unable to check character attribute existence.`);
} else { } else {
console.error("An unknown error occurred."); 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<BookCharactersTable[]> { static async fetchBookCharacters(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookCharactersTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const characters: BookCharactersTable[] = db.all(query, params) as BookCharactersTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages.` : `Unable to retrieve characters.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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<BookCharactersAttributesTable[]> { /**
* 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<BookCharactersAttributesTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, characterId];
if (e instanceof Error) { const attributes: BookCharactersAttributesTable[] = db.all(query, params) as BookCharactersAttributesTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs des personnages.` : `Unable to retrieve character attributes.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedCharacters(userId: string, lang: 'fr' | 'en'): SyncedCharacterResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT character_id, book_id, first_name, last_update FROM book_characters WHERE user_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedCharacters: SyncedCharacterResult[] = db.all(query, params) as SyncedCharacterResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages synchronisés.` : `Unable to retrieve synced characters.`);
} else { } else {
console.error("An unknown error occurred."); 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[] { static fetchSyncedCharacterAttributes(userId: string, lang: 'fr' | 'en'): SyncedCharacterAttributeResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT attr_id, character_id, attribute_name, last_update FROM book_characters_attributes WHERE user_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedAttributes: SyncedCharacterAttributeResult[] = db.all(query, params) as SyncedCharacterAttributeResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs des personnages synchronisés.` : `Unable to retrieve synced character attributes.`);
} else { } else {
console.error("An unknown error occurred."); 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 { 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( 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)
`INSERT INTO book_characters (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, const params: SQLiteValue[] = [characterId, bookId, userId, firstName, lastName, category, title, image, role, biography, history, lastUpdate];
[characterId, bookId, userId, firstName, lastName, category, title, image, role, biography, history, lastUpdate] const insertResult: RunResult = db.run(query, params);
); return insertResult.changes > 0;
return result.changes > 0; } catch (error: unknown) {
} catch (e: unknown) { if (error instanceof Error) {
if (e instanceof Error) { console.error(`DB Error: ${error.message}`);
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le personnage.` : `Unable to insert character.`); throw new Error(lang === 'fr' ? `Impossible d'insérer le personnage.` : `Unable to insert character.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { static insertSyncCharacterAttribute(attrId: string, characterId: string, userId: string, attributeName: string, attributeValue: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = `INSERT INTO book_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update)
`INSERT INTO book_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?, ?, ?, ?, ?, ?)`;
VALUES (?, ?, ?, ?, ?, ?)`, const params: SQLiteValue[] = [attrId, characterId, userId, attributeName, attributeValue, lastUpdate];
[attrId, characterId, userId, attributeName, attributeValue, lastUpdate] const insertResult: RunResult = db.run(query, params);
); return insertResult.changes > 0;
return result.changes > 0; } catch (error: unknown) {
} catch (e: unknown) { if (error instanceof Error) {
if (e instanceof Error) { console.error(`DB Error: ${error.message}`);
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer l'attribut du personnage.` : `Unable to insert character attribute.`); throw new Error(lang === 'fr' ? `Impossible d'insérer l'attribut du personnage.` : `Unable to insert character attribute.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteCharacterById(id: string, lang: "fr" | "en"):Promise<BookCharactersTable[]> {
/**
* 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<BookCharactersTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( const query: string = `SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update
`SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update
FROM book_characters FROM book_characters
WHERE character_id = ?`, WHERE character_id = ?`;
[id] const params: SQLiteValue[] = [id];
) as BookCharactersTable[]; const character: BookCharactersTable[] = db.all(query, params) as BookCharactersTable[];
} catch (e:unknown){ return character;
if (e instanceof Error) { } 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le personnage complet.` : `Unable to retrieve complete character.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteCharacterAttributeById(id: string, lang: "fr" | "en"):Promise<BookCharactersAttributesTable[]> { /**
* 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<BookCharactersAttributesTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( const query: string = `SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update
`SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update
FROM book_characters_attributes FROM book_characters_attributes
WHERE attr_id = ?`, WHERE attr_id = ?`;
[id] const params: SQLiteValue[] = [id];
) as BookCharactersAttributesTable[]; const attribute: BookCharactersAttributesTable[] = db.all(query, params) as BookCharactersAttributesTable[];
} catch (e:unknown){ return attribute;
if (e instanceof Error) { } 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer l'attribut de personnage complet.` : `Unable to retrieve complete character attribute.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");

View File

@@ -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 System from "@/electron/database/System";
import {ChapterBookResult, EritBooksTable} from "@/electron/database/repositories/book.repository";
export interface BookAIGuideLineTable extends Record<string, SQLiteValue> { export interface BookAIGuideLineTable extends Record<string, SQLiteValue> {
user_id: string; user_id: string;
@@ -72,35 +71,111 @@ export interface GuideLineAIQuery extends Record<string, SQLiteValue> {
} }
export default class GuidelineRepo { 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[] { public static fetchGuideLine(userId: string, bookId: string, lang: 'fr' | 'en'): GuideLineQuery[] {
let result: GuideLineQuery[]; let guidelines: GuideLineQuery[];
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
result = db.all('SELECT * FROM book_guide_line WHERE book_id=? AND user_id=?', [bookId, userId]) as GuideLineQuery[]; const query: string = 'SELECT * FROM book_guide_line WHERE book_id=? AND user_id=?';
} catch (e: unknown) { const params: SQLiteValue[] = [bookId, userId];
if (e instanceof Error) { guidelines = db.all(query, params) as GuideLineQuery[];
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 récupérer la ligne directrice.` : `Unable to retrieve guideline.`); throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice.` : `Unable to retrieve guideline.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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 { try {
const db: Database = System.getDb(); 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]); 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=?';
if (result.changes > 0) { 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; return true;
} else { } 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()]); 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 (?,?,?,?,?,?,?,?,?,?,?,?,?)';
return insert.changes > 0; 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) { } catch (error: unknown) {
if (e instanceof Error) { if (error instanceof Error) {
console.error(`DB Error: ${e.message}`); console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour la ligne directrice.` : `Unable to update guideline.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour la ligne directrice.` : `Unable to update guideline.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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]); 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=?';
if (result.changes > 0) { 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; return true;
} else { } 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()]); 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 (?,?,?,?,?,?,?,?,?,?,?,?)';
return result.changes > 0; 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) { } catch (error: unknown) {
if (e instanceof Error) { if (error instanceof Error) {
console.error(`DB Error: ${e.message}`); console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice IA.` : `Unable to insert AI guideline.`); throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice IA.` : `Unable to insert AI guideline.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static fetchGuideLineAI(userId: string, bookId: string, lang: 'fr' | 'en'): GuideLineAIQuery {
let result: GuideLineAIQuery | null; let aiGuideline: GuideLineAIQuery | null;
try { try {
const db: Database = System.getDb(); 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; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { aiGuideline = db.get(query, params) as GuideLineAIQuery | null;
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 récupérer la ligne directrice IA.` : `Unable to retrieve AI guideline.`); throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice IA.` : `Unable to retrieve AI guideline.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); 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<BookAIGuideLineTable[]> { static async fetchBookAIGuideLine(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookAIGuideLineTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const aiGuidelines: BookAIGuideLineTable[] = db.all(query, params) as BookAIGuideLineTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice IA.` : `Unable to retrieve AI guideline.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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<BookGuideLineTable[]> { static async fetchBookGuideLineTable(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookGuideLineTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const guidelines: BookGuideLineTable[] = db.all(query, params) as BookGuideLineTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice.` : `Unable to retrieve guideline.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedGuideLine(userId: string, lang: 'fr' | 'en'): SyncedGuideLineResult[] {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all('SELECT book_id, last_update FROM book_guide_line WHERE user_id = ?', [userId]) as SyncedGuideLineResult[]; const query: string = 'SELECT book_id, last_update FROM book_guide_line WHERE user_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedGuidelines: SyncedGuideLineResult[] = db.all(query, params) as SyncedGuideLineResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lignes directrices synchronisées.` : `Unable to retrieve synced guidelines.`);
} else { } else {
console.error("An unknown error occurred."); 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[] { static fetchSyncedAIGuideLine(userId: string, lang: 'fr' | 'en'): SyncedAIGuideLineResult[] {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all('SELECT book_id, last_update FROM book_ai_guide_line WHERE user_id = ?', [userId]) as SyncedAIGuideLineResult[]; const query: string = 'SELECT book_id, last_update FROM book_ai_guide_line WHERE user_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedAIGuidelines: SyncedAIGuideLineResult[] = db.all(query, params) as SyncedAIGuideLineResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lignes directrices IA synchronisées.` : `Unable to retrieve synced AI guidelines.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( 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)
`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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, const params: SQLiteValue[] = [
[userId, bookId, globalResume, themes, verbeTense, narrativeType, langue, dialogueType, tone, atmosphere, currentResume, lastUpdate] userId,
); bookId,
return result.changes > 0; globalResume,
} catch (e: unknown) { themes,
if (e instanceof Error) { verbeTense,
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice IA.` : `Unable to insert AI guideline.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( 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)
`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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, const params: SQLiteValue[] = [
[userId, bookId, tone, atmosphere, writingStyle, themes, symbolism, motifs, narrativeVoice, pacing, intendedAudience, keyMessages, lastUpdate] userId,
); bookId,
return result.changes > 0; tone,
} catch (e: unknown) { atmosphere,
if (e instanceof Error) { writingStyle,
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice.` : `Unable to insert guideline.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
} }

View File

@@ -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"; import System from "@/electron/database/System";
export interface BookIncidentsTable extends Record<string, SQLiteValue> { export interface BookIncidentsTable extends Record<string, SQLiteValue> {
@@ -25,13 +25,24 @@ export interface IncidentQuery extends Record<string, SQLiteValue> {
} }
export default class IncidentRepository { 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 { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT incident_id, title, summary FROM book_incidents WHERE author_id=? AND book_id=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const incidents: IncidentQuery[] = db.all(query, params) as IncidentQuery[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les incidents.` : `Unable to retrieve incidents.`);
} else { } else {
console.error("An unknown error occurred."); 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 { public static insertNewIncident(incidentId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string {
let result: RunResult; let insertResult: RunResult;
try { try {
const db: Database = System.getDb(); 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()]); const query: string = 'INSERT INTO book_incidents (incident_id,author_id, book_id, title, hashed_title, last_update) VALUES (?,?,?,?,?,?)';
} catch (e: unknown) { const params: SQLiteValue[] = [incidentId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()];
if (e instanceof Error) { insertResult = db.run(query, params);
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'ajouter l'élément déclencheur.` : `Unable to add incident.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter l'élément déclencheur.` : `Unable to add incident.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); 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; 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 { public static deleteIncident(userId: string, bookId: string, incidentId: string, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); 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]); const query: string = 'DELETE FROM book_incidents WHERE author_id=? AND book_id=? AND incident_id=?';
return result.changes > 0; const params: SQLiteValue[] = [userId, bookId, incidentId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
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 supprimer l'élément déclencheur.` : `Unable to delete incident.`); throw new Error(lang === 'fr' ? `Impossible de supprimer l'élément déclencheur.` : `Unable to delete incident.`);
} else { } else {
console.error("An unknown error occurred."); 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 { public static updateIncident(userId: string, bookId: string, incidentId: string, encryptedIncidentName: string, incidentHashedName: string, incidentSummary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); 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]); const query: string = 'UPDATE book_incidents SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND incident_id=?';
return result.changes > 0; const params: SQLiteValue[] = [encryptedIncidentName, incidentHashedName, incidentSummary, lastUpdate, userId, bookId, incidentId];
} catch (e: unknown) { const updateResult: RunResult = db.run(query, params);
if (e instanceof Error) { return updateResult.changes > 0;
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 l'incident.` : `Unable to update incident.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'incident.` : `Unable to update incident.`);
} else { } else {
console.error("An unknown error occurred."); 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<BookIncidentsTable[]> { static async fetchBookIncidents(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookIncidentsTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const incidents: BookIncidentsTable[] = db.all(query, params) as BookIncidentsTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les incidents.` : `Unable to retrieve incidents.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedIncidents(userId: string, lang: 'fr' | 'en'): SyncedIncidentResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT incident_id, book_id, title, last_update FROM book_incidents WHERE author_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedIncidents: SyncedIncidentResult[] = db.all(query, params) as SyncedIncidentResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les incidents synchronisés.` : `Unable to retrieve synced incidents.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static insertSyncIncident(incidentId: string, authorId: string, bookId: string, title: string, hashedTitle: string, summary: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = `INSERT INTO book_incidents (incident_id, author_id, book_id, title, hashed_title, summary, last_update)
`INSERT INTO book_incidents (incident_id, author_id, book_id, title, hashed_title, summary, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)`;
VALUES (?, ?, ?, ?, ?, ?, ?)`, const params: SQLiteValue[] = [incidentId, authorId, bookId, title, hashedTitle, summary, lastUpdate];
[incidentId, authorId, bookId, title, hashedTitle, summary, lastUpdate] const insertResult: RunResult = db.run(query, params);
); return insertResult.changes > 0;
return result.changes > 0; } catch (error: unknown) {
} catch (e: unknown) { if (error instanceof Error) {
if (e instanceof Error) { console.error(`DB Error: ${error.message}`);
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer l'incident.` : `Unable to insert incident.`); throw new Error(lang === 'fr' ? `Impossible d'insérer l'incident.` : `Unable to insert incident.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteIncidentById(id: string, lang: "fr" | "en"):Promise<BookIncidentsTable[]> {
/**
* 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<BookIncidentsTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( const query: string = `SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update
`SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update
FROM book_incidents FROM book_incidents
WHERE incident_id = ?`, WHERE incident_id = ?`;
[id] const params: SQLiteValue[] = [id];
) as BookIncidentsTable[]; const incident: BookIncidentsTable[] = db.all(query, params) as BookIncidentsTable[];
} catch (e:unknown){ return incident;
if (e instanceof Error) { } 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer l'incident complet.` : `Unable to retrieve complete incident.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM book_incidents WHERE book_id=? AND incident_id=? AND author_id=?';
return result !== null; const params: SQLiteValue[] = [bookId, incidentId, userId];
} catch (e: unknown) { const existingIncident: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return existingIncident !== null;
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 vérifier l'existence de l'incident.` : `Unable to check incident existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'incident.` : `Unable to check incident existence.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
} }

View File

@@ -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"; import System from "@/electron/database/System";
export interface BookIssuesTable extends Record<string, SQLiteValue> { export interface BookIssuesTable extends Record<string, SQLiteValue> {
@@ -23,13 +23,23 @@ export interface IssueQuery extends Record<string, SQLiteValue> {
} }
export default class IssueRepository { 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 { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT issue_id, name FROM book_issues WHERE author_id=? AND book_id=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const issues: IssueQuery[] = db.all(query, params) as IssueQuery[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les problématiques.` : `Unable to retrieve issues.`);
} else { } else {
console.error("An unknown error occurred."); 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 { 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; let insertResult: RunResult;
try { try {
const db: Database = System.getDb(); 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]); const checkQuery: string = 'SELECT issue_id FROM book_issues WHERE hashed_issue_name=? AND book_id=? AND author_id=?';
} catch (e: unknown) { const checkParams: SQLiteValue[] = [hashedName, bookId, userId];
if (e instanceof Error) { existingIssue = db.get(checkQuery, checkParams);
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 vérifier l'existence de la problématique.` : `Unable to verify issue existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de la problématique.` : `Unable to verify issue existence.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); throw new Error(lang === 'fr' ? `La problématique existe déjà.` : `This issue already exists.`);
} }
try { try {
const db: Database = System.getDb(); 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()]); const insertQuery: string = 'INSERT INTO book_issues (issue_id, author_id, book_id, name, hashed_issue_name, last_update) VALUES (?, ?, ?, ?, ?, ?)';
} catch (e: unknown) { const insertParams: SQLiteValue[] = [issueId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()];
if (e instanceof Error) { insertResult = db.run(insertQuery, insertParams);
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'ajouter la problématique.` : `Unable to add issue.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter la problématique.` : `Unable to add issue.`);
} else { } else {
console.error("An unknown error occurred."); console.error("An unknown error occurred.");
@@ -72,15 +97,24 @@ export default class IssueRepository {
} }
return issueId; 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 { public static deleteIssue(userId: string, issueId: string, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_issues WHERE author_id=? AND issue_id=?', [userId, issueId]); const query: string = 'DELETE FROM book_issues WHERE author_id=? AND issue_id=?';
return result.changes > 0; const params: SQLiteValue[] = [userId, issueId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
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 supprimer la problématique.` : `Unable to delete issue.`); throw new Error(lang === 'fr' ? `Impossible de supprimer la problématique.` : `Unable to delete issue.`);
} else { } else {
console.error("An unknown error occurred."); 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<BookIssuesTable[]> { static async fetchBookIssues(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookIssuesTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const issues: BookIssuesTable[] = db.all(query, params) as BookIssuesTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les problématiques.` : `Unable to retrieve issues.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedIssues(userId: string, lang: 'fr' | 'en'): SyncedIssueResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT issue_id, book_id, name, last_update FROM book_issues WHERE author_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedIssues: SyncedIssueResult[] = db.all(query, params) as SyncedIssueResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les problématiques synchronisées.` : `Unable to retrieve synced issues.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static insertSyncIssue(issueId: string, authorId: string, bookId: string, name: string, hashedIssueName: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = `INSERT INTO book_issues (issue_id, author_id, book_id, name, hashed_issue_name, last_update) VALUES (?, ?, ?, ?, ?, ?)`;
`INSERT INTO book_issues (issue_id, author_id, book_id, name, hashed_issue_name, last_update) const params: SQLiteValue[] = [issueId, authorId, bookId, name, hashedIssueName, lastUpdate];
VALUES (?, ?, ?, ?, ?, ?)`, const insertResult: RunResult = db.run(query, params);
[issueId, authorId, bookId, name, hashedIssueName, lastUpdate] return insertResult.changes > 0;
); } catch (error: unknown) {
return result.changes > 0; if (error instanceof Error) {
} catch (e: unknown) { console.error(`DB Error: ${error.message}`);
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer la problématique.` : `Unable to insert issue.`); throw new Error(lang === 'fr' ? `Impossible d'insérer la problématique.` : `Unable to insert issue.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteIssueById(id: string, lang: "fr" | "en"):Promise<BookIssuesTable[]> {
/**
* 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<BookIssuesTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( const query: string = `SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update FROM book_issues WHERE issue_id = ?`;
`SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update const params: SQLiteValue[] = [id];
FROM book_issues const issues: BookIssuesTable[] = db.all(query, params) as BookIssuesTable[];
WHERE issue_id = ?`, return issues;
[id] } catch (error: unknown) {
) as BookIssuesTable[]; if (error instanceof Error) {
} catch (e:unknown){
if (e instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le problème complet.` : `Unable to retrieve complete issue.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le problème complet.` : `Unable to retrieve complete issue.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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 query: string = `UPDATE book_issues SET name = ?, hashed_issue_name = ?, last_update = ? WHERE issue_id = ? AND author_id = ? AND book_id = ?`;
const params:(string|number)[] = [name, hashedName, lastUpdate, issueId, userId, bookId]; const params: SQLiteValue[] = [name, hashedName, lastUpdate, issueId, userId, bookId];
const result:RunResult = db.run(query, params); const updateResult: RunResult = db.run(query, params);
return result.changes > 0; return updateResult.changes > 0;
} catch (e:unknown) { } catch (error: unknown) {
if (e instanceof Error) { if (error instanceof Error) {
console.error(`DB Error: ${e.message}`); console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour la problématique.` : `Unable to update issue.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour la problématique.` : `Unable to update issue.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM book_issues WHERE issue_id=? AND author_id=? AND book_id=?';
return result !== null; const params: SQLiteValue[] = [issueId, userId, bookId];
} catch (e: unknown) { const existingIssue: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return existingIssue !== null;
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 vérifier l'existence du problème.` : `Unable to check issue existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du problème.` : `Unable to check issue existence.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
} }

View File

@@ -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"; import System from "../System.js";
export interface LocationQueryResult extends Record<string, SQLiteValue> { export interface LocationQueryResult extends Record<string, SQLiteValue> {
@@ -79,92 +79,166 @@ export interface SyncedLocationSubElementResult extends Record<string, SQLiteVal
} }
export default class LocationRepo { export default class LocationRepo {
/**
* Retrieves all locations with their elements and sub-elements 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 An array of location query results with nested elements
*/
static getLocation(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationQueryResult[] { static getLocation(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationQueryResult[] {
let result: LocationQueryResult[];
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const query = 'SELECT loc_id, loc_name, element.element_id AS element_id, element.element_name, element.element_description, sub_elem.sub_element_id AS sub_element_id, sub_elem.sub_elem_name, sub_elem.sub_elem_description FROM book_location AS location LEFT JOIN location_element AS element ON location.loc_id=element.location LEFT JOIN location_sub_element AS sub_elem ON element.element_id=sub_elem.element_id WHERE location.user_id=? AND location.book_id=?'; const query: string = `
result = db.all(query, [userId, bookId]) as LocationQueryResult[]; SELECT loc_id, loc_name, element.element_id AS element_id, element.element_name,
} catch (e: unknown) { element.element_description, sub_elem.sub_element_id AS sub_element_id,
if (e instanceof Error) { sub_elem.sub_elem_name, sub_elem.sub_elem_description
console.error(`DB Error: ${e.message}`); FROM book_location AS location
LEFT JOIN location_element AS element ON location.loc_id = element.location
LEFT JOIN location_sub_element AS sub_elem ON element.element_id = sub_elem.element_id
WHERE location.user_id = ? AND location.book_id = ?
`;
const params: SQLiteValue[] = [userId, bookId];
const locations: LocationQueryResult[] = db.all(query, params) as LocationQueryResult[];
return locations;
} 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.` : `Unable to retrieve locations.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les emplacements.` : `Unable to retrieve locations.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
return result;
} }
/**
* Inserts a new location section for a book.
* @param userId - The user's unique identifier
* @param locationId - The new location's unique identifier
* @param bookId - The book's unique identifier
* @param encryptedName - The encrypted location name
* @param originalName - The original (unencrypted) location name
* @param lang - The language for error messages ('fr' or 'en')
* @returns The location ID if insertion was successful
*/
static insertLocation(userId: string, locationId: string, bookId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string { static insertLocation(userId: string, locationId: string, bookId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
result = db.run('INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) VALUES (?, ?, ?, ?, ?, ?)', [locationId, bookId, userId, encryptedName, originalName, System.timeStampInSeconds()]); const query: string = `
} catch (e: unknown) { INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update)
if (e instanceof Error) { VALUES (?, ?, ?, ?, ?, ?)
console.error(`DB Error: ${e.message}`); `;
const params: SQLiteValue[] = [locationId, bookId, userId, encryptedName, originalName, 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 la section d'emplacement.` : `Error adding location section.`);
}
return locationId;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter la section d'emplacement.` : `Unable to add location section.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter la section d'emplacement.` : `Unable to add location section.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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 la section d'emplacement.` : `Error adding location section.`);
}
return locationId;
} }
/**
* Inserts a new location element within a location section.
* @param userId - The user's unique identifier
* @param elementId - The new element's unique identifier
* @param locationId - The parent location's unique identifier
* @param encryptedName - The encrypted element name
* @param originalName - The original (unencrypted) element name
* @param lang - The language for error messages ('fr' or 'en')
* @returns The element ID if insertion was successful
*/
static insertLocationElement(userId: string, elementId: string, locationId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string { static insertLocationElement(userId: string, elementId: string, locationId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
result = db.run('INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) VALUES (?,?,?,?,?,?,?)', [elementId, locationId, userId, encryptedName, originalName, '', System.timeStampInSeconds()]); const query: string = `
} catch (e: unknown) { INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update)
if (e instanceof Error) { VALUES (?, ?, ?, ?, ?, ?, ?)
console.error(`DB Error: ${e.message}`); `;
const params: SQLiteValue[] = [elementId, locationId, userId, encryptedName, originalName, '', 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'élément d'emplacement.` : `Error adding location element.`);
}
return elementId;
} 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'emplacement.` : `Unable to add location element.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter l'élément d'emplacement.` : `Unable to add location element.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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'élément d'emplacement.` : `Error adding location element.`);
}
return elementId;
} }
/**
* Inserts a new sub-element within a location element.
* @param userId - The user's unique identifier
* @param subElementId - The new sub-element's unique identifier
* @param elementId - The parent element's unique identifier
* @param encryptedName - The encrypted sub-element name
* @param originalName - The original (unencrypted) sub-element name
* @param lang - The language for error messages ('fr' or 'en')
* @returns The sub-element ID if insertion was successful
*/
static insertLocationSubElement(userId: string, subElementId: string, elementId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string { static insertLocationSubElement(userId: string, subElementId: string, elementId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
result = 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, encryptedName, originalName, '', System.timeStampInSeconds()]); const query: string = `
} catch (e: unknown) { INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update)
if (e instanceof Error) { VALUES (?, ?, ?, ?, ?, ?, ?)
console.error(`DB Error: ${e.message}`); `;
const params: SQLiteValue[] = [subElementId, elementId, userId, encryptedName, originalName, '', 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 sous-élément d'emplacement.` : `Error adding location sub-element.`);
}
return subElementId;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter le sous-élément d'emplacement.` : `Unable to add location sub-element.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le sous-élément d'emplacement.` : `Unable to add location sub-element.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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 sous-élément d'emplacement.` : `Error adding location sub-element.`);
}
return subElementId;
} }
/**
* Updates an existing location sub-element's name and description.
* @param userId - The user's unique identifier
* @param id - The sub-element's unique identifier
* @param encryptedName - The new encrypted sub-element name
* @param originalName - The new original (unencrypted) sub-element name
* @param encryptDescription - 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 updateLocationSubElement(userId: string, id: string, encryptedName: string, originalName: string, encryptDescription: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean { static updateLocationSubElement(userId: string, id: string, encryptedName: string, originalName: string, encryptDescription: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE location_sub_element SET sub_elem_name=?, original_name=?, sub_elem_description=?, last_update=? WHERE sub_element_id=? AND user_id=?', [encryptedName, originalName, encryptDescription, lastUpdate, id, userId]); const query: string = `
return result.changes > 0; UPDATE location_sub_element
} catch (e: unknown) { SET sub_elem_name = ?, original_name = ?, sub_elem_description = ?, last_update = ?
if (e instanceof Error) { WHERE sub_element_id = ? AND user_id = ?
console.error(`DB Error: ${e.message}`); `;
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.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le sous-élément d'emplacement.` : `Unable to update location sub-element.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static updateLocationElement(userId: string, id: string, encryptedName: string, originalName: string, encryptedDescription: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); 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]); const query: string = `
return result.changes > 0; UPDATE location_element
} catch (e: unknown) { SET element_name = ?, original_name = ?, element_description = ?, last_update = ?
if (e instanceof Error) { WHERE element_id = ? AND user_id = ?
console.error(`DB Error: ${e.message}`); `;
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.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'élément d'emplacement.` : `Unable to update location element.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static updateLocationSection(userId: string, id: string, encryptedName: string, originalName: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); 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]); const query: string = `
return result.changes > 0; UPDATE book_location
} catch (e: unknown) { SET loc_name = ?, loc_original_name = ?, last_update = ?
if (e instanceof Error) { WHERE loc_id = ? AND user_id = ?
console.error(`DB Error: ${e.message}`); `;
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.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour la section d'emplacement.` : `Unable to update location section.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static deleteLocationSection(userId: string, locationId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_location WHERE loc_id=? AND user_id=?', [locationId, userId]); const query: string = 'DELETE FROM book_location WHERE loc_id = ? AND user_id = ?';
return result.changes > 0; const params: SQLiteValue[] = [locationId, userId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
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 supprimer la section d'emplacement.` : `Unable to delete location section.`); throw new Error(lang === 'fr' ? `Impossible de supprimer la section d'emplacement.` : `Unable to delete location section.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static deleteLocationElement(userId: string, elementId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM location_element WHERE element_id=? AND user_id=?', [elementId, userId]); const query: string = 'DELETE FROM location_element WHERE element_id = ? AND user_id = ?';
return result.changes > 0; const params: SQLiteValue[] = [elementId, userId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
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 supprimer l'élément d'emplacement.` : `Unable to delete location element.`); throw new Error(lang === 'fr' ? `Impossible de supprimer l'élément d'emplacement.` : `Unable to delete location element.`);
} else { } else {
console.error("An unknown error occurred."); 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 { static deleteLocationSubElement(userId: string, subElementId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM location_sub_element WHERE sub_element_id=? AND user_id=?', [subElementId, userId]); const query: string = 'DELETE FROM location_sub_element WHERE sub_element_id = ? AND user_id = ?';
return result.changes > 0; const params: SQLiteValue[] = [subElementId, userId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
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 supprimer le sous-élément d'emplacement.` : `Unable to delete location sub-element.`); throw new Error(lang === 'fr' ? `Impossible de supprimer le sous-élément d'emplacement.` : `Unable to delete location sub-element.`);
} else { } else {
console.error("An unknown error occurred."); 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[] { static fetchLocationTags(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationElementQueryResult[] {
let result: LocationElementQueryResult[];
try { try {
const db: Database = System.getDb(); 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 = ?'; const query: string = `
result = db.all(query, [bookId, userId]) as LocationElementQueryResult[]; SELECT se.sub_element_id AS sub_element_id, se.sub_elem_name, se.sub_elem_description,
} catch (e: unknown) { el.element_id AS element_id, el.element_name, el.element_description
if (e instanceof Error) { FROM location_sub_element AS se
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les tags d'emplacement.` : `Unable to retrieve location tags.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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[] { static fetchLocationsByTags(userId: string, locations: string[], lang: 'fr' | 'en' = 'fr'): LocationByTagResult[] {
if (locations.length === 0) { if (locations.length === 0) {
throw new Error(lang === 'fr' ? `Aucun tag fourni.` : `No tags provided.`); throw new Error(lang === 'fr' ? `Aucun tag fourni.` : `No tags provided.`);
} }
let result: LocationByTagResult[];
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const locationIds: string = locations.map((): string => '?').join(','); const locationPlaceholders: string = locations.map((): string => '?').join(',');
const query: string = ` const query: string = `
SELECT el.element_name, SELECT el.element_name,
el.element_description, el.element_description,
se.sub_elem_name, se.sub_elem_name,
se.sub_elem_description se.sub_elem_description
FROM location_element AS el 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 = ? 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]; const params: SQLiteValue[] = [userId, ...locations, ...locations];
result = db.all(query, values) as LocationByTagResult[]; const locationsByTags: LocationByTagResult[] = db.all(query, params) as LocationByTagResult[];
} catch (e: unknown) { if (locationsByTags.length === 0) {
if (e instanceof Error) { throw new Error(lang === 'fr' ? `Aucun emplacement trouvé avec ces tags.` : `No locations found with these tags.`);
console.error(`DB Error: ${e.message}`); }
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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les emplacements par tags.` : `Unable to retrieve locations by tags.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM `book_location` WHERE `loc_id` = ? AND `user_id` = ?';
return result !== null; const params: SQLiteValue[] = [locId, userId];
} catch (e: unknown) { const existingLocation: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return existingLocation !== null;
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 vérifier l'existence de l'emplacement.` : `Unable to check location existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'emplacement.` : `Unable to check location existence.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM `location_element` WHERE `element_id` = ? AND `user_id` = ?';
return result !== null; const params: SQLiteValue[] = [elementId, userId];
} catch (e: unknown) { const existingElement: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return existingElement !== null;
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 vérifier l'existence de l'élément d'emplacement.` : `Unable to check location element existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément d'emplacement.` : `Unable to check location element existence.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM `location_sub_element` WHERE `sub_element_id` = ? AND `user_id` = ?';
return result !== null; const params: SQLiteValue[] = [subElementId, userId];
} catch (e: unknown) { const existingSubElement: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return existingSubElement !== null;
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 vérifier l'existence du sous-élément d'emplacement.` : `Unable to check location sub-element existence.`); 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 { } else {
console.error("An unknown error occurred."); 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<BookLocationTable[]> { static async fetchBookLocations(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookLocationTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = `
} catch (e: unknown) { SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update
if (e instanceof Error) { FROM book_location
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lieux.` : `Unable to retrieve locations.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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<LocationElementTable[]> {
/**
* 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<LocationElementTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = `
} catch (e: unknown) { SELECT element_id, location, user_id, element_name, original_name, element_description, last_update
if (e instanceof Error) { FROM location_element
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de lieu.` : `Unable to retrieve location elements.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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<LocationSubElementTable[]> { /**
* 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<LocationSubElementTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = `
} catch (e: unknown) { SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update
if (e instanceof Error) { FROM location_sub_element
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les sous-éléments de lieu.` : `Unable to retrieve location sub-elements.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedLocations(userId: string, lang: 'fr' | 'en'): SyncedLocationResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT loc_id, book_id, loc_name, last_update FROM book_location WHERE user_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedLocations: SyncedLocationResult[] = db.all(query, params) as SyncedLocationResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les lieux synchronisés.` : `Unable to retrieve synced locations.`);
} else { } else {
console.error("An unknown error occurred."); 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[] { static fetchSyncedLocationElements(userId: string, lang: 'fr' | 'en'): SyncedLocationElementResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT element_id, location, element_name, last_update FROM location_element WHERE user_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedLocationElements: SyncedLocationElementResult[] = db.all(query, params) as SyncedLocationElementResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de lieu synchronisés.` : `Unable to retrieve synced location elements.`);
} else { } else {
console.error("An unknown error occurred."); 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[] { static fetchSyncedLocationSubElements(userId: string, lang: 'fr' | 'en'): SyncedLocationSubElementResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT sub_element_id, element_id, sub_elem_name, last_update FROM location_sub_element WHERE user_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedLocationSubElements: SyncedLocationSubElementResult[] = db.all(query, params) as SyncedLocationSubElementResult[];
console.error(`DB Error: ${e.message}`); 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.`); 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 { } else {
console.error("An unknown error occurred."); 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 { static insertSyncLocation(locId: string, bookId: string, userId: string, locName: string, locOriginalName: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = `
`INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update)
VALUES (?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?)
[locId, bookId, userId, locName, locOriginalName, lastUpdate] `;
); const params: SQLiteValue[] = [locId, bookId, userId, locName, locOriginalName, lastUpdate];
return result.changes > 0; const insertResult: RunResult = db.run(query, params);
} catch (e: unknown) { return insertResult.changes > 0;
if (e instanceof Error) { } catch (error: unknown) {
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible d'insérer le lieu.` : `Unable to insert location.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { static insertSyncLocationElement(elementId: string, location: string, userId: string, elementName: string, originalName: string, elementDescription: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = `
`INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?)
[elementId, location, userId, elementName, originalName, elementDescription, lastUpdate] `;
); const params: SQLiteValue[] = [elementId, location, userId, elementName, originalName, elementDescription, lastUpdate];
return result.changes > 0; const insertResult: RunResult = db.run(query, params);
} catch (e: unknown) { return insertResult.changes > 0;
if (e instanceof Error) { } catch (error: unknown) {
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible d'insérer l'élément du lieu.` : `Unable to insert location element.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { static insertSyncLocationSubElement(subElementId: string, elementId: string, userId: string, subElemName: string, originalName: string, subElemDescription: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( 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) INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?)
[subElementId, elementId, userId, subElemName, originalName, subElemDescription, lastUpdate] `;
); const params: SQLiteValue[] = [subElementId, elementId, userId, subElemName, originalName, subElemDescription, lastUpdate];
return result.changes > 0; const insertResult: RunResult = db.run(query, params);
} catch (e: unknown) { return insertResult.changes > 0;
if (e instanceof Error) { } catch (error: unknown) {
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible d'insérer le sous-élément du lieu.` : `Unable to insert location sub-element.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteLocationById(id: string, lang: "fr" | "en"):Promise<BookLocationTable[]> {
/**
* 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<BookLocationTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( const query: string = `
`SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update
FROM book_location FROM book_location
WHERE loc_id = ?`, WHERE loc_id = ?
[id] `;
) as BookLocationTable[]; const params: SQLiteValue[] = [id];
} catch (e:unknown){ const completeLocation: BookLocationTable[] = db.all(query, params) as BookLocationTable[];
if (e instanceof Error) { 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le lieu complet.` : `Unable to retrieve complete location.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteLocationElementById(id: string, lang: "fr" | "en"):Promise<LocationElementTable[]> { /**
* 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<LocationElementTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( const query: string = `
`SELECT element_id, location, user_id, element_name, original_name, element_description, last_update SELECT element_id, location, user_id, element_name, original_name, element_description, last_update
FROM location_element FROM location_element
WHERE element_id = ?`, WHERE element_id = ?
[id] `;
) as LocationElementTable[]; const params: SQLiteValue[] = [id];
} catch (e:unknown){ const completeLocationElement: LocationElementTable[] = db.all(query, params) as LocationElementTable[];
if (e instanceof Error) { 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de lieu complet.` : `Unable to retrieve complete location element.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteLocationSubElementById(id: string, lang: "fr" | "en"):Promise<LocationSubElementTable[]> { /**
* 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<LocationSubElementTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( const query: string = `
`SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update
FROM location_sub_element FROM location_sub_element
WHERE sub_element_id = ?`, WHERE sub_element_id = ?
[id] `;
) as LocationSubElementTable[]; const params: SQLiteValue[] = [id];
} catch (e:unknown){ const completeLocationSubElement: LocationSubElementTable[] = db.all(query, params) as LocationSubElementTable[];
if (e instanceof Error) { 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.`); 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 { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");

View File

@@ -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"; import System from "@/electron/database/System";
export interface BookPlotPointsTable extends Record<string, SQLiteValue> { export interface BookPlotPointsTable extends Record<string, SQLiteValue> {
@@ -27,13 +27,23 @@ export interface PlotPointQuery extends Record<string, SQLiteValue> {
} }
export default class PlotPointRepository { 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 { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT plot_point_id, title, summary, linked_incident_id FROM book_plot_points WHERE author_id=? AND book_id=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const plotPoints: PlotPointQuery[] = db.all(query, params) as PlotPointQuery[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les points d'intrigue.` : `Unable to retrieve plot points.`);
} else { } else {
console.error("An unknown error occurred."); 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 { 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; let insertResult: RunResult;
try { try {
const db: Database = System.getDb(); 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]); const checkQuery: string = 'SELECT plot_point_id FROM book_plot_points WHERE author_id=? AND book_id=? AND hashed_title=?';
} catch (e: unknown) { const checkParams: SQLiteValue[] = [userId, bookId, hashedName];
if (e instanceof Error) { existingPlotPoint = db.get(checkQuery, checkParams);
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 vérifier l'existence du point d'intrigue.` : `Unable to verify plot point existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du point d'intrigue.` : `Unable to verify plot point existence.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); throw new Error(lang === 'fr' ? `Ce point de l'intrigue existe déjà.` : `This plot point already exists.`);
} }
try { try {
const db: Database = System.getDb(); 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()]); const insertQuery: string = 'INSERT INTO book_plot_points (plot_point_id,title,hashed_title,author_id,book_id,linked_incident_id,last_update) VALUES (?,?,?,?,?,?,?)';
} catch (e: unknown) { const insertParams: SQLiteValue[] = [plotPointId, encryptedName, hashedName, userId, bookId, incidentId, System.timeStampInSeconds()];
if (e instanceof Error) { insertResult = db.run(insertQuery, insertParams);
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'ajouter le point d'intrigue.` : `Unable to add plot point.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le point d'intrigue.` : `Unable to add plot point.`);
} else { } else {
console.error("An unknown error occurred."); console.error("An unknown error occurred.");
@@ -76,15 +102,24 @@ export default class PlotPointRepository {
} }
return plotPointId; 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_plot_points WHERE author_id=? AND plot_point_id=?', [userId, plotNumId]); const query: string = 'DELETE FROM book_plot_points WHERE author_id=? AND plot_point_id=?';
return result.changes > 0; const params: SQLiteValue[] = [userId, plotPointId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
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 supprimer le point d'intrigue.` : `Unable to delete plot point.`); throw new Error(lang === 'fr' ? `Impossible de supprimer le point d'intrigue.` : `Unable to delete plot point.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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]); const query: string = 'UPDATE book_plot_points SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND plot_point_id=?';
return result.changes > 0; const params: SQLiteValue[] = [encryptedPlotPointName, plotPointHashedName, plotPointSummary, lastUpdate, userId, bookId, plotPointId];
} catch (e: unknown) { const updateResult: RunResult = db.run(query, params);
if (e instanceof Error) { return updateResult.changes > 0;
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 point d'intrigue.` : `Unable to update plot point.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le point d'intrigue.` : `Unable to update plot point.`);
} else { } else {
console.error("An unknown error occurred."); 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<BookPlotPointsTable[]> { static async fetchBookPlotPoints(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookPlotPointsTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const plotPoints: BookPlotPointsTable[] = db.all(query, params) as BookPlotPointsTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les points d'intrigue.` : `Unable to retrieve plot points.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedPlotPoints(userId: string, lang: 'fr' | 'en'): SyncedPlotPointResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT plot_point_id, book_id, title, last_update FROM book_plot_points WHERE author_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedPlotPoints: SyncedPlotPointResult[] = db.all(query, params) as SyncedPlotPointResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les points d'intrigue synchronisés.` : `Unable to retrieve synced plot points.`);
} else { } else {
console.error("An unknown error occurred."); 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 { 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = `INSERT INTO book_plot_points (plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update)
`INSERT INTO book_plot_points (plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, const params: SQLiteValue[] = [plotPointId, title, hashedTitle, summary, linkedIncidentId, authorId, bookId, lastUpdate];
[plotPointId, title, hashedTitle, summary, linkedIncidentId, authorId, bookId, lastUpdate] const insertResult: RunResult = db.run(query, params);
); return insertResult.changes > 0;
return result.changes > 0; } catch (error: unknown) {
} catch (e: unknown) { if (error instanceof Error) {
if (e instanceof Error) { console.error(`DB Error: ${error.message}`);
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le point d'intrigue.` : `Unable to insert plot point.`); throw new Error(lang === 'fr' ? `Impossible d'insérer le point d'intrigue.` : `Unable to insert plot point.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompletePlotPointById(id: string, lang: "fr" | "en"):Promise<BookPlotPointsTable[]> {
/**
* 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<BookPlotPointsTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( const query: string = `SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update
`SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update
FROM book_plot_points FROM book_plot_points
WHERE plot_point_id = ?`, WHERE plot_point_id = ?`;
[id] const params: SQLiteValue[] = [plotPointId];
) as BookPlotPointsTable[]; const plotPoint: BookPlotPointsTable[] = db.all(query, params) as BookPlotPointsTable[];
} catch (e:unknown){ return plotPoint;
if (e instanceof Error) { } 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le point d'intrigue complet.` : `Unable to retrieve complete plot point.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM book_plot_points WHERE author_id =? AND book_id =? AND plot_point_id =?';
return result !== null; const params: SQLiteValue[] = [userId, bookId, plotPointId];
} catch (e: unknown) { const existingPlotPoint: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return existingPlotPoint !== null;
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 vérifier l'existence du point de intrigue.` : `Unable to check plot point existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du point de intrigue.` : `Unable to check plot point existence.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
} }

View File

@@ -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"; import System from "../System.js";
export interface UserInfosQueryResponse extends Record<string, SQLiteValue> { export interface UserInfosQueryResponse extends Record<string, SQLiteValue> {
@@ -45,15 +45,39 @@ export interface GuideTourResult extends Record<string, SQLiteValue> {
export default class UserRepo { 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const query = `INSERT INTO erit_users (user_id, first_name, last_name, username, email, origin_email, const query: string = `
origin_username, plateform, term_accepted, INSERT INTO erit_users (
account_verified, reg_date) user_id, first_name, last_name, username, email, origin_email,
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; origin_username, plateform, term_accepted, account_verified, reg_date
const values: (string | null | number)[] = [ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [
uuId, uuId,
firstName, firstName,
lastName, lastName,
@@ -61,56 +85,113 @@ export default class UserRepo {
email, email,
originEmail, originEmail,
originUsername, originUsername,
'desktop', // plateform 'desktop',
0, // term_accepted 0,
1, // account_verified 1,
Date.now() // reg_date (current timestamp) Date.now()
]; ];
result = db.run(query, values); insertResult = db.run(query, params);
} catch (e: unknown) { } catch (error: unknown) {
if (e instanceof Error) { if (error instanceof Error) {
console.error(`DB Error: ${e.message}`); console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'enregistrer l'utilisateur.` : `Unable to register user.`); throw new Error(lang === 'fr' ? `Impossible d'enregistrer l'utilisateur.` : `Unable to register user.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
if (result.changes > 0) { if (insertResult.changes > 0) {
return uuId; return uuId;
} else { } else {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'enregistrement de l'utilisateur.` : `Error registering user.`); 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 { public static fetchUserInfos(userId: string, lang: 'fr' | 'en' = 'fr'): UserInfosQueryResponse {
let result; let userInfo: Record<string, SQLiteValue> | undefined;
try { try {
const db: Database = System.getDb(); 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]); const query: string = `
} catch (e: unknown) { SELECT first_name, last_name, username, email, plateform, term_accepted,
if (e instanceof Error) { account_verified, author_name, erite_points AS rite_points, user_group
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les informations utilisateur.` : `Unable to retrieve user information.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); 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 { try {
const db: Database = System.getDb(); 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]); const query: string = `
return result.changes > 0; UPDATE erit_users
} catch (e: unknown) { SET first_name = ?, last_name = ?, username = ?, email = ?,
if (e instanceof Error) { origin_username = ?, origin_author_name = ?, author_name = ?
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour les informations utilisateur.` : `Unable to update user information.`);
} else { } else {
console.error("An unknown error occurred."); 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 { public static fetchAccountInformation(userId: string, lang: 'fr' | 'en' = 'fr'): UserAccountQuery {
let result; let accountInfo: Record<string, SQLiteValue> | undefined;
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
result = db.get('SELECT first_name, last_name, username, author_name, email FROM erit_users WHERE user_id=?', [userId]); const query: string = `
} catch (e: unknown) { SELECT first_name, last_name, username, author_name, email
if (e instanceof Error) { FROM erit_users
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les informations du compte.` : `Unable to retrieve account information.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); throw new Error(lang === 'fr' ? `Compte non trouvé.` : `Account not found.`);
} }
return result as UserAccountQuery; return accountInfo as UserAccountQuery;
} }
} }

View File

@@ -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"; import System from "@/electron/database/System";
export interface BookWorldTable extends Record<string, SQLiteValue> { export interface BookWorldTable extends Record<string, SQLiteValue> {
@@ -61,15 +61,25 @@ export interface WorldElementValue {
type: number; type: number;
} }
export default class WorldRepository { export default class WorldRepository {
public static checkWorldExist(userId:string,bookId:string,worldName:string, lang: 'fr' | 'en'):boolean{ /**
* 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 { try {
const db: Database = System.getDb(); 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]); const query: string = 'SELECT world_id FROM book_world WHERE author_id=? AND book_id=? AND hashed_name=?';
return result !== null; const params: SQLiteValue[] = [userId, bookId, worldName];
} catch (e: unknown) { const world: QueryResult | null = db.get(query, params);
if (e instanceof Error) { return world !== null;
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 vérifier l'existence du monde.` : `Unable to verify world existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du monde.` : `Unable to verify world existence.`);
} else { } else {
console.error("An unknown error occurred."); 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 { public static insertNewWorld(worldId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string {
let result: RunResult; let insertResult: RunResult;
try { try {
const db: Database = System.getDb(); 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()]); const query: string = 'INSERT INTO book_world (world_id, author_id, book_id, name, hashed_name, last_update) VALUES (?, ?, ?, ?, ?, ?)';
} catch (e: unknown) { const params: SQLiteValue[] = [worldId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()];
if (e instanceof Error) { insertResult = db.run(query, params);
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'ajouter le monde.` : `Unable to add world.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter le monde.` : `Unable to add world.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); throw new Error(lang === 'fr' ? `Erreur lors de l'ajout du monde.` : `Error adding world.`);
} }
return worldId; 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 { try {
const db: Database = System.getDb(); 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[]; 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=?`;
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const worlds: WorldQuery[] = db.all(query, params) as WorldQuery[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les mondes.` : `Unable to retrieve worlds.`);
} else { } else {
console.error("An unknown error occurred."); 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 { 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 { try {
const db: Database = System.getDb(); 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]); const query: string = 'UPDATE book_world SET name=?, hashed_name=?, history=?, politics=?, economy=?, religion=?, languages=?, last_update=? WHERE author_id=? AND world_id=?';
return result.changes > 0; const params: SQLiteValue[] = [encryptName, hashedName, encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, lastUpdate, userId, worldId];
} catch (e: unknown) { const updateResult: RunResult = db.run(query, params);
if (e instanceof Error) { return updateResult.changes > 0;
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 monde.` : `Unable to update world.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour le monde.` : `Unable to update world.`);
} else { } else {
console.error("An unknown error occurred."); 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 { public static updateWorldElements(userId: string, elements: WorldElementValue[], lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); 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) { 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]); const params: SQLiteValue[] = [element.name, element.description, element.type, System.timeStampInSeconds(), userId, element.id];
if (result.changes <= 0) { const updateResult: RunResult = db.run(query, params);
if (updateResult.changes <= 0) {
return false; return false;
} }
} }
return true; return true;
} catch (e: unknown) { } catch (error: unknown) {
if (e instanceof Error) { if (error instanceof Error) {
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour les éléments du monde.` : `Unable to update world elements.`);
} else { } else {
console.error("An unknown error occurred."); 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 { public static checkElementExist(worldNumId: string, hashedName: string, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result = db.get('SELECT element_id FROM book_world_elements WHERE world_id=? AND original_name=?', [worldNumId, hashedName]); const query: string = 'SELECT element_id FROM book_world_elements WHERE world_id=? AND original_name=?';
return result !== null; const params: SQLiteValue[] = [worldNumId, hashedName];
} catch (e: unknown) { const element: QueryResult | null = db.get(query, params);
if (e instanceof Error) { return element !== null;
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 vérifier l'existence de l'élément.` : `Unable to verify element existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément.` : `Unable to verify element existence.`);
} else { } else {
console.error("An unknown error occurred."); 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 { 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 { try {
const db: Database = System.getDb(); 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()]); const query: string = 'INSERT INTO book_world_elements (element_id, world_id, user_id, name, original_name, element_type, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)';
} catch (e: unknown) { const params: SQLiteValue[] = [elementId, worldId, userId, encryptedName, hashedName, elementType, System.timeStampInSeconds()];
if (e instanceof Error) { insertResult = db.run(query, params);
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'ajouter l'élément.` : `Unable to add element.`); throw new Error(lang === 'fr' ? `Impossible d'ajouter l'élément.` : `Unable to add element.`);
} else { } else {
console.error("An unknown error occurred."); 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' ? "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.`); throw new Error(lang === 'fr' ? `Erreur lors de l'ajout de l'élément.` : `Error adding element.`);
} }
return elementId; 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 { public static deleteElement(userId: string, elementId: string, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_world_elements WHERE user_id=? AND element_id=?', [userId, elementId]); const query: string = 'DELETE FROM book_world_elements WHERE user_id=? AND element_id=?';
return result.changes > 0; const params: SQLiteValue[] = [userId, elementId];
} catch (e: unknown) { const deleteResult: RunResult = db.run(query, params);
if (e instanceof Error) { return deleteResult.changes > 0;
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 supprimer l'élément.` : `Unable to delete element.`); throw new Error(lang === 'fr' ? `Impossible de supprimer l'élément.` : `Unable to delete element.`);
} else { } else {
console.error("An unknown error occurred."); 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<BookWorldTable[]> { static async fetchBookWorlds(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookWorldTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, bookId];
if (e instanceof Error) { const worlds: BookWorldTable[] = db.all(query, params) as BookWorldTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les mondes.` : `Unable to retrieve worlds.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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<BookWorldElementsTable[]> { /**
* 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<BookWorldElementsTable[]> {
try { try {
const db: Database = System.getDb(); 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[]; 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=?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId, worldId];
if (e instanceof Error) { const elements: BookWorldElementsTable[] = db.all(query, params) as BookWorldElementsTable[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments du monde.` : `Unable to retrieve world elements.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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[] { static fetchSyncedWorlds(userId: string, lang: 'fr' | 'en'): SyncedWorldResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT world_id, book_id, name, last_update FROM book_world WHERE author_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedWorlds: SyncedWorldResult[] = db.all(query, params) as SyncedWorldResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les mondes synchronisés.` : `Unable to retrieve synced worlds.`);
} else { } else {
console.error("An unknown error occurred."); 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[] { static fetchSyncedWorldElements(userId: string, lang: 'fr' | 'en'): SyncedWorldElementResult[] {
try { try {
const db: Database = System.getDb(); 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[]; const query: string = 'SELECT element_id, world_id, name, last_update FROM book_world_elements WHERE user_id = ?';
} catch (e: unknown) { const params: SQLiteValue[] = [userId];
if (e instanceof Error) { const syncedElements: SyncedWorldElementResult[] = db.all(query, params) as SyncedWorldElementResult[];
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de monde synchronisés.` : `Unable to retrieve synced world elements.`);
} else { } else {
console.error("An unknown error occurred."); 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 { 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 { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = `INSERT INTO book_world (world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
`INSERT INTO book_world (world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update) const params: SQLiteValue[] = [worldId, name, hashedName, authorId, bookId, history, politics, economy, religion, languages, lastUpdate];
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, const insertResult: RunResult = db.run(query, params);
[worldId, name, hashedName, authorId, bookId, history, politics, economy, religion, languages, lastUpdate] return insertResult.changes > 0;
); } catch (error: unknown) {
return result.changes > 0; if (error instanceof Error) {
} catch (e: unknown) { console.error(`DB Error: ${error.message}`);
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le monde.` : `Unable to insert world.`); throw new Error(lang === 'fr' ? `Impossible d'insérer le monde.` : `Unable to insert world.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { static insertSyncWorldElement(elementId: string, worldId: string, userId: string, elementType: number, name: string, originalName: string, description: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
const result: RunResult = db.run( const query: string = `INSERT INTO book_world_elements (element_id, world_id, user_id, element_type, name, original_name, description, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
`INSERT INTO book_world_elements (element_id, world_id, user_id, element_type, name, original_name, description, last_update) const params: SQLiteValue[] = [elementId, worldId, userId, elementType, name, originalName, description, lastUpdate];
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, const insertResult: RunResult = db.run(query, params);
[elementId, worldId, userId, elementType, name, originalName, description, lastUpdate] return insertResult.changes > 0;
); } catch (error: unknown) {
return result.changes > 0; if (error instanceof Error) {
} catch (e: unknown) { console.error(`DB Error: ${error.message}`);
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer l'élément du monde.` : `Unable to insert world element.`); throw new Error(lang === 'fr' ? `Impossible d'insérer l'élément du monde.` : `Unable to insert world element.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteWorldById(id: string, lang: "fr" | "en"):Promise<BookWorldTable[]> { /**
* 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<BookWorldTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( 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 = ?`;
`SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update const params: SQLiteValue[] = [id];
FROM book_world const worlds: BookWorldTable[] = db.all(query, params) as BookWorldTable[];
WHERE world_id = ?`, return worlds;
[id] } catch (error: unknown) {
) as BookWorldTable[]; if (error instanceof Error) {
} catch (e:unknown){
if (e instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le monde complet.` : `Unable to retrieve complete world.`); throw new Error(lang === 'fr' ? `Impossible de récupérer le monde complet.` : `Unable to retrieve complete world.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
static async fetchCompleteWorldElementById(id: string, lang: "fr" | "en"):Promise<BookWorldElementsTable[]> { /**
* 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<BookWorldElementsTable[]> {
try { try {
const db: Database = System.getDb(); const db: Database = System.getDb();
return db.all( 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 = ?`;
`SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update const params: SQLiteValue[] = [id];
FROM book_world_elements const elements: BookWorldElementsTable[] = db.all(query, params) as BookWorldElementsTable[];
WHERE element_id = ?`, return elements;
[id] } catch (error: unknown) {
) as BookWorldElementsTable[]; if (error instanceof Error) {
} catch (e:unknown){
if (e instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de monde complet.` : `Unable to retrieve complete world element.`); throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de monde complet.` : `Unable to retrieve complete world element.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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 query: string = `UPDATE book_world_elements SET name = ?, description = ?, last_update = ? WHERE element_id = ? AND user_id = ?`;
const params:(string|number)[] = [name, description, lastUpdate, elementId, userId]; const params: SQLiteValue[] = [name, description, lastUpdate, elementId, userId];
const result:RunResult = db.run(query, params); const updateResult: RunResult = db.run(query, params);
return result.changes > 0; return updateResult.changes > 0;
} catch (e:unknown) { } catch (error: unknown) {
if (e instanceof Error) { if (error instanceof Error) {
console.error(`DB Error: ${e.message}`); 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.`); throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'élément du monde.` : `Unable to update world element.`);
} else { } else {
console.error("An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM book_world WHERE world_id=? AND author_id=? AND book_id=?';
return result !== null; const params: SQLiteValue[] = [worldId, userId, bookId];
} catch (e: unknown) { const world: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return world !== null;
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 vérifier l'existence du monde.` : `Unable to check world existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du monde.` : `Unable to check world existence.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); 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 { try {
const db: Database = System.getDb(); 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; const query: string = 'SELECT 1 FROM book_world_elements WHERE element_id=? AND world_id=? AND user_id=?';
return result !== null; const params: SQLiteValue[] = [elementId, worldId, userId];
} catch (e: unknown) { const element: QueryResult | null = db.get(query, params) || null;
if (e instanceof Error) { return element !== null;
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 vérifier l'existence de l'élément du monde.` : `Unable to check world element existence.`); throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément du monde.` : `Unable to check world element existence.`);
} else { } else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
} }
} }
} }
} }