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 {
/**
* Fetches all acts for a specific book and user.
* @param userId - The unique identifier of the user.
* @param bookId - The unique identifier of the book.
* @param lang - The language for error messages ('fr' or 'en').
* @returns An array of ActQuery objects containing act index and summary.
* @throws Error if the database operation fails.
*/
public static fetchAllActs(userId: string, bookId: string, lang: 'fr' | 'en'): ActQuery[] {
try {
const db: Database = System.getDb();
return db.all('SELECT act_index, summary FROM book_act_summaries WHERE book_id=? AND user_id=?', [bookId, userId]) as ActQuery[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT act_index, summary FROM book_act_summaries WHERE book_id=? AND user_id=?';
const params: SQLiteValue[] = [bookId, userId];
return db.all(query, params) as ActQuery[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les actes.` : `Unable to retrieve acts.`);
} else {
console.error("An unknown error occurred.");
@@ -36,14 +46,28 @@ export default class ActRepository {
}
}
}
/**
* Updates the summary of an existing act.
* @param userId - The unique identifier of the user.
* @param bookId - The unique identifier of the book.
* @param actId - The unique identifier of the act summary.
* @param summary - The new summary text.
* @param lastUpdate - The timestamp of the last update in seconds.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the update was successful, false otherwise.
* @throws Error if the database operation fails.
*/
public static updateActSummary(userId: string, bookId: string, actId: number, summary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_act_summaries SET summary=?, last_update=? WHERE user_id=? AND book_id=? AND act_sum_id=?', [summary, lastUpdate, userId, bookId, actId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'UPDATE book_act_summaries SET summary=?, last_update=? WHERE user_id=? AND book_id=? AND act_sum_id=?';
const params: SQLiteValue[] = [summary, lastUpdate, userId, bookId, actId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour le résumé de l'acte.` : `Unable to update act summary.`);
} else {
console.error("An unknown error occurred.");
@@ -51,45 +75,81 @@ export default class ActRepository {
}
}
}
/**
* Inserts a new act summary into the database.
* @param actSummaryId - The unique identifier for the new act summary.
* @param userId - The unique identifier of the user.
* @param bookId - The unique identifier of the book.
* @param actId - The act index number.
* @param actSummary - The summary text for the act.
* @param lang - The language for error messages ('fr' or 'en').
* @returns The act summary ID if insertion was successful.
* @throws Error if the database operation fails.
*/
static insertActSummary(actSummaryId: string, userId: string, bookId: string, actId: number, actSummary: string, lang: 'fr' | 'en'): string {
let result:RunResult
let insertResult: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?,?,?,?,?,?)', [actSummaryId, bookId, userId, actId, actSummary, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?,?,?,?,?,?)';
const params: SQLiteValue[] = [actSummaryId, bookId, userId, actId, actSummary, System.timeStampInSeconds()];
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter le résumé de l'acte.` : `Unable to add act summary.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result) {
if (!insertResult) {
throw new Error(lang === 'fr' ? `Erreur lors de l'ajout du résumé de l'acte.` : `Error adding act summary.`);
}
return actSummaryId;
}
/**
* Fetches all act summaries for a specific book.
* @param userId - The unique identifier of the user.
* @param bookId - The unique identifier of the book.
* @param lang - The language for error messages ('fr' or 'en').
* @returns A promise resolving to an array of BookActSummariesTable objects.
* @throws Error if the database operation fails.
*/
static async fetchBookActSummaries(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookActSummariesTable[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT act_sum_id, book_id, user_id, act_index, summary, last_update FROM book_act_summaries WHERE user_id=? AND book_id=?', [userId, bookId]) as BookActSummariesTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT act_sum_id, book_id, user_id, act_index, summary, last_update FROM book_act_summaries WHERE user_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
return db.all(query, params) as BookActSummariesTable[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les résumés des actes.` : `Unable to retrieve act summaries.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Fetches all synced act summaries for a user.
* @param userId - The unique identifier of the user.
* @param lang - The language for error messages ('fr' or 'en').
* @returns An array of SyncedActSummaryResult objects containing sync metadata.
* @throws Error if the database operation fails.
*/
static fetchSyncedActSummaries(userId: string, lang: 'fr' | 'en'): SyncedActSummaryResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT act_sum_id, book_id, last_update FROM book_act_summaries WHERE user_id = ?', [userId]) as SyncedActSummaryResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT act_sum_id, book_id, last_update FROM book_act_summaries WHERE user_id = ?';
const params: SQLiteValue[] = [userId];
const syncedActSummaries: SyncedActSummaryResult[] = db.all(query, params) as SyncedActSummaryResult[];
return syncedActSummaries;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les résumés d'actes synchronisés.` : `Unable to retrieve synced act summaries.`);
} else {
console.error("An unknown error occurred.");
@@ -97,48 +157,80 @@ export default class ActRepository {
}
}
}
/**
* Inserts a synced act summary from remote data.
* @param actSumId - The unique identifier of the act summary.
* @param bookId - The unique identifier of the book.
* @param userId - The unique identifier of the user.
* @param actIndex - The act index number.
* @param summary - The summary text (can be null).
* @param lastUpdate - The timestamp of the last update in seconds.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the insertion was successful, false otherwise.
* @throws Error if the database operation fails.
*/
static insertSyncActSummary(actSumId: string, bookId: string, userId: string, actIndex: number, summary: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?, ?, ?, ?, ?, ?)`,
[actSumId, bookId, userId, actIndex, summary, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, last_update) VALUES (?, ?, ?, ?, ?, ?)';
const params: SQLiteValue[] = [actSumId, bookId, userId, actIndex, summary, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le résumé d'acte.` : `Unable to insert act summary.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static async fetchCompleteActSummaryById(id: string, lang: "fr" | "en"):Promise<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 {
const db: Database = System.getDb();
return db.all(
`SELECT act_sum_id, book_id, user_id, act_index, summary, last_update
const query: string = `SELECT act_sum_id, book_id, user_id, act_index, summary, last_update
FROM book_act_summaries
WHERE act_sum_id = ?`,
[id]
) as BookActSummariesTable[];
} catch (e:unknown){
if (e instanceof Error) {
WHERE act_sum_id = ?`;
const params: SQLiteValue[] = [id];
const actSummary: BookActSummariesTable[] = db.all(query, params) as BookActSummariesTable[];
return actSummary;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le résumé d'acte complet.` : `Unable to retrieve complete act summary.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static actSummarizeExist(userId: string, bookId: string, act_index: number,lang: "fr" | "en"): boolean {
/**
* Checks if an act summary exists for a given user, book, and act index.
* @param userId - The unique identifier of the user.
* @param bookId - The unique identifier of the book.
* @param actIndex - The act index number to check.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the act summary exists, false otherwise.
* @throws Error if the database operation fails.
*/
static actSummarizeExist(userId: string, bookId: string, actIndex: number, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result:QueryResult|null = db.get('SELECT 1 FROM book_act_summaries WHERE user_id =? AND book_id =? AND act_index = ?', [userId, bookId, act_index]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM book_act_summaries WHERE user_id =? AND book_id =? AND act_index = ?';
const params: SQLiteValue[] = [userId, bookId, actIndex];
const existenceCheck: QueryResult | null = db.get(query, params) || null;
return existenceCheck !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du résumé de l'acte.` : `Unable to check act summary existence.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");

View File

@@ -47,197 +47,318 @@ export interface BookCoverQuery extends Record<string, SQLiteValue> {
}
export default class BookRepo {
/**
* Retrieves all books for a user.
* @param userId - The user identifier
* @param lang - The language for error messages
* @returns List of user's books
*/
public static fetchBooks(userId: string, lang: 'fr' | 'en'): BookQuery[] {
try {
const db: Database = System.getDb();
return db.all('SELECT book_id, type, author_id, title, sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image FROM erit_books WHERE author_id = ? ORDER BY book_id DESC', [userId]) as BookQuery[];
const query: string = 'SELECT book_id, type, author_id, title, sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image FROM erit_books WHERE author_id = ? ORDER BY book_id DESC';
const params: SQLiteValue[] = [userId];
return db.all(query, params) as BookQuery[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(error.message);
throw new Error(lang === 'fr' ? 'Impossible de récupérer la liste des livres.' : 'Unable to retrieve book list.');
} else {
}
console.error(error);
throw new Error(lang === 'fr' ? 'Une erreur inconnue est survenue.' : 'An unknown error occurred.');
}
}
}
public static updateBookCover(bookId:string,coverImageName:string,userId:string, lang: 'fr' | 'en'):boolean{
/**
* Updates a book's cover image.
* @param bookId - The book identifier
* @param coverImageName - The cover image file name
* @param userId - The user identifier
* @param lang - The language for error messages
* @returns true if the update was successful
*/
public static updateBookCover(bookId: string, coverImageName: string, userId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result:RunResult = db.run('UPDATE `erit_books` SET cover_image=?, last_update=? WHERE `book_id`=? AND author_id=?', [coverImageName, System.timeStampInSeconds(), bookId, userId]);
return result.changes>0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour la couverture du livre.` : `Unable to update book cover.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
public static fetchBook(bookId: string, userId: string, lang: 'fr' | 'en'): BookQuery {
let result: BookQuery;
try {
const db: Database = System.getDb();
result = db.get('SELECT book_id, author_id, title, summary, sub_title, cover_image, desired_release_date, desired_word_count, words_count FROM erit_books WHERE book_id=? AND author_id=?', [bookId, userId]) as BookQuery;
const query: string = 'UPDATE erit_books SET cover_image=?, last_update=? WHERE book_id=? AND author_id=?';
const params: SQLiteValue[] = [coverImageName, System.timeStampInSeconds(), bookId, userId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les informations du livre.` : `Unable to retrieve book information.`);
} else {
throw new Error(lang === 'fr' ? 'Impossible de mettre à jour la couverture du livre.' : 'Unable to update book cover.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result) {
throw new Error(lang === 'fr' ? `Livre non trouvé.` : `Book not found.`);
}
return result;
}
public static verifyBookExist(hashedTitle:string,hashedSubTitle:string,userId:string, lang: 'fr' | 'en'):boolean{
try {
const db: Database = System.getDb();
const result:QueryResult|null = db.get('SELECT book_id FROM erit_books WHERE hashed_title=? AND author_id=? AND erit_books.hashed_sub_title=?', [hashedTitle,userId,hashedSubTitle]);
return result!==null;
} catch (err: unknown) {
if (err instanceof Error) {
console.error(`DB Error: ${err.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du livre.` : `Unable to verify book existence.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
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;
/**
* Retrieves a book by its identifier.
* @param bookId - The book identifier
* @param userId - The user identifier
* @param lang - The language for error messages
* @returns The book information
*/
public static fetchBook(bookId: string, userId: string, lang: 'fr' | 'en'): BookQuery {
let book: BookQuery;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO erit_books (book_id, type, author_id, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [bookId, type, userId, encryptedTitle, hashedTitle, encryptedSubTitle, hashedSubTitle, encryptedSummary, serie, publicationDate ? publicationDate : null, desiredWordCount, System.timeStampInSeconds()]);
} catch (err: unknown) {
if (err instanceof Error) {
console.error(`DB Error: ${err.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter le livre.` : `Unable to add book.`);
} else {
const query: string = 'SELECT book_id, author_id, title, summary, sub_title, cover_image, desired_release_date, desired_word_count, words_count FROM erit_books WHERE book_id=? AND author_id=?';
const params: SQLiteValue[] = [bookId, userId];
book = db.get(query, params) as BookQuery;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer les informations du livre.' : 'Unable to retrieve book information.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
if (!book) {
throw new Error(lang === 'fr' ? 'Livre non trouvé.' : 'Book not found.');
}
return book;
}
/**
* Verifies if a book already exists for a user.
* @param hashedTitle - The hashed book title
* @param hashedSubTitle - The hashed book subtitle
* @param userId - The user identifier
* @param lang - The language for error messages
* @returns true if the book exists
*/
public static verifyBookExist(hashedTitle: string, hashedSubTitle: string, userId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const query: string = 'SELECT book_id FROM erit_books WHERE hashed_title=? AND author_id=? AND hashed_sub_title=?';
const params: SQLiteValue[] = [hashedTitle, userId, hashedSubTitle];
const book: QueryResult | null = db.get(query, params);
return book !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence du livre." : 'Unable to verify book existence.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Erreur lors de l'ajout du livre.` : `Error adding book.`);
/**
* Inserts a new book into the database.
* @param bookId - The book identifier
* @param userId - The user identifier
* @param encryptedTitle - The encrypted title
* @param hashedTitle - The hashed title
* @param encryptedSubTitle - The encrypted subtitle
* @param hashedSubTitle - The hashed subtitle
* @param encryptedSummary - The encrypted summary
* @param type - The book type
* @param serie - The series identifier
* @param publicationDate - The desired publication date
* @param desiredWordCount - The desired word count
* @param lang - The language for error messages
* @returns The created book identifier
*/
public static insertBook(bookId: string, userId: string, encryptedTitle: string, hashedTitle: string, encryptedSubTitle: string, hashedSubTitle: string, encryptedSummary: string, type: string, serie: number, publicationDate: string, desiredWordCount: number, lang: 'fr' | 'en'): string {
let insertResult: RunResult;
try {
const db: Database = System.getDb();
const query: string = 'INSERT INTO erit_books (book_id, type, author_id, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)';
const params: SQLiteValue[] = [bookId, type, userId, encryptedTitle, hashedTitle, encryptedSubTitle, hashedSubTitle, encryptedSummary, serie, publicationDate ? publicationDate : null, desiredWordCount, System.timeStampInSeconds()];
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible d'ajouter le livre." : 'Unable to add book.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? "Erreur lors de l'ajout du livre." : 'Error adding book.');
}
return bookId;
}
public static fetchBookCover(userId:string,bookId:string, lang: 'fr' | 'en'):BookCoverQuery{
/**
* Retrieves a book's cover image.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param lang - The language for error messages
* @returns The cover information
*/
public static fetchBookCover(userId: string, bookId: string, lang: 'fr' | 'en'): BookCoverQuery {
try {
const db: Database = System.getDb();
return db.get('SELECT cover_image FROM erit_books WHERE author_id=? AND book_id=?', [userId, bookId]) as BookCoverQuery;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer la couverture du livre.` : `Unable to retrieve book cover.`);
} else {
const query: string = 'SELECT cover_image FROM erit_books WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
return db.get(query, params) as BookCoverQuery;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer la couverture du livre.' : 'Unable to retrieve book cover.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Updates a book's basic information.
* @param userId - The user identifier
* @param title - The new title
* @param hashedTitle - The hashed title
* @param subTitle - The new subtitle
* @param hashedSubTitle - The hashed subtitle
* @param summary - The new summary
* @param publicationDate - The new publication date
* @param wordCount - The new desired word count
* @param bookId - The book identifier
* @param lang - The language for error messages
* @returns true if the update was successful
*/
static updateBookBasicInformation(userId: string, title: string, hashedTitle: string, subTitle: string, hashedSubTitle: string, summary: string, publicationDate: string, wordCount: number, bookId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE erit_books SET title=?, hashed_title=?, sub_title=?, hashed_sub_title=?, summary=?, serie_id=?, desired_release_date=?, desired_word_count=?, last_update=? WHERE author_id=? AND book_id=?',
[title, hashedTitle, subTitle, hashedSubTitle, summary, 0, publicationDate ? System.dateToMySqlDate(publicationDate) : null, wordCount, System.timeStampInSeconds(), userId, bookId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour les informations du livre.` : `Unable to update book information.`);
} else {
const query: string = 'UPDATE erit_books SET title=?, hashed_title=?, sub_title=?, hashed_sub_title=?, summary=?, serie_id=?, desired_release_date=?, desired_word_count=?, last_update=? WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [title, hashedTitle, subTitle, hashedSubTitle, summary, 0, publicationDate ? System.dateToMySqlDate(publicationDate) : null, wordCount, System.timeStampInSeconds(), userId, bookId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de mettre à jour les informations du livre.' : 'Unable to update book information.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Deletes a book from the database.
* @param userId - The user identifier
* @param bookId - The book identifier to delete
* @param lang - The language for error messages
* @returns true if the deletion was successful
*/
public static deleteBook(userId: string, bookId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM erit_books WHERE author_id=? AND book_id=?', [userId,bookId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer le livre.` : `Unable to delete book.`);
} else {
const query: string = 'DELETE FROM erit_books WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de supprimer le livre.' : 'Unable to delete book.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
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.");
}
}
}
/**
* 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();
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 {
const query: string = 'SELECT book_id, type, title, sub_title, last_update FROM erit_books WHERE author_id = ?';
const params: SQLiteValue[] = [userId];
return db.all(query, params) as SyncedBookResult[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer les livres synchronisés.' : 'Unable to retrieve synced books.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Inserts a synced book from the server.
* @param bookId - The book identifier
* @param userId - The user identifier
* @param type - The book type
* @param title - The encrypted title
* @param hashedTitle - The hashed title
* @param subTitle - The encrypted subtitle
* @param hashedSubTitle - The hashed subtitle
* @param summary - The encrypted summary
* @param serieId - The series identifier
* @param desiredReleaseDate - The desired release date
* @param desiredWordCount - The desired word count
* @param wordsCount - The current word count
* @param coverImage - The cover image file name
* @param lastUpdate - The last update timestamp
* @param lang - The language for error messages
* @returns true if the insertion was successful
*/
static insertSyncBook(bookId: string, userId: string, type: string, title: string, hashedTitle: string, subTitle: string | null, hashedSubTitle: string | null, summary: string | null, serieId: number | null, desiredReleaseDate: string | null, desiredWordCount: number | null, wordsCount: number | null, coverImage: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO erit_books (book_id, author_id, type, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[bookId, userId, type, title, hashedTitle, subTitle, hashedSubTitle, summary, serieId, desiredReleaseDate, desiredWordCount, wordsCount, coverImage, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le livre synchronisé.` : `Unable to insert synced book.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
const query: string = 'INSERT INTO erit_books (book_id, author_id, type, title, hashed_title, sub_title, hashed_sub_title, summary, serie_id, desired_release_date, desired_word_count, words_count, cover_image, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
const params: SQLiteValue[] = [bookId, userId, type, title, hashedTitle, subTitle, hashedSubTitle, summary, serieId, desiredReleaseDate, desiredWordCount, wordsCount, coverImage, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible d'insérer le livre synchronisé." : 'Unable to insert synced book.');
}
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
static async fetchCompleteBookById(bookId: string, lang: "fr" | "en") {
/**
* Retrieves a complete book by its identifier (without author verification).
* @param bookId - The book identifier
* @param lang - The language for error messages
* @returns The complete book data
*/
static async fetchCompleteBookById(bookId: string, lang: 'fr' | 'en'): Promise<EritBooksTable[]> {
try {
const db: Database = System.getDb();
return db.all(`SELECT * FROM erit_books WHERE book_id = ?`, [bookId]) as EritBooksTable[];
} catch (e: unknown) {
if (e instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le livre complet.` : `Unable to retrieve complete book.`);
} else {
const query: string = 'SELECT * FROM erit_books WHERE book_id = ?';
const params: SQLiteValue[] = [bookId];
return db.all(query, params) as EritBooksTable[];
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? 'Impossible de récupérer le livre complet.' : 'Unable to retrieve complete book.');
}
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
}

View File

@@ -1,14 +1,13 @@
import {Database, QueryResult, RunResult, SQLiteValue} from 'node-sqlite3-wasm';
import System from "../System.js";
import {BookQuery, EritBooksTable, SyncedBookResult} from "@/electron/database/repositories/book.repository";
export interface ChapterQueryResult extends Record<string, SQLiteValue>{
export interface ChapterQueryResult extends Record<string, SQLiteValue> {
chapter_id: string;
title: string;
chapter_order: number;
}
export interface ActChapterQuery extends Record<string, SQLiteValue>{
export interface ActChapterQuery extends Record<string, SQLiteValue> {
chapter_info_id: number;
chapter_id: string;
title: string;
@@ -20,7 +19,7 @@ export interface ActChapterQuery extends Record<string, SQLiteValue>{
goal: string;
}
export interface ChapterStoryQueryResult extends Record<string, SQLiteValue>{
export interface ChapterStoryQueryResult extends Record<string, SQLiteValue> {
chapter_info_id: number;
act_id: number;
summary: string;
@@ -34,7 +33,7 @@ export interface ChapterStoryQueryResult extends Record<string, SQLiteValue>{
plot_summary: string;
}
export interface LastChapterResult extends Record<string, SQLiteValue>{
export interface LastChapterResult extends Record<string, SQLiteValue> {
chapter_id: string;
version: number;
}
@@ -83,413 +82,620 @@ export interface ChapterBookResult extends Record<string, SQLiteValue> {
content: string | null;
}
export default class ChapterRepo{
public static checkNameDuplication(userId:string,bookId:string,hashedTitle:string, lang: 'fr' | 'en' = 'fr'):boolean{
export default class ChapterRepo {
/**
* Checks if a chapter name already exists for a book.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param hashedTitle - The hashed chapter title
* @param lang - The language for error messages
* @returns true if a chapter with this name exists
*/
public static checkNameDuplication(userId: string, bookId: string, hashedTitle: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result = db.get('SELECT chapter_id FROM book_chapters WHERE author_id=? AND book_id=? AND hashed_title=?', [userId,bookId,hashedTitle]);
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier la duplication du nom.` : `Unable to verify name duplication.`);
} else {
const query: string = 'SELECT chapter_id FROM book_chapters WHERE author_id=? AND book_id=? AND hashed_title=?';
const params: SQLiteValue[] = [userId, bookId, hashedTitle];
const chapter: QueryResult | null = db.get(query, params);
return chapter !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de vérifier la duplication du nom.' : 'Unable to verify name duplication.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Inserts a new chapter into the database.
* @param chapterId - The chapter identifier
* @param userId - The user identifier
* @param bookId - The book identifier
* @param title - The encrypted chapter title
* @param hashedTitle - The hashed chapter title
* @param wordsCount - The word count
* @param chapterOrder - The chapter order position
* @param lang - The language for error messages
* @returns The created chapter identifier
*/
public static insertChapter(chapterId: string, userId: string, bookId: string, title: string, hashedTitle: string, wordsCount: number, chapterOrder: number, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
let insertResult: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO book_chapters (chapter_id, author_id, book_id, title, hashed_title, words_count, chapter_order, last_update) VALUES (?,?,?,?,?,?,?,?)', [chapterId, userId, bookId, title, hashedTitle, wordsCount, chapterOrder, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter le chapitre.` : `Unable to add chapter.`);
} else {
const query: string = 'INSERT INTO book_chapters (chapter_id, author_id, book_id, title, hashed_title, words_count, chapter_order, last_update) VALUES (?,?,?,?,?,?,?,?)';
const params: SQLiteValue[] = [chapterId, userId, bookId, title, hashedTitle, wordsCount, chapterOrder, System.timeStampInSeconds()];
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible d'ajouter le chapitre." : 'Unable to add chapter.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est passé lors de l'ajout du chapitre.` : `Error adding chapter.`);
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? "Une erreur s'est passé lors de l'ajout du chapitre." : 'Error adding chapter.');
}
return chapterId;
}
public static fetchAllChapterForActs(userId:string,bookId:string, lang: 'fr' | 'en' = 'fr'):ActChapterQuery[]{
/**
* Retrieves all chapters with their act information for a book.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param lang - The language for error messages
* @returns List of chapters with act information
*/
public static fetchAllChapterForActs(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ActChapterQuery[] {
try {
const db: Database = System.getDb();
return db.all('SELECT ci.chapter_info_id AS chapter_info_id, ci.chapter_id AS chapter_id, chapter.title, chapter.chapter_order, ci.act_id, ci.incident_id AS incident_id, ci.plot_point_id AS plot_point_id, ci.summary, ci.goal FROM book_chapter_infos AS ci INNER JOIN book_chapters AS chapter ON chapter.chapter_id = ci.chapter_id WHERE ci.book_id = ? AND ci.author_id = ?', [bookId, userId]) as ActChapterQuery[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres pour les actes.` : `Unable to retrieve chapters for acts.`);
} else {
const query: string = 'SELECT ci.chapter_info_id AS chapter_info_id, ci.chapter_id AS chapter_id, chapter.title, chapter.chapter_order, ci.act_id, ci.incident_id AS incident_id, ci.plot_point_id AS plot_point_id, ci.summary, ci.goal FROM book_chapter_infos AS ci INNER JOIN book_chapters AS chapter ON chapter.chapter_id = ci.chapter_id WHERE ci.book_id = ? AND ci.author_id = ?';
const params: SQLiteValue[] = [bookId, userId];
return db.all(query, params) as ActChapterQuery[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres pour les actes.' : 'Unable to retrieve chapters for acts.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Retrieves all chapters from a book ordered by chapter order.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param lang - The language for error messages
* @returns List of chapters
*/
public static fetchAllChapterFromABook(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterQueryResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT chapter_id, title, chapter_order FROM book_chapters WHERE book_id=? AND author_id=? ORDER BY chapter_order', [bookId, userId]) as ChapterQueryResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres.` : `Unable to retrieve chapters.`);
} else {
const query: string = 'SELECT chapter_id, title, chapter_order FROM book_chapters WHERE book_id=? AND author_id=? ORDER BY chapter_order';
const params: SQLiteValue[] = [bookId, userId];
return db.all(query, params) as ChapterQueryResult[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres.' : 'Unable to retrieve chapters.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Deletes a chapter from the database.
* @param userId - The user identifier
* @param chapterId - The chapter identifier to delete
* @param lang - The language for error messages
* @returns true if the deletion was successful
*/
public static deleteChapter(userId: string, chapterId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_chapters WHERE author_id=? AND chapter_id=?', [userId, chapterId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer le chapitre.` : `Unable to delete chapter.`);
} else {
const query: string = 'DELETE FROM book_chapters WHERE author_id=? AND chapter_id=?';
const params: SQLiteValue[] = [userId, chapterId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de supprimer le chapitre.' : 'Unable to delete chapter.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Inserts chapter information linking a chapter to an act.
* @param chapterInfoId - The chapter info identifier
* @param userId - The user identifier
* @param chapterId - The chapter identifier
* @param actId - The act identifier
* @param bookId - The book identifier
* @param plotId - The plot point identifier (optional)
* @param incidentId - The incident identifier (optional)
* @param lang - The language for error messages
* @returns The created chapter info identifier
*/
static insertChapterInformation(chapterInfoId: string, userId: string, chapterId: string, actId: number, bookId: string, plotId: string | null, incidentId: string | null, lang: 'fr' | 'en' = 'fr'): string {
let existResult;
let result: RunResult;
let existingChapter: QueryResult | null;
let insertResult: RunResult;
try {
const db: Database = System.getDb();
existResult = db.get('SELECT chapter_info_id FROM book_chapter_infos WHERE chapter_id=? AND act_id=? AND book_id=? AND plot_point_id=? AND incident_id=? AND author_id=?', [chapterId, actId, bookId, plotId, incidentId, userId]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'information du chapitre.` : `Unable to verify chapter information existence.`);
} else {
const checkQuery: string = 'SELECT chapter_info_id FROM book_chapter_infos WHERE chapter_id=? AND act_id=? AND book_id=? AND plot_point_id=? AND incident_id=? AND author_id=?';
const checkParams: SQLiteValue[] = [chapterId, actId, bookId, plotId, incidentId, userId];
existingChapter = db.get(checkQuery, checkParams);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence de l'information du chapitre." : 'Unable to verify chapter information existence.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (existResult !== null) {
throw new Error(lang === 'fr' ? `Le chapitre est déjà lié.` : `Chapter is already linked.`);
if (existingChapter !== null) {
throw new Error(lang === 'fr' ? 'Le chapitre est déjà lié.' : 'Chapter is already linked.');
}
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, book_id, author_id, incident_id, plot_point_id, summary, goal, last_update) VALUES (?,?,?,?,?,?,?,?,?,?)', [chapterInfoId, chapterId, actId, bookId, userId, incidentId, plotId, '', '', System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter l'information du chapitre.` : `Unable to add chapter information.`);
} else {
const query: string = 'INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, book_id, author_id, incident_id, plot_point_id, summary, goal, last_update) VALUES (?,?,?,?,?,?,?,?,?,?)';
const params: SQLiteValue[] = [chapterInfoId, chapterId, actId, bookId, userId, incidentId, plotId, '', '', System.timeStampInSeconds()];
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible d'ajouter l'information du chapitre." : 'Unable to add chapter information.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite pendant la liaison du chapitre.` : `Error linking chapter.`);
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? "Une erreur s'est produite pendant la liaison du chapitre." : 'Error linking chapter.');
}
return chapterInfoId;
}
public static updateChapter(userId: string, chapterId: string, encryptedTitle: string, hashTitle: string, chapterOrder: number, lastUpdate:number, lang: 'fr' | 'en' = 'fr'): boolean {
/**
* Updates a chapter's basic information.
* @param userId - The user identifier
* @param chapterId - The chapter identifier
* @param encryptedTitle - The encrypted title
* @param hashTitle - The hashed title
* @param chapterOrder - The chapter order position
* @param lastUpdate - The last update timestamp
* @param lang - The language for error messages
* @returns true if the update was successful
*/
public static updateChapter(userId: string, chapterId: string, encryptedTitle: string, hashTitle: string, chapterOrder: number, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=?, last_update=? WHERE author_id=? AND chapter_id=?', [encryptedTitle, hashTitle, chapterOrder, lastUpdate, userId, chapterId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour le chapitre.` : `Unable to update chapter.`);
} else {
const query: string = 'UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=?, last_update=? WHERE author_id=? AND chapter_id=?';
const params: SQLiteValue[] = [encryptedTitle, hashTitle, chapterOrder, lastUpdate, userId, chapterId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de mettre à jour le chapitre.' : 'Unable to update chapter.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Updates chapter information (summary and goal).
* @param userId - The user identifier
* @param chapterId - The chapter identifier
* @param actId - The act identifier
* @param bookId - The book identifier
* @param incidentId - The incident identifier (optional)
* @param plotId - The plot point identifier (optional)
* @param summary - The chapter summary
* @param goal - The chapter goal
* @param lastUpdate - The last update timestamp
* @param lang - The language for error messages
* @returns true if the update was successful
*/
public static updateChapterInfos(userId: string, chapterId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, summary: string, goal: string | null, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
let sql: string = `UPDATE book_chapter_infos SET summary=?,goal=?,last_update=? WHERE chapter_id = ? AND act_id = ? AND book_id = ?`;
const params: (string|null|number)[] = [summary, goal, lastUpdate, chapterId, actId, bookId];
let query: string = 'UPDATE book_chapter_infos SET summary=?,goal=?,last_update=? WHERE chapter_id = ? AND act_id = ? AND book_id = ?';
const params: SQLiteValue[] = [summary, goal, lastUpdate, chapterId, actId, bookId];
if (incidentId) {
sql += ` AND incident_id=?`;
query += ' AND incident_id=?';
params.push(incidentId);
} else {
sql += ` AND incident_id IS NULL`;
query += ' AND incident_id IS NULL';
}
if (plotId) {
sql += ` AND plot_point_id=?`;
query += ' AND plot_point_id=?';
params.push(plotId);
} else {
sql += ` AND plot_point_id IS NULL`;
query += ' AND plot_point_id IS NULL';
}
sql += ` AND author_id=?`;
query += ' AND author_id=?';
params.push(userId);
const result: RunResult = db.run(sql, params);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour les informations du chapitre.` : `Unable to update chapter information.`);
} else {
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de mettre à jour les informations du chapitre.' : 'Unable to update chapter information.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Retrieves the last opened chapter for a book.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param lang - The language for error messages
* @returns The last chapter information or null
*/
public static fetchLastChapter(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LastChapterResult | null {
try {
const db: Database = System.getDb();
const result = db.get('SELECT chapter_id as chapter_id,version FROM user_last_chapter WHERE user_id=? AND book_id=?', [userId, bookId]) as LastChapterResult | null;
return result;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer le dernier chapitre ouvert.` : `Unable to retrieve last opened chapter.`);
} else {
const query: string = 'SELECT chapter_id as chapter_id,version FROM user_last_chapter WHERE user_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
return db.get(query, params) as LastChapterResult | null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer le dernier chapitre ouvert.' : 'Unable to retrieve last opened chapter.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Updates or inserts the last chapter record for a user.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param chapterId - The chapter identifier
* @param version - The chapter version
* @param lang - The language for error messages
* @returns true if the operation was successful
*/
public static updateLastChapterRecord(userId: string, bookId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE user_last_chapter SET chapter_id=?, version=? WHERE user_id=? AND book_id=?', [chapterId, version, userId, bookId]);
if (result.changes > 0) {
const updateQuery: string = 'UPDATE user_last_chapter SET chapter_id=?, version=? WHERE user_id=? AND book_id=?';
const updateParams: SQLiteValue[] = [chapterId, version, userId, bookId];
const updateResult: RunResult = db.run(updateQuery, updateParams);
if (updateResult.changes > 0) {
return true;
} else {
const insertResult: RunResult = db.run('INSERT INTO user_last_chapter (user_id, book_id, chapter_id, version) VALUES (?,?,?,?)', [userId, bookId, chapterId, version]);
return insertResult.changes > 0;
}
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'enregistrer le dernier chapitre.` : `Unable to save last chapter.`);
} else {
const insertQuery: string = 'INSERT INTO user_last_chapter (user_id, book_id, chapter_id, version) VALUES (?,?,?,?)';
const insertParams: SQLiteValue[] = [userId, bookId, chapterId, version];
const insertResult: RunResult = db.run(insertQuery, insertParams);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible d'enregistrer le dernier chapitre." : 'Unable to save last chapter.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
public static fetchChapterStory(userId: string, chapterId: string, lang: 'fr' | 'en' = 'fr'):ChapterStoryQueryResult[] {
/**
* Retrieves chapter story information including act, incident, and plot point data.
* @param userId - The user identifier
* @param chapterId - The chapter identifier
* @param lang - The language for error messages
* @returns List of chapter story information
*/
public static fetchChapterStory(userId: string, chapterId: string, lang: 'fr' | 'en' = 'fr'): ChapterStoryQueryResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT chapter_info_id, chapter.act_id, act_sum.summary, chapter.summary AS chapter_summary, chapter.goal AS chapter_goal, chapter.incident_id, incident.title AS incident_title, incident.summary AS incident_summary, chapter.plot_point_id, plot.title AS plot_title, plot.summary AS plot_summary FROM book_chapter_infos AS chapter LEFT JOIN book_incidents AS incident ON chapter.incident_id=incident.incident_id LEFT JOIN book_plot_points AS plot ON chapter.plot_point_id=plot.plot_point_id LEFT JOIN book_act_summaries AS act_sum ON chapter.act_id=act_sum.act_sum_id AND chapter.book_id=act_sum.book_id WHERE chapter.chapter_id=? AND chapter.author_id=?', [chapterId, userId]) as ChapterStoryQueryResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer l'histoire du chapitre.` : `Unable to retrieve chapter story.`);
} else {
const query: string = 'SELECT chapter_info_id, chapter.act_id, act_sum.summary, chapter.summary AS chapter_summary, chapter.goal AS chapter_goal, chapter.incident_id, incident.title AS incident_title, incident.summary AS incident_summary, chapter.plot_point_id, plot.title AS plot_title, plot.summary AS plot_summary FROM book_chapter_infos AS chapter LEFT JOIN book_incidents AS incident ON chapter.incident_id=incident.incident_id LEFT JOIN book_plot_points AS plot ON chapter.plot_point_id=plot.plot_point_id LEFT JOIN book_act_summaries AS act_sum ON chapter.act_id=act_sum.act_sum_id AND chapter.book_id=act_sum.book_id WHERE chapter.chapter_id=? AND chapter.author_id=?';
const params: SQLiteValue[] = [chapterId, userId];
return db.all(query, params) as ChapterStoryQueryResult[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible de récupérer l'histoire du chapitre." : 'Unable to retrieve chapter story.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Deletes chapter information by its identifier.
* @param userId - The user identifier
* @param chapterInfoId - The chapter info identifier to delete
* @param lang - The language for error messages
* @returns true if the deletion was successful
*/
static deleteChapterInformation(userId: string, chapterInfoId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_chapter_infos WHERE chapter_info_id=? AND author_id=?', [chapterInfoId, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer les informations du chapitre.` : `Unable to delete chapter information.`);
} else {
const query: string = 'DELETE FROM book_chapter_infos WHERE chapter_info_id=? AND author_id=?';
const params: SQLiteValue[] = [chapterInfoId, userId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de supprimer les informations du chapitre.' : 'Unable to delete chapter information.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static isChapterExist(userId: string, chapter_id: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM book_chapters WHERE chapter_id=? AND author_id=?', [chapter_id, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du chapitre.` : `Unable to check chapter existence.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static isChapterInfoExist(userId: string, chapter_id: string, lang: "fr" | "en"): boolean {
/**
* Checks if a chapter exists.
* @param userId - The user identifier
* @param chapterId - The chapter identifier
* @param lang - The language for error messages
* @returns true if the chapter exists
*/
static isChapterExist(userId: string, chapterId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `book_chapter_infos` WHERE `chapter_id`=? AND `author_id`=?', [chapter_id, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence des informations du chapitre.` : `Unable to check chapter info existence.`);
} else {
const query: string = 'SELECT 1 FROM book_chapters WHERE chapter_id=? AND author_id=?';
const params: SQLiteValue[] = [chapterId, userId];
const chapter: QueryResult | null = db.get(query, params) || null;
return chapter !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence du chapitre." : 'Unable to check chapter existence.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static fetchCompleteBookChapters(id: string, lang: 'fr' | 'en'): ChapterBookResult[] {
let result: ChapterBookResult[];
/**
* Checks if chapter info exists.
* @param userId - The user identifier
* @param chapterId - The chapter identifier
* @param lang - The language for error messages
* @returns true if the chapter info exists
*/
static isChapterInfoExist(userId: string, chapterId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
result = db.all('SELECT title, chapter_order, content.content FROM book_chapters AS chapter LEFT JOIN book_chapter_content AS content ON chapter.chapter_id = content.chapter_id AND content.version = (SELECT MAX(version) FROM book_chapter_content WHERE chapter_id = chapter.chapter_id AND version > 1) WHERE chapter.book_id = ? ORDER BY chapter.chapter_order', [id]) as ChapterBookResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres.` : `Unable to retrieve chapters.`);
} else {
const query: string = 'SELECT 1 FROM book_chapter_infos WHERE chapter_id=? AND author_id=?';
const params: SQLiteValue[] = [chapterId, userId];
const chapterInfo: QueryResult | null = db.get(query, params) || null;
return chapterInfo !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible de vérifier l'existence des informations du chapitre." : 'Unable to check chapter info existence.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.length === 0) {
throw new Error(lang === 'fr' ? `Aucun chapitre trouvé.` : `No chapters found.`);
/**
* Retrieves complete book chapters with their content.
* @param bookId - The book identifier
* @param lang - The language for error messages
* @returns List of chapters with content
*/
static fetchCompleteBookChapters(bookId: string, lang: 'fr' | 'en'): ChapterBookResult[] {
let chapters: ChapterBookResult[];
try {
const db: Database = System.getDb();
const query: string = 'SELECT title, chapter_order, content.content FROM book_chapters AS chapter LEFT JOIN book_chapter_content AS content ON chapter.chapter_id = content.chapter_id AND content.version = (SELECT MAX(version) FROM book_chapter_content WHERE chapter_id = chapter.chapter_id AND version > 1) WHERE chapter.book_id = ? ORDER BY chapter.chapter_order';
const params: SQLiteValue[] = [bookId];
chapters = db.all(query, params) as ChapterBookResult[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres.' : 'Unable to retrieve chapters.');
}
return result;
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
if (chapters.length === 0) {
throw new Error(lang === 'fr' ? 'Aucun chapitre trouvé.' : 'No chapters found.');
}
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[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update FROM book_chapters WHERE author_id=? AND book_id=?', [userId, bookId]) as BookChaptersTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres.` : `Unable to retrieve chapters.`);
} else {
const query: string = 'SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update FROM book_chapters WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
return db.all(query, params) as BookChaptersTable[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres.' : 'Unable to retrieve chapters.');
}
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Retrieves chapter information for a specific chapter.
* @param userId - The user identifier
* @param chapterId - The chapter identifier
* @param lang - The language for error messages
* @returns List of chapter info records
*/
static async fetchBookChapterInfos(userId: string, chapterId: string, lang: 'fr' | 'en'): Promise<BookChapterInfosTable[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update FROM book_chapter_infos WHERE author_id=? AND chapter_id=?', [userId, chapterId]) as BookChapterInfosTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les infos des chapitres.` : `Unable to retrieve chapter infos.`);
} else {
const query: string = 'SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update FROM book_chapter_infos WHERE author_id=? AND chapter_id=?';
const params: SQLiteValue[] = [userId, chapterId];
return db.all(query, params) as BookChapterInfosTable[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer les infos des chapitres.' : 'Unable to retrieve chapter infos.');
}
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Retrieves synced chapters for a user.
* @param userId - The user identifier
* @param lang - The language for error messages
* @returns List of synced chapters
*/
static fetchSyncedChapters(userId: string, lang: 'fr' | 'en'): SyncedChapterResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT chapter_id, book_id, title, last_update FROM book_chapters WHERE author_id = ?', [userId]) as SyncedChapterResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les chapitres synchronisés.` : `Unable to retrieve synced chapters.`);
} else {
const query: string = 'SELECT chapter_id, book_id, title, last_update FROM book_chapters WHERE author_id = ?';
const params: SQLiteValue[] = [userId];
return db.all(query, params) as SyncedChapterResult[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer les chapitres synchronisés.' : 'Unable to retrieve synced chapters.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Retrieves synced chapter infos for a user.
* @param userId - The user identifier
* @param lang - The language for error messages
* @returns List of synced chapter infos
*/
static fetchSyncedChapterInfos(userId: string, lang: 'fr' | 'en'): SyncedChapterInfoResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT chapter_info_id, chapter_id, book_id, last_update FROM book_chapter_infos WHERE author_id = ?', [userId]) as SyncedChapterInfoResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les infos des chapitres synchronisés.` : `Unable to retrieve synced chapter infos.`);
} else {
const query: string = 'SELECT chapter_info_id, chapter_id, book_id, last_update FROM book_chapter_infos WHERE author_id = ?';
const params: SQLiteValue[] = [userId];
return db.all(query, params) as SyncedChapterInfoResult[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? 'Impossible de récupérer les infos des chapitres synchronisés.' : 'Unable to retrieve synced chapter infos.');
}
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Inserts a synced chapter from the server.
* @param chapterId - The chapter identifier
* @param bookId - The book identifier
* @param authorId - The author identifier
* @param title - The encrypted title
* @param hashedTitle - The hashed title
* @param wordsCount - The word count
* @param chapterOrder - The chapter order
* @param lastUpdate - The last update timestamp
* @param lang - The language for error messages
* @returns true if the insertion was successful
*/
static insertSyncChapter(chapterId: string, bookId: string, authorId: string, title: string, hashedTitle: string | null, wordsCount: number | null, chapterOrder: number | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_chapters (chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[chapterId, bookId, authorId, title, hashedTitle, wordsCount, chapterOrder, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le chapitre.` : `Unable to insert chapter.`);
} else {
const query: string = 'INSERT INTO book_chapters (chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?)';
const params: SQLiteValue[] = [chapterId, bookId, authorId, title, hashedTitle, wordsCount, chapterOrder, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible d'insérer le chapitre." : 'Unable to insert chapter.');
}
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Inserts synced chapter info from the server.
* @param chapterInfoId - The chapter info identifier
* @param chapterId - The chapter identifier
* @param actId - The act identifier
* @param incidentId - The incident identifier
* @param plotPointId - The plot point identifier
* @param bookId - The book identifier
* @param authorId - The author identifier
* @param summary - The chapter summary
* @param goal - The chapter goal
* @param lastUpdate - The last update timestamp
* @param lang - The language for error messages
* @returns true if the insertion was successful
*/
static insertSyncChapterInfo(chapterInfoId: string, chapterId: string, actId: number | null, incidentId: string | null, plotPointId: string | null, bookId: string, authorId: string, summary: string | null, goal: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(`INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [chapterInfoId, chapterId, actId, incidentId, plotPointId, bookId, authorId, summary, goal, lastUpdate]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer les infos du chapitre.` : `Unable to insert chapter info.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
const query: string = 'INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
const params: SQLiteValue[] = [chapterInfoId, chapterId, actId, incidentId, plotPointId, bookId, authorId, summary, goal, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? "Impossible d'insérer les infos du chapitre." : 'Unable to insert chapter info.');
}
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
static async fetchCompleteChapterById(id: string, lang: "fr" | "en"):Promise<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 {
const db: Database = System.getDb();
return db.all(
`SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update
FROM book_chapters
WHERE chapter_id = ?`,
[id]
) as BookChaptersTable[];
} catch (e:unknown){
if (e instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le chapitre complet.` : `Unable to retrieve complete chapter.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
const query: string = 'SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update FROM book_chapters WHERE chapter_id = ?';
const params: SQLiteValue[] = [chapterId];
return db.all(query, params) as BookChaptersTable[];
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? 'Impossible de récupérer le chapitre complet.' : 'Unable to retrieve complete chapter.');
}
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
static async fetchCompleteChapterInfoById(id: string, lang: "fr" | "en"):Promise<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 {
const db: Database = System.getDb();
return db.all(
`SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update
FROM book_chapter_infos
WHERE chapter_info_id = ?`,
[id]
) as BookChapterInfosTable[];
} catch (e:unknown){
if (e instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer les informations de chapitre complètes.` : `Unable to retrieve complete chapter info.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
const query: string = 'SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update FROM book_chapter_infos WHERE chapter_info_id = ?';
const params: SQLiteValue[] = [chapterInfoId];
return db.all(query, params) as BookChapterInfosTable[];
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? 'Impossible de récupérer les informations de chapitre complètes.' : 'Unable to retrieve complete chapter info.');
}
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}

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";
export interface ChapterContentQueryResult extends Record<string, SQLiteValue>{
export interface ChapterContentQueryResult extends Record<string, SQLiteValue> {
chapter_id: string;
version: number;
content: string;
@@ -14,7 +14,7 @@ export interface ContentQueryResult extends Record<string, SQLiteValue> {
content: string;
}
export interface CompanionContentQueryResult extends Record<string, SQLiteValue>{
export interface CompanionContentQueryResult extends Record<string, SQLiteValue> {
version: number;
content: string;
words_count: number;
@@ -38,14 +38,36 @@ export interface SyncedChapterContentResult extends Record<string, SQLiteValue>
}
export default class ChapterContentRepository {
/**
* Fetches the last chapter content for a given book.
* @param userId - The ID of the user/author.
* @param bookId - The ID of the book.
* @param lang - The language for error messages ('fr' or 'en').
* @returns An array of chapter content results ordered by chapter order and version descending.
*/
public static fetchLastChapterContent(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterContentQueryResult[] {
try {
const db: Database = System.getDb();
const query: string = `SELECT book_chapters.chapter_id as chapter_id, COALESCE(book_chapter_content.version, 2) AS version, COALESCE(book_chapter_content.content, '') AS content, COALESCE(book_chapter_content.words_count, 0) AS words_count, book_chapters.title, book_chapters.chapter_order FROM book_chapters LEFT JOIN book_chapter_content ON book_chapters.chapter_id = book_chapter_content.chapter_id WHERE book_chapters.author_id = ? AND book_chapters.book_id = ? ORDER BY book_chapters.chapter_order DESC, book_chapter_content.version DESC LIMIT 1`;
return db.all(query, [userId, bookId]) as ChapterContentQueryResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
SELECT
book_chapters.chapter_id as chapter_id,
COALESCE(book_chapter_content.version, 2) AS version,
COALESCE(book_chapter_content.content, '') AS content,
COALESCE(book_chapter_content.words_count, 0) AS words_count,
book_chapters.title,
book_chapters.chapter_order
FROM book_chapters
LEFT JOIN book_chapter_content ON book_chapters.chapter_id = book_chapter_content.chapter_id
WHERE book_chapters.author_id = ? AND book_chapters.book_id = ?
ORDER BY book_chapters.chapter_order DESC, book_chapter_content.version DESC
LIMIT 1
`;
const params: SQLiteValue[] = [userId, bookId];
const chapterContents: ChapterContentQueryResult[] = db.all(query, params) as ChapterContentQueryResult[];
return chapterContents;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer le dernier chapitre.` : `Unable to retrieve last chapter.`);
} else {
console.error("An unknown error occurred.");
@@ -53,20 +75,37 @@ export default class ChapterContentRepository {
}
}
}
/**
* Updates the content of a chapter. If no existing content is found, inserts a new record.
* @param userId - The ID of the user/author.
* @param chapterId - The ID of the chapter.
* @param version - The version number of the content.
* @param encryptContent - The encrypted content string.
* @param wordsCount - The word count of the content.
* @param lastUpdate - The timestamp of the last update.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the update or insert was successful.
*/
public static updateChapterContent(userId: string, chapterId: string, version: number, encryptContent: string, wordsCount: number, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_chapter_content SET content=?, words_count=?, last_update=? WHERE chapter_id=? AND author_id=? AND version=?', [encryptContent, wordsCount, lastUpdate, chapterId, userId, version]);
if (result.changes > 0) {
const updateQuery: string = 'UPDATE book_chapter_content SET content=?, words_count=?, last_update=? WHERE chapter_id=? AND author_id=? AND version=?';
const updateParams: SQLiteValue[] = [encryptContent, wordsCount, lastUpdate, chapterId, userId, version];
const updateResult: RunResult = db.run(updateQuery, updateParams);
if (updateResult.changes > 0) {
return true;
} else {
const contentId:string = System.createUniqueId();
const insertResult: RunResult = db.run('INSERT INTO book_chapter_content (content_id,chapter_id, author_id, version, content, words_count, last_update) VALUES (?,?,?,?,?,?,?)', [contentId, chapterId, userId, version, encryptContent, wordsCount, lastUpdate]);
const contentId: string = System.createUniqueId();
const insertQuery: string = 'INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, last_update) VALUES (?,?,?,?,?,?,?)';
const insertParams: SQLiteValue[] = [contentId, chapterId, userId, version, encryptContent, wordsCount, lastUpdate];
const insertResult: RunResult = db.run(insertQuery, insertParams);
return insertResult.changes > 0;
}
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour le contenu du chapitre.` : `Unable to update chapter content.`);
} else {
console.error("An unknown error occurred.");
@@ -75,13 +114,24 @@ export default class ChapterContentRepository {
}
}
static fetchCompanionContent(userId: string, chapterIdNum: string, versionNum: number, lang: 'fr' | 'en' = 'fr'): CompanionContentQueryResult[] {
/**
* Fetches companion content for a specific chapter and version.
* @param userId - The ID of the user/author.
* @param chapterId - The ID of the chapter.
* @param version - The version number to fetch.
* @param lang - The language for error messages ('fr' or 'en').
* @returns An array of companion content results.
*/
static fetchCompanionContent(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): CompanionContentQueryResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT version, content, words_count FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?', [userId, chapterIdNum, versionNum]) as CompanionContentQueryResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT version, content, words_count FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?';
const params: SQLiteValue[] = [userId, chapterId, version];
const companionContents: CompanionContentQueryResult[] = db.all(query, params) as CompanionContentQueryResult[];
return companionContents;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu compagnon.` : `Unable to retrieve companion content.`);
} else {
console.error("An unknown error occurred.");
@@ -89,53 +139,91 @@ export default class ChapterContentRepository {
}
}
}
/**
* Fetches chapter content by its order position within a book.
* @param userId - The ID of the user/author.
* @param chapterOrder - The order position of the chapter.
* @param bookId - The ID of the book.
* @param lang - The language for error messages ('fr' or 'en').
* @returns The content query result for the specified chapter.
* @throws Error if no chapter is found with the specified order.
*/
static fetchChapterContentByChapterOrder(userId: string, chapterOrder: number, bookId: string, lang: 'fr' | 'en' = 'fr'): ContentQueryResult {
let result: ContentQueryResult | null;
let chapterContent: ContentQueryResult | null;
try {
const db: Database = System.getDb();
result = db.get('SELECT content.content FROM book_chapters as chapter INNER JOIN book_chapter_content AS content ON chapter.chapter_id=content.chapter_id WHERE chapter.chapter_order=? AND content.version=2 AND chapter.book_id=? AND chapter.author_id=?', [chapterOrder, bookId, userId]) as ContentQueryResult | null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
SELECT content.content
FROM book_chapters as chapter
INNER JOIN book_chapter_content AS content ON chapter.chapter_id=content.chapter_id
WHERE chapter.chapter_order=? AND content.version=2 AND chapter.book_id=? AND chapter.author_id=?
`;
const params: SQLiteValue[] = [chapterOrder, bookId, userId];
chapterContent = db.get(query, params) as ContentQueryResult | null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu du chapitre.` : `Unable to retrieve chapter content.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result) {
if (!chapterContent) {
throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cet ordre.` : `No chapter found with this order.`);
}
return result;
return chapterContent;
}
static fetchChapterContentByVersion(userId: string, chapterid: string, version: number, lang: 'fr' | 'en' = 'fr'): ContentQueryResult {
let result: ContentQueryResult | null;
/**
* Fetches chapter content by chapter ID and version number.
* @param userId - The ID of the user/author.
* @param chapterId - The ID of the chapter.
* @param version - The version number to fetch.
* @param lang - The language for error messages ('fr' or 'en').
* @returns The content query result for the specified version.
* @throws Error if no chapter is found with the specified version.
*/
static fetchChapterContentByVersion(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): ContentQueryResult {
let chapterContent: ContentQueryResult | null;
try {
const db: Database = System.getDb();
result = db.get('SELECT content FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?', [userId, chapterid, version]) as ContentQueryResult | null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT content FROM book_chapter_content WHERE author_id=? AND chapter_id=? AND version=?';
const params: SQLiteValue[] = [userId, chapterId, version];
chapterContent = db.get(query, params) as ContentQueryResult | null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu du chapitre.` : `Unable to retrieve chapter content.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result) {
if (!chapterContent) {
throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cette version.` : `No chapter found with this version.`);
}
return result;
return chapterContent;
}
static isChapterContentExist(userId: string, content_id: string, lang: "fr" | "en"): boolean {
/**
* Checks whether chapter content exists for a given content ID and user.
* @param userId - The ID of the user/author.
* @param contentId - The ID of the content to check.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the chapter content exists, false otherwise.
*/
static isChapterContentExist(userId: string, contentId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `book_chapter_content` WHERE `content_id`=? AND `author_id`=?', [content_id, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM `book_chapter_content` WHERE `content_id`=? AND `author_id`=?';
const params: SQLiteValue[] = [contentId, userId];
const existenceCheck: QueryResult | null = db.get(query, params) || null;
return existenceCheck !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du contenu du chapitre.` : `Unable to check chapter content existence.`);
} else {
console.error("An unknown error occurred.");
@@ -143,26 +231,47 @@ export default class ChapterContentRepository {
}
}
}
static async fetchBookChapterContents(userId: string,chapterId:string, lang: 'fr' | 'en'): Promise<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 {
const db: Database = System.getDb();
return db.all('SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE author_id=? AND chapter_id=?', [userId, chapterId]) as BookChapterContentTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE author_id=? AND chapter_id=?';
const params: SQLiteValue[] = [userId, chapterId];
const bookChapterContents: BookChapterContentTable[] = db.all(query, params) as BookChapterContentTable[];
return bookChapterContents;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu des chapitres.` : `Unable to retrieve chapter contents.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Fetches all synced chapter contents for a user (content ID, chapter ID, and last update timestamp).
* @param userId - The ID of the user/author.
* @param lang - The language for error messages ('fr' or 'en').
* @returns An array of synced chapter content results.
*/
static fetchSyncedChapterContents(userId: string, lang: 'fr' | 'en'): SyncedChapterContentResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT content_id, chapter_id, last_update FROM book_chapter_content WHERE author_id = ?', [userId]) as SyncedChapterContentResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT content_id, chapter_id, last_update FROM book_chapter_content WHERE author_id = ?';
const params: SQLiteValue[] = [userId];
const syncedChapterContents: SyncedChapterContentResult[] = db.all(query, params) as SyncedChapterContentResult[];
return syncedChapterContents;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu des chapitres synchronisés.` : `Unable to retrieve synced chapter contents.`);
} else {
console.error("An unknown error occurred.");
@@ -170,54 +279,101 @@ export default class ChapterContentRepository {
}
}
}
/**
* Inserts a new chapter content record during synchronization.
* @param contentId - The unique ID for the content.
* @param chapterId - The ID of the chapter.
* @param authorId - The ID of the author.
* @param version - The version number of the content.
* @param content - The content string (can be null).
* @param wordsCount - The word count of the content.
* @param timeOnIt - The time spent on this content.
* @param lastUpdate - The timestamp of the last update.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the insert was successful.
*/
static insertSyncChapterContent(contentId: string, chapterId: string, authorId: string, version: number, content: string | null, wordsCount: number, timeOnIt: number, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[contentId, chapterId, authorId, version, content, wordsCount, timeOnIt, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [contentId, chapterId, authorId, version, content, wordsCount, timeOnIt, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le contenu du chapitre.` : `Unable to insert chapter content.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static async fetchCompleteChapterContentById(id: string, lang: "fr" | "en"):Promise<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 {
const db: Database = System.getDb();
return db.all(`SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE content_id = ?`, [id]) as BookChapterContentTable[];
} catch (e:unknown){
if (e instanceof Error) {
const query: string = 'SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE content_id = ?';
const params: SQLiteValue[] = [contentId];
const completeChapterContent: BookChapterContentTable[] = db.all(query, params) as BookChapterContentTable[];
return completeChapterContent;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu de chapitre complet.` : `Unable to retrieve complete chapter content.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Fetches a complete chapter with its content by joining chapters and chapter content tables.
* @param userId - The ID of the user/author.
* @param chapterId - The ID of the chapter.
* @param version - The version number of the content to fetch.
* @param lang - The language for error messages ('fr' or 'en').
* @returns The chapter content query result with chapter metadata.
* @throws Error if no chapter is found with the specified ID.
*/
public static fetchWholeChapter(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): ChapterContentQueryResult {
let result: ChapterContentQueryResult | null;
let wholeChapter: ChapterContentQueryResult | null;
try {
const db: Database = System.getDb();
const query: string = 'SELECT chapter.chapter_id as chapter_id, chapter.title as title, chapter.chapter_order, chapter.words_count, content.content AS content, content.version as version FROM book_chapters AS chapter LEFT JOIN book_chapter_content AS content ON content.chapter_id = chapter.chapter_id AND content.version = ? WHERE chapter.chapter_id = ? AND chapter.author_id = ?';
result = db.get(query, [version, chapterId, userId]) as ChapterContentQueryResult | null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
SELECT
chapter.chapter_id as chapter_id,
chapter.title as title,
chapter.chapter_order,
chapter.words_count,
content.content AS content,
content.version as version
FROM book_chapters AS chapter
LEFT JOIN book_chapter_content AS content ON content.chapter_id = chapter.chapter_id AND content.version = ?
WHERE chapter.chapter_id = ? AND chapter.author_id = ?
`;
const params: SQLiteValue[] = [version, chapterId, userId];
wholeChapter = db.get(query, params) as ChapterContentQueryResult | null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer le chapitre.` : `Unable to retrieve chapter.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result) {
if (!wholeChapter) {
throw new Error(lang === 'fr' ? `Aucun chapitre trouvé avec cet ID.` : `No chapter found with this ID.`);
}
return result;
return wholeChapter;
}
}

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";
export interface BookCharactersTable extends Record<string, SQLiteValue> {
@@ -71,71 +71,125 @@ export interface CompleteCharacterResult extends Record<string, SQLiteValue> {
}
export default class CharacterRepo {
/**
* Fetches all characters for a specific book and user.
* @param userId - The unique identifier of the user
* @param bookId - The unique identifier of the book
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of character results
*/
public static fetchCharacters(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): CharacterResult[] {
let result: CharacterResult[];
try {
const db: Database = System.getDb();
result = db.all('SELECT character_id, first_name, last_name, title, category, image, role, biography, history FROM book_characters WHERE book_id=? AND user_id=?', [bookId, userId]) as CharacterResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT character_id, first_name, last_name, title, category, image, role, biography, history FROM book_characters WHERE book_id=? AND user_id=?';
const params: SQLiteValue[] = [bookId, userId];
const characters: CharacterResult[] = db.all(query, params) as CharacterResult[];
return characters;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages.` : `Unable to retrieve characters.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
return result;
}
/**
* Adds a new character to the database.
* @param userId - The unique identifier of the user
* @param characterId - The unique identifier for the new character
* @param encryptedName - The encrypted first name of the character
* @param encryptedLastName - The encrypted last name of the character
* @param encryptedTitle - The encrypted title of the character
* @param encryptedCategory - The encrypted category of the character
* @param encryptedImage - The encrypted image path of the character
* @param encryptedRole - The encrypted role of the character
* @param encryptedBiography - The encrypted biography of the character
* @param encryptedHistory - The encrypted history of the character
* @param bookId - The unique identifier of the book
* @param lang - The language for error messages ('fr' or 'en')
* @returns The character ID if successful
*/
public static addNewCharacter(userId: string, characterId: string, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, bookId: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO `book_characters` (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'INSERT INTO `book_characters` (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)';
const params: SQLiteValue[] = [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds()];
const insertResult: RunResult = db.run(query, params);
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du personnage.` : `Error adding character.`);
}
return characterId;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter le personnage.` : `Unable to add character.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du personnage.` : `Error adding character.`);
}
return characterId;
}
/**
* Inserts a new attribute for a character.
* @param attributeId - The unique identifier for the new attribute
* @param characterId - The unique identifier of the character
* @param userId - The unique identifier of the user
* @param type - The attribute name/type
* @param name - The attribute value
* @param lang - The language for error messages ('fr' or 'en')
* @returns The attribute ID if successful
*/
static insertAttribute(attributeId: string, characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?,?,?,?,?,?)', [attributeId, characterId, userId, type, name, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?,?,?,?,?,?)';
const params: SQLiteValue[] = [attributeId, characterId, userId, type, name, System.timeStampInSeconds()];
const insertResult: RunResult = db.run(query, params);
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'attribut.` : `Error adding attribute.`);
}
return attributeId;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter l'attribut.` : `Unable to add attribute.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'attribut.` : `Error adding attribute.`);
}
return attributeId;
}
/**
* Updates an existing character's information.
* @param userId - The unique identifier of the user
* @param id - The unique identifier of the character to update
* @param encryptedName - The encrypted first name of the character
* @param encryptedLastName - The encrypted last name of the character
* @param encryptedTitle - The encrypted title of the character
* @param encryptedCategory - The encrypted category of the character
* @param encryptedImage - The encrypted image path of the character
* @param encryptedRole - The encrypted role of the character
* @param encryptedBiography - The encrypted biography of the character
* @param encryptedHistory - The encrypted history of the character
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the update was successful, false otherwise
*/
static updateCharacter(userId: string, id: string, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=?,`last_update`=? WHERE `character_id`=? AND `user_id`=?', [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, lastUpdate, id, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=?,`last_update`=? WHERE `character_id`=? AND `user_id`=?';
const params: SQLiteValue[] = [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, lastUpdate, id, userId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour le personnage.` : `Unable to update character.`);
} else {
console.error("An unknown error occurred.");
@@ -144,14 +198,23 @@ export default class CharacterRepo {
}
}
/**
* Deletes a character attribute from the database.
* @param userId - The unique identifier of the user
* @param attributeId - The unique identifier of the attribute to delete
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the deletion was successful, false otherwise
*/
static deleteAttribute(userId: string, attributeId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?', [attributeId, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'DELETE FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?';
const params: SQLiteValue[] = [attributeId, userId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer l'attribut.` : `Unable to delete attribute.`);
} else {
console.error("An unknown error occurred.");
@@ -160,57 +223,85 @@ export default class CharacterRepo {
}
}
/**
* Fetches all attributes for a specific character.
* @param characterId - The unique identifier of the character
* @param userId - The unique identifier of the user
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of attribute results
*/
static fetchAttributes(characterId: string, userId: string, lang: 'fr' | 'en' = 'fr'): AttributeResult[] {
let result: AttributeResult[];
try {
const db: Database = System.getDb();
result = db.all('SELECT attr_id, attribute_name, attribute_value FROM book_characters_attributes WHERE character_id=? AND user_id=?', [characterId, userId]) as AttributeResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT attr_id, attribute_name, attribute_value FROM book_characters_attributes WHERE character_id=? AND user_id=?';
const params: SQLiteValue[] = [characterId, userId];
const attributes: AttributeResult[] = db.all(query, params) as AttributeResult[];
return attributes;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs.` : `Unable to retrieve attributes.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
return result;
}
/**
* Fetches complete character information including attributes, optionally filtered by character IDs.
* @param userId - The unique identifier of the user
* @param bookId - The unique identifier of the book
* @param tags - An optional array of character IDs to filter by
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of complete character results with attributes
*/
static fetchCompleteCharacters(userId: string, bookId: string, tags: string[], lang: 'fr' | 'en' = 'fr'): CompleteCharacterResult[] {
let result: CompleteCharacterResult[];
try {
const db: Database = System.getDb();
let query: string = 'SELECT charac.character_id, first_name, last_name, category, title, role, biography, history, attribute_name, attribute_value FROM book_characters AS charac LEFT JOIN book_characters_attributes AS attr ON charac.character_id=attr.character_id WHERE charac.user_id=? AND charac.book_id=?';
let values: any[] = [userId, bookId];
let params: SQLiteValue[] = [userId, bookId];
if (tags && tags.length > 0) {
const placeholders: string = tags.map((): string => '?').join(',');
query += ` AND charac.character_id IN (${placeholders})`;
values.push(...tags);
params.push(...tags);
}
result = db.all(query, values) as CompleteCharacterResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const characters: CompleteCharacterResult[] = db.all(query, params) as CompleteCharacterResult[];
if (characters.length === 0) {
throw new Error(lang === 'fr' ? `Aucun personnage complet trouvé.` : `No complete characters found.`);
}
return characters;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages complets.` : `Unable to retrieve complete characters.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.length === 0) {
throw new Error(lang === 'fr' ? `Aucun personnage complet trouvé.` : `No complete characters found.`);
}
return result;
}
static updateCharacterAttribute(userId: string, characterAttributeId: string, attributeName: string, attributeValue: string, lastUpdate: number,lang: "fr" | "en"):boolean {
/**
* Updates an existing character attribute.
* @param userId - The unique identifier of the user
* @param characterAttributeId - The unique identifier of the attribute to update
* @param attributeName - The new attribute name
* @param attributeValue - The new attribute value
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the update was successful, false otherwise
*/
static updateCharacterAttribute(userId: string, characterAttributeId: string, attributeName: string, attributeValue: string, lastUpdate: number, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result:RunResult = db.run('UPDATE `book_characters_attributes` SET `attribute_name`=?,`attribute_value`=?, last_update=FROM_UNIXTIME(?) WHERE `attr_id`=UUID_TO_BIN(?) AND `user_id`=UUID_TO_BIN(?)', [attributeName, attributeValue, lastUpdate, characterAttributeId, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'UPDATE `book_characters_attributes` SET `attribute_name`=?,`attribute_value`=?, last_update=FROM_UNIXTIME(?) WHERE `attr_id`=UUID_TO_BIN(?) AND `user_id`=UUID_TO_BIN(?)';
const params: SQLiteValue[] = [attributeName, attributeValue, lastUpdate, characterAttributeId, userId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'attribut du personnage.` : `Unable to update character attribute.`);
} else {
console.error("An unknown error occurred.");
@@ -218,14 +309,24 @@ export default class CharacterRepo {
}
}
}
static isCharacterExist(userId: string, characterId: string,lang: "fr" | "en"): boolean {
/**
* Checks if a character exists in the database.
* @param userId - The unique identifier of the user
* @param characterId - The unique identifier of the character to check
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the character exists, false otherwise
*/
static isCharacterExist(userId: string, characterId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `book_characters` WHERE `character_id`=? AND `user_id`=?', [characterId, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM `book_characters` WHERE `character_id`=? AND `user_id`=?';
const params: SQLiteValue[] = [characterId, userId];
const character: QueryResult | null = db.get(query, params) || null;
return character !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du personnage.` : `Unable to check character existence.`);
} else {
console.error("An unknown error occurred.");
@@ -234,14 +335,23 @@ export default class CharacterRepo {
}
}
static isCharacterAttributeExist(userId: string, characterAttributeId: string,lang: "fr" | "en"): boolean {
/**
* Checks if a character attribute exists in the database.
* @param userId - The unique identifier of the user
* @param characterAttributeId - The unique identifier of the attribute to check
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the attribute exists, false otherwise
*/
static isCharacterAttributeExist(userId: string, characterAttributeId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?', [characterAttributeId, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM `book_characters_attributes` WHERE `attr_id`=? AND `user_id`=?';
const params: SQLiteValue[] = [characterAttributeId, userId];
const attribute: QueryResult | null = db.get(query, params) || null;
return attribute !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'attribut du personnage.` : `Unable to check character attribute existence.`);
} else {
console.error("An unknown error occurred.");
@@ -249,13 +359,24 @@ 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[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update FROM book_characters WHERE user_id=? AND book_id=?', [userId, bookId]) as BookCharactersTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update FROM book_characters WHERE user_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const characters: BookCharactersTable[] = db.all(query, params) as BookCharactersTable[];
return characters;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages.` : `Unable to retrieve characters.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -263,13 +384,23 @@ export default class CharacterRepo {
}
}
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 {
const db: Database = System.getDb();
return db.all('SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update FROM book_characters_attributes WHERE user_id=? AND character_id=?', [userId, characterId]) as BookCharactersAttributesTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update FROM book_characters_attributes WHERE user_id=? AND character_id=?';
const params: SQLiteValue[] = [userId, characterId];
const attributes: BookCharactersAttributesTable[] = db.all(query, params) as BookCharactersAttributesTable[];
return attributes;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs des personnages.` : `Unable to retrieve character attributes.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -277,13 +408,22 @@ export default class CharacterRepo {
}
}
/**
* Fetches all synced characters for a user.
* @param userId - The unique identifier of the user
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced character results
*/
static fetchSyncedCharacters(userId: string, lang: 'fr' | 'en'): SyncedCharacterResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT character_id, book_id, first_name, last_update FROM book_characters WHERE user_id = ?', [userId]) as SyncedCharacterResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT character_id, book_id, first_name, last_update FROM book_characters WHERE user_id = ?';
const params: SQLiteValue[] = [userId];
const syncedCharacters: SyncedCharacterResult[] = db.all(query, params) as SyncedCharacterResult[];
return syncedCharacters;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les personnages synchronisés.` : `Unable to retrieve synced characters.`);
} else {
console.error("An unknown error occurred.");
@@ -292,13 +432,22 @@ export default class CharacterRepo {
}
}
/**
* Fetches all synced character attributes for a user.
* @param userId - The unique identifier of the user
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced character attribute results
*/
static fetchSyncedCharacterAttributes(userId: string, lang: 'fr' | 'en'): SyncedCharacterAttributeResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT attr_id, character_id, attribute_name, last_update FROM book_characters_attributes WHERE user_id = ?', [userId]) as SyncedCharacterAttributeResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT attr_id, character_id, attribute_name, last_update FROM book_characters_attributes WHERE user_id = ?';
const params: SQLiteValue[] = [userId];
const syncedAttributes: SyncedCharacterAttributeResult[] = db.all(query, params) as SyncedCharacterAttributeResult[];
return syncedAttributes;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les attributs des personnages synchronisés.` : `Unable to retrieve synced character attributes.`);
} else {
console.error("An unknown error occurred.");
@@ -307,18 +456,34 @@ export default class CharacterRepo {
}
}
/**
* Inserts a synced character into the database.
* @param characterId - The unique identifier of the character
* @param bookId - The unique identifier of the book
* @param userId - The unique identifier of the user
* @param firstName - The first name of the character
* @param lastName - The last name of the character (nullable)
* @param category - The category of the character
* @param title - The title of the character (nullable)
* @param image - The image path of the character (nullable)
* @param role - The role of the character (nullable)
* @param biography - The biography of the character (nullable)
* @param history - The history of the character (nullable)
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the insertion was successful, false otherwise
*/
static insertSyncCharacter(characterId: string, bookId: string, userId: string, firstName: string, lastName: string | null, category: string, title: string | null, image: string | null, role: string | null, biography: string | null, history: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_characters (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[characterId, bookId, userId, firstName, lastName, category, title, image, role, biography, history, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `INSERT INTO book_characters (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
const params: SQLiteValue[] = [characterId, bookId, userId, firstName, lastName, category, title, image, role, biography, history, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le personnage.` : `Unable to insert character.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -326,35 +491,52 @@ export default class CharacterRepo {
}
}
/**
* Inserts a synced character attribute into the database.
* @param attrId - The unique identifier of the attribute
* @param characterId - The unique identifier of the character
* @param userId - The unique identifier of the user
* @param attributeName - The name of the attribute
* @param attributeValue - The value of the attribute
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the insertion was successful, false otherwise
*/
static insertSyncCharacterAttribute(attrId: string, characterId: string, userId: string, attributeName: string, attributeValue: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update)
VALUES (?, ?, ?, ?, ?, ?)`,
[attrId, characterId, userId, attributeName, attributeValue, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `INSERT INTO book_characters_attributes (attr_id, character_id, user_id, attribute_name, attribute_value, last_update)
VALUES (?, ?, ?, ?, ?, ?)`;
const params: SQLiteValue[] = [attrId, characterId, userId, attributeName, attributeValue, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer l'attribut du personnage.` : `Unable to insert character attribute.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static async fetchCompleteCharacterById(id: string, lang: "fr" | "en"):Promise<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 {
const db: Database = System.getDb();
return db.all(
`SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update
const query: string = `SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update
FROM book_characters
WHERE character_id = ?`,
[id]
) as BookCharactersTable[];
} catch (e:unknown){
if (e instanceof Error) {
WHERE character_id = ?`;
const params: SQLiteValue[] = [id];
const character: BookCharactersTable[] = db.all(query, params) as BookCharactersTable[];
return character;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le personnage complet.` : `Unable to retrieve complete character.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -362,17 +544,23 @@ export default class CharacterRepo {
}
}
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 {
const db: Database = System.getDb();
return db.all(
`SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update
const query: string = `SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update
FROM book_characters_attributes
WHERE attr_id = ?`,
[id]
) as BookCharactersAttributesTable[];
} catch (e:unknown){
if (e instanceof Error) {
WHERE attr_id = ?`;
const params: SQLiteValue[] = [id];
const attribute: BookCharactersAttributesTable[] = db.all(query, params) as BookCharactersAttributesTable[];
return attribute;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer l'attribut de personnage complet.` : `Unable to retrieve complete character attribute.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");

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 {ChapterBookResult, EritBooksTable} from "@/electron/database/repositories/book.repository";
export interface BookAIGuideLineTable extends Record<string, SQLiteValue> {
user_id: string;
@@ -72,35 +71,111 @@ export interface GuideLineAIQuery extends Record<string, SQLiteValue> {
}
export default class GuidelineRepo {
/**
* Fetches the guideline for a specific book.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of guideline query results
* @throws Error if the guideline cannot be retrieved
*/
public static fetchGuideLine(userId: string, bookId: string, lang: 'fr' | 'en'): GuideLineQuery[] {
let result: GuideLineQuery[];
let guidelines: GuideLineQuery[];
try {
const db: Database = System.getDb();
result = db.all('SELECT * FROM book_guide_line WHERE book_id=? AND user_id=?', [bookId, userId]) as GuideLineQuery[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT * FROM book_guide_line WHERE book_id=? AND user_id=?';
const params: SQLiteValue[] = [bookId, userId];
guidelines = db.all(query, params) as GuideLineQuery[];
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice.` : `Unable to retrieve guideline.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
return result;
return guidelines;
}
static updateGuideLine(userId: string, bookId: string, encryptedTone: string, encryptedAtmosphere: string, encryptedWritingStyle: string, encryptedThemes: string, encryptedSymbolism: string, encryptedMotifs: string, encryptedNarrativeVoice: string, encryptedPacing: string, encryptedKeyMessages: string, encryptedIntendedAudience: string, lang: 'fr' | 'en'): boolean {
/**
* Updates or inserts a guideline for a specific book.
* If the guideline exists, it updates it; otherwise, it inserts a new one.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param encryptedTone - The encrypted tone value
* @param encryptedAtmosphere - The encrypted atmosphere value
* @param encryptedWritingStyle - The encrypted writing style value
* @param encryptedThemes - The encrypted themes value
* @param encryptedSymbolism - The encrypted symbolism value
* @param encryptedMotifs - The encrypted motifs value
* @param encryptedNarrativeVoice - The encrypted narrative voice value
* @param encryptedPacing - The encrypted pacing value
* @param encryptedKeyMessages - The encrypted key messages value
* @param encryptedIntendedAudience - The encrypted intended audience value
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the operation was successful
* @throws Error if the guideline cannot be updated or inserted
*/
static updateGuideLine(
userId: string,
bookId: string,
encryptedTone: string,
encryptedAtmosphere: string,
encryptedWritingStyle: string,
encryptedThemes: string,
encryptedSymbolism: string,
encryptedMotifs: string,
encryptedNarrativeVoice: string,
encryptedPacing: string,
encryptedKeyMessages: string,
encryptedIntendedAudience: string,
lang: 'fr' | 'en'
): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_guide_line SET tone=?, atmosphere=?, writing_style=?, themes=?, symbolism=?, motifs=?, narrative_voice=?, pacing=?, key_messages=?, last_update=? WHERE user_id=? AND book_id=?', [encryptedTone, encryptedAtmosphere, encryptedWritingStyle, encryptedThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedKeyMessages, System.timeStampInSeconds(), userId, bookId]);
if (result.changes > 0) {
const updateQuery: string = 'UPDATE book_guide_line SET tone=?, atmosphere=?, writing_style=?, themes=?, symbolism=?, motifs=?, narrative_voice=?, pacing=?, key_messages=?, last_update=? WHERE user_id=? AND book_id=?';
const updateParams: SQLiteValue[] = [
encryptedTone,
encryptedAtmosphere,
encryptedWritingStyle,
encryptedThemes,
encryptedSymbolism,
encryptedMotifs,
encryptedNarrativeVoice,
encryptedPacing,
encryptedKeyMessages,
System.timeStampInSeconds(),
userId,
bookId
];
const updateResult: RunResult = db.run(updateQuery, updateParams);
if (updateResult.changes > 0) {
return true;
} else {
const insert:RunResult = db.run('INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)', [userId, bookId, encryptedTone, encryptedAtmosphere, encryptedWritingStyle, encryptedThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedIntendedAudience, encryptedKeyMessages, System.timeStampInSeconds()]);
return insert.changes > 0;
const insertQuery: string = 'INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)';
const insertParams: SQLiteValue[] = [
userId,
bookId,
encryptedTone,
encryptedAtmosphere,
encryptedWritingStyle,
encryptedThemes,
encryptedSymbolism,
encryptedMotifs,
encryptedNarrativeVoice,
encryptedPacing,
encryptedIntendedAudience,
encryptedKeyMessages,
System.timeStampInSeconds()
];
const insertResult: RunResult = db.run(insertQuery, insertParams);
return insertResult.changes > 0;
}
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour la ligne directrice.` : `Unable to update guideline.`);
} else {
console.error("An unknown error occurred.");
@@ -108,19 +183,76 @@ export default class GuidelineRepo {
}
}
}
static insertAIGuideLine(userId: string, bookId: string, narrativeType: number, dialogueType: number, encryptedPlotSummary: string, encryptedToneAtmosphere: string, verbTense: number, language: number, encryptedThemes: string, lang: 'fr' | 'en'): boolean {
/**
* Inserts or updates an AI guideline for a specific book.
* If the AI guideline exists, it updates it; otherwise, it inserts a new one.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param narrativeType - The narrative type identifier
* @param dialogueType - The dialogue type identifier
* @param encryptedPlotSummary - The encrypted plot summary
* @param encryptedToneAtmosphere - The encrypted tone and atmosphere value
* @param verbTense - The verb tense identifier
* @param language - The language identifier
* @param encryptedThemes - The encrypted themes value
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the operation was successful
* @throws Error if the AI guideline cannot be inserted or updated
*/
static insertAIGuideLine(
userId: string,
bookId: string,
narrativeType: number,
dialogueType: number,
encryptedPlotSummary: string,
encryptedToneAtmosphere: string,
verbTense: number,
language: number,
encryptedThemes: string,
lang: 'fr' | 'en'
): boolean {
try {
const db: Database = System.getDb();
let result: RunResult = db.run('UPDATE book_ai_guide_line SET narrative_type=?, dialogue_type=?, global_resume=?, atmosphere=?, verbe_tense=?, langue=?, themes=?, last_update=? WHERE user_id=? AND book_id=?', [narrativeType ? narrativeType : null, dialogueType ? dialogueType : null, encryptedPlotSummary, encryptedToneAtmosphere, verbTense ? verbTense : null, language ? language : null, encryptedThemes, System.timeStampInSeconds(), userId, bookId]);
if (result.changes > 0) {
const updateQuery: string = 'UPDATE book_ai_guide_line SET narrative_type=?, dialogue_type=?, global_resume=?, atmosphere=?, verbe_tense=?, langue=?, themes=?, last_update=? WHERE user_id=? AND book_id=?';
const updateParams: SQLiteValue[] = [
narrativeType ? narrativeType : null,
dialogueType ? dialogueType : null,
encryptedPlotSummary,
encryptedToneAtmosphere,
verbTense ? verbTense : null,
language ? language : null,
encryptedThemes,
System.timeStampInSeconds(),
userId,
bookId
];
const updateResult: RunResult = db.run(updateQuery, updateParams);
if (updateResult.changes > 0) {
return true;
} else {
result = db.run('INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [userId, bookId, encryptedPlotSummary, encryptedThemes, verbTense ? verbTense : null, narrativeType ? narrativeType : null, language ? language : null, dialogueType ? dialogueType : null, encryptedToneAtmosphere, encryptedToneAtmosphere, encryptedPlotSummary, System.timeStampInSeconds()]);
return result.changes > 0;
const insertQuery: string = 'INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)';
const insertParams: SQLiteValue[] = [
userId,
bookId,
encryptedPlotSummary,
encryptedThemes,
verbTense ? verbTense : null,
narrativeType ? narrativeType : null,
language ? language : null,
dialogueType ? dialogueType : null,
encryptedToneAtmosphere,
encryptedToneAtmosphere,
encryptedPlotSummary,
System.timeStampInSeconds()
];
const insertResult: RunResult = db.run(insertQuery, insertParams);
return insertResult.changes > 0;
}
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice IA.` : `Unable to insert AI guideline.`);
} else {
console.error("An unknown error occurred.");
@@ -129,58 +261,103 @@ export default class GuidelineRepo {
}
}
/**
* Fetches the AI guideline for a specific book.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns The AI guideline query result
* @throws Error if the AI guideline cannot be retrieved or is not found
*/
static fetchGuideLineAI(userId: string, bookId: string, lang: 'fr' | 'en'): GuideLineAIQuery {
let result: GuideLineAIQuery | null;
let aiGuideline: GuideLineAIQuery | null;
try {
const db: Database = System.getDb();
result = db.get('SELECT narrative_type, dialogue_type, global_resume, atmosphere, verbe_tense, langue, themes, current_resume FROM book_ai_guide_line WHERE user_id=? AND book_id=?', [userId, bookId]) as GuideLineAIQuery | null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT narrative_type, dialogue_type, global_resume, atmosphere, verbe_tense, langue, themes, current_resume FROM book_ai_guide_line WHERE user_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
aiGuideline = db.get(query, params) as GuideLineAIQuery | null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice IA.` : `Unable to retrieve AI guideline.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result) {
if (!aiGuideline) {
throw new Error(lang === 'fr' ? `Ligne directrice IA non trouvée.` : `AI guideline not found.`);
}
return result;
return aiGuideline;
}
/**
* Fetches the book AI guideline table data for a specific book.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns A promise resolving to an array of book AI guideline table entries
* @throws Error if the AI guideline cannot be retrieved
*/
static async fetchBookAIGuideLine(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookAIGuideLineTable[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update FROM book_ai_guide_line WHERE user_id=? AND book_id=?', [userId, bookId]) as BookAIGuideLineTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update FROM book_ai_guide_line WHERE user_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const aiGuidelines: BookAIGuideLineTable[] = db.all(query, params) as BookAIGuideLineTable[];
return aiGuidelines;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice IA.` : `Unable to retrieve AI guideline.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Fetches the book guideline table data for a specific book.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns A promise resolving to an array of book guideline table entries
* @throws Error if the guideline cannot be retrieved
*/
static async fetchBookGuideLineTable(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookGuideLineTable[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update FROM book_guide_line WHERE user_id=? AND book_id=?', [userId, bookId]) as BookGuideLineTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update FROM book_guide_line WHERE user_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const guidelines: BookGuideLineTable[] = db.all(query, params) as BookGuideLineTable[];
return guidelines;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer la ligne directrice.` : `Unable to retrieve guideline.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Fetches all synced guidelines for a specific user.
* @param userId - The user identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced guideline results containing book_id and last_update
* @throws Error if the synced guidelines cannot be retrieved
*/
static fetchSyncedGuideLine(userId: string, lang: 'fr' | 'en'): SyncedGuideLineResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT book_id, last_update FROM book_guide_line WHERE user_id = ?', [userId]) as SyncedGuideLineResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT book_id, last_update FROM book_guide_line WHERE user_id = ?';
const params: SQLiteValue[] = [userId];
const syncedGuidelines: SyncedGuideLineResult[] = db.all(query, params) as SyncedGuideLineResult[];
return syncedGuidelines;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les lignes directrices synchronisées.` : `Unable to retrieve synced guidelines.`);
} else {
console.error("An unknown error occurred.");
@@ -189,13 +366,23 @@ export default class GuidelineRepo {
}
}
/**
* Fetches all synced AI guidelines for a specific user.
* @param userId - The user identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced AI guideline results containing book_id and last_update
* @throws Error if the synced AI guidelines cannot be retrieved
*/
static fetchSyncedAIGuideLine(userId: string, lang: 'fr' | 'en'): SyncedAIGuideLineResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT book_id, last_update FROM book_ai_guide_line WHERE user_id = ?', [userId]) as SyncedAIGuideLineResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT book_id, last_update FROM book_ai_guide_line WHERE user_id = ?';
const params: SQLiteValue[] = [userId];
const syncedAIGuidelines: SyncedAIGuideLineResult[] = db.all(query, params) as SyncedAIGuideLineResult[];
return syncedAIGuidelines;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les lignes directrices IA synchronisées.` : `Unable to retrieve synced AI guidelines.`);
} else {
console.error("An unknown error occurred.");
@@ -203,36 +390,129 @@ export default class GuidelineRepo {
}
}
}
static insertSyncAIGuideLine(userId: string, bookId: string, globalResume: string | null, themes: string | null, verbeTense: number | null, narrativeType: number | null, langue: number | null, dialogueType: number | null, tone: string | null, atmosphere: string | null, currentResume: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
/**
* Inserts a synced AI guideline for a specific book.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param globalResume - The global resume value (nullable)
* @param themes - The themes value (nullable)
* @param verbeTense - The verb tense identifier (nullable)
* @param narrativeType - The narrative type identifier (nullable)
* @param langue - The language identifier (nullable)
* @param dialogueType - The dialogue type identifier (nullable)
* @param tone - The tone value (nullable)
* @param atmosphere - The atmosphere value (nullable)
* @param currentResume - The current resume value (nullable)
* @param lastUpdate - The last update timestamp
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the insertion was successful
* @throws Error if the AI guideline cannot be inserted
*/
static insertSyncAIGuideLine(
userId: string,
bookId: string,
globalResume: string | null,
themes: string | null,
verbeTense: number | null,
narrativeType: number | null,
langue: number | null,
dialogueType: number | null,
tone: string | null,
atmosphere: string | null,
currentResume: string | null,
lastUpdate: number,
lang: 'fr' | 'en'
): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[userId, bookId, globalResume, themes, verbeTense, narrativeType, langue, dialogueType, tone, atmosphere, currentResume, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
const params: SQLiteValue[] = [
userId,
bookId,
globalResume,
themes,
verbeTense,
narrativeType,
langue,
dialogueType,
tone,
atmosphere,
currentResume,
lastUpdate
];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice IA.` : `Unable to insert AI guideline.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static insertSyncGuideLine(userId: string, bookId: string, tone: string | null, atmosphere: string | null, writingStyle: string | null, themes: string | null, symbolism: string | null, motifs: string | null, narrativeVoice: string | null, pacing: string | null, intendedAudience: string | null, keyMessages: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
/**
* Inserts a synced guideline for a specific book.
* @param userId - The user identifier
* @param bookId - The book identifier
* @param tone - The tone value (nullable)
* @param atmosphere - The atmosphere value (nullable)
* @param writingStyle - The writing style value (nullable)
* @param themes - The themes value (nullable)
* @param symbolism - The symbolism value (nullable)
* @param motifs - The motifs value (nullable)
* @param narrativeVoice - The narrative voice value (nullable)
* @param pacing - The pacing value (nullable)
* @param intendedAudience - The intended audience value (nullable)
* @param keyMessages - The key messages value (nullable)
* @param lastUpdate - The last update timestamp
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the insertion was successful
* @throws Error if the guideline cannot be inserted
*/
static insertSyncGuideLine(
userId: string,
bookId: string,
tone: string | null,
atmosphere: string | null,
writingStyle: string | null,
themes: string | null,
symbolism: string | null,
motifs: string | null,
narrativeVoice: string | null,
pacing: string | null,
intendedAudience: string | null,
keyMessages: string | null,
lastUpdate: number,
lang: 'fr' | 'en'
): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[userId, bookId, tone, atmosphere, writingStyle, themes, symbolism, motifs, narrativeVoice, pacing, intendedAudience, keyMessages, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `INSERT INTO book_guide_line (user_id, book_id, tone, atmosphere, writing_style, themes, symbolism, motifs, narrative_voice, pacing, intended_audience, key_messages, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
const params: SQLiteValue[] = [
userId,
bookId,
tone,
atmosphere,
writingStyle,
themes,
symbolism,
motifs,
narrativeVoice,
pacing,
intendedAudience,
keyMessages,
lastUpdate
];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer la ligne directrice.` : `Unable to insert guideline.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");

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";
export interface BookIncidentsTable extends Record<string, SQLiteValue> {
@@ -25,13 +25,24 @@ export interface IncidentQuery extends Record<string, SQLiteValue> {
}
export default class IncidentRepository {
public static fetchAllIncitentIncidents(userId:string,bookId:string, lang: 'fr' | 'en'):IncidentQuery[]{
/**
* Fetches all incidents for a specific book belonging to a user.
* @param userId - The ID of the user (author)
* @param bookId - The ID of the book
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of incidents with their ID, title, and summary
* @throws Error if the database query fails
*/
public static fetchAllIncidents(userId: string, bookId: string, lang: 'fr' | 'en'): IncidentQuery[] {
try {
const db: Database = System.getDb();
return db.all('SELECT incident_id, title, summary FROM book_incidents WHERE author_id=? AND book_id=?', [userId, bookId]) as IncidentQuery[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT incident_id, title, summary FROM book_incidents WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const incidents: IncidentQuery[] = db.all(query, params) as IncidentQuery[];
return incidents;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les incidents.` : `Unable to retrieve incidents.`);
} else {
console.error("An unknown error occurred.");
@@ -40,34 +51,58 @@ export default class IncidentRepository {
}
}
/**
* Inserts a new incident into the database.
* @param incidentId - The unique ID for the new incident
* @param userId - The ID of the user (author)
* @param bookId - The ID of the book
* @param encryptedName - The encrypted title of the incident
* @param hashedName - The hashed title of the incident
* @param lang - The language for error messages ('fr' or 'en')
* @returns The incident ID if insertion was successful
* @throws Error if the database insertion fails
*/
public static insertNewIncident(incidentId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string {
let result: RunResult;
let insertResult: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO book_incidents (incident_id,author_id, book_id, title, hashed_title, last_update) VALUES (?,?,?,?,?,?)', [incidentId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'INSERT INTO book_incidents (incident_id,author_id, book_id, title, hashed_title, last_update) VALUES (?,?,?,?,?,?)';
const params: SQLiteValue[] = [incidentId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()];
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter l'élément déclencheur.` : `Unable to add incident.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'élément déclencheur.` : `Error adding incident.`);
}
return incidentId;
}
/**
* Deletes an incident from the database.
* @param userId - The ID of the user (author)
* @param bookId - The ID of the book
* @param incidentId - The ID of the incident to delete
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the incident was deleted, false otherwise
* @throws Error if the database deletion fails
*/
public static deleteIncident(userId: string, bookId: string, incidentId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_incidents WHERE author_id=? AND book_id=? AND incident_id=?', [userId, bookId, incidentId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'DELETE FROM book_incidents WHERE author_id=? AND book_id=? AND incident_id=?';
const params: SQLiteValue[] = [userId, bookId, incidentId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer l'élément déclencheur.` : `Unable to delete incident.`);
} else {
console.error("An unknown error occurred.");
@@ -75,14 +110,30 @@ export default class IncidentRepository {
}
}
}
/**
* Updates an existing incident in the database.
* @param userId - The ID of the user (author)
* @param bookId - The ID of the book
* @param incidentId - The ID of the incident to update
* @param encryptedIncidentName - The new encrypted title
* @param incidentHashedName - The new hashed title
* @param incidentSummary - The new summary
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the incident was updated, false otherwise
* @throws Error if the database update fails
*/
public static updateIncident(userId: string, bookId: string, incidentId: string, encryptedIncidentName: string, incidentHashedName: string, incidentSummary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_incidents SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND incident_id=?', [encryptedIncidentName, incidentHashedName, incidentSummary, lastUpdate, userId, bookId, incidentId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'UPDATE book_incidents SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND incident_id=?';
const params: SQLiteValue[] = [encryptedIncidentName, incidentHashedName, incidentSummary, lastUpdate, userId, bookId, incidentId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'incident.` : `Unable to update incident.`);
} else {
console.error("An unknown error occurred.");
@@ -90,26 +141,49 @@ export default class IncidentRepository {
}
}
}
/**
* Fetches all incidents for a book with complete information.
* @param userId - The ID of the user (author)
* @param bookId - The ID of the book
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of complete incident records
* @throws Error if the database query fails
*/
static async fetchBookIncidents(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookIncidentsTable[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update FROM book_incidents WHERE author_id=? AND book_id=?', [userId, bookId]) as BookIncidentsTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update FROM book_incidents WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const incidents: BookIncidentsTable[] = db.all(query, params) as BookIncidentsTable[];
return incidents;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les incidents.` : `Unable to retrieve incidents.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Fetches all synced incidents for a user across all books.
* @param userId - The ID of the user (author)
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced incident records with minimal information
* @throws Error if the database query fails
*/
static fetchSyncedIncidents(userId: string, lang: 'fr' | 'en'): SyncedIncidentResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT incident_id, book_id, title, last_update FROM book_incidents WHERE author_id = ?', [userId]) as SyncedIncidentResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT incident_id, book_id, title, last_update FROM book_incidents WHERE author_id = ?';
const params: SQLiteValue[] = [userId];
const syncedIncidents: SyncedIncidentResult[] = db.all(query, params) as SyncedIncidentResult[];
return syncedIncidents;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les incidents synchronisés.` : `Unable to retrieve synced incidents.`);
} else {
console.error("An unknown error occurred.");
@@ -117,49 +191,82 @@ export default class IncidentRepository {
}
}
}
/**
* Inserts a synced incident into the database.
* @param incidentId - The unique ID for the incident
* @param authorId - The ID of the author
* @param bookId - The ID of the book
* @param title - The encrypted title
* @param hashedTitle - The hashed title
* @param summary - The encrypted summary (can be null)
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the incident was inserted, false otherwise
* @throws Error if the database insertion fails
*/
static insertSyncIncident(incidentId: string, authorId: string, bookId: string, title: string, hashedTitle: string, summary: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_incidents (incident_id, author_id, book_id, title, hashed_title, summary, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[incidentId, authorId, bookId, title, hashedTitle, summary, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `INSERT INTO book_incidents (incident_id, author_id, book_id, title, hashed_title, summary, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)`;
const params: SQLiteValue[] = [incidentId, authorId, bookId, title, hashedTitle, summary, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer l'incident.` : `Unable to insert incident.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static async fetchCompleteIncidentById(id: string, lang: "fr" | "en"):Promise<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 {
const db: Database = System.getDb();
return db.all(
`SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update
const query: string = `SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update
FROM book_incidents
WHERE incident_id = ?`,
[id]
) as BookIncidentsTable[];
} catch (e:unknown){
if (e instanceof Error) {
WHERE incident_id = ?`;
const params: SQLiteValue[] = [id];
const incident: BookIncidentsTable[] = db.all(query, params) as BookIncidentsTable[];
return incident;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer l'incident complet.` : `Unable to retrieve complete incident.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static incidentExist(userId: string, bookId: string, incident_id: string,lang: "fr" | "en"): boolean {
/**
* Checks if an incident exists in the database.
* @param userId - The ID of the user (author)
* @param bookId - The ID of the book
* @param incidentId - The ID of the incident to check
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the incident exists, false otherwise
* @throws Error if the database query fails
*/
static incidentExist(userId: string, bookId: string, incidentId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM book_incidents WHERE book_id=? AND incident_id=? AND author_id=?', [bookId, incident_id, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM book_incidents WHERE book_id=? AND incident_id=? AND author_id=?';
const params: SQLiteValue[] = [bookId, incidentId, userId];
const existingIncident: QueryResult | null = db.get(query, params) || null;
return existingIncident !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'incident.` : `Unable to check incident existence.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");

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";
export interface BookIssuesTable extends Record<string, SQLiteValue> {
@@ -23,13 +23,23 @@ export interface IssueQuery extends Record<string, SQLiteValue> {
}
export default class IssueRepository {
public static fetchIssuesFromBook(userId:string,bookId:string, lang: 'fr' | 'en'):IssueQuery[]{
/**
* Fetches all issues associated with a specific book.
* @param userId - The unique identifier of the user/author.
* @param bookId - The unique identifier of the book.
* @param lang - The language for error messages ('fr' or 'en').
* @returns An array of issues with their IDs and names.
*/
public static fetchIssuesFromBook(userId: string, bookId: string, lang: 'fr' | 'en'): IssueQuery[] {
try {
const db: Database = System.getDb();
return db.all('SELECT issue_id, name FROM book_issues WHERE author_id=? AND book_id=?', [userId, bookId]) as IssueQuery[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT issue_id, name FROM book_issues WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const issues: IssueQuery[] = db.all(query, params) as IssueQuery[];
return issues;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les problématiques.` : `Unable to retrieve issues.`);
} else {
console.error("An unknown error occurred.");
@@ -37,30 +47,45 @@ export default class IssueRepository {
}
}
}
/**
* Inserts a new issue into the database after verifying it doesn't already exist.
* @param issueId - The unique identifier for the new issue.
* @param userId - The unique identifier of the user/author.
* @param bookId - The unique identifier of the book.
* @param encryptedName - The encrypted name of the issue.
* @param hashedName - The hashed name of the issue for duplicate checking.
* @param lang - The language for error messages ('fr' or 'en').
* @returns The issue ID if successfully inserted.
*/
public static insertNewIssue(issueId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string {
let existingResult: QueryResult | null;
let existingIssue: QueryResult | null;
let insertResult: RunResult;
try {
const db: Database = System.getDb();
existingResult = db.get('SELECT issue_id FROM book_issues WHERE hashed_issue_name=? AND book_id=? AND author_id=?', [hashedName, bookId, userId]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const checkQuery: string = 'SELECT issue_id FROM book_issues WHERE hashed_issue_name=? AND book_id=? AND author_id=?';
const checkParams: SQLiteValue[] = [hashedName, bookId, userId];
existingIssue = db.get(checkQuery, checkParams);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de la problématique.` : `Unable to verify issue existence.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (existingResult !== null) {
if (existingIssue !== null) {
throw new Error(lang === 'fr' ? `La problématique existe déjà.` : `This issue already exists.`);
}
try {
const db: Database = System.getDb();
insertResult = db.run('INSERT INTO book_issues (issue_id,author_id, book_id, name, hashed_issue_name, last_update) VALUES (?,?,?,?,?,?)', [issueId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const insertQuery: string = 'INSERT INTO book_issues (issue_id, author_id, book_id, name, hashed_issue_name, last_update) VALUES (?, ?, ?, ?, ?, ?)';
const insertParams: SQLiteValue[] = [issueId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()];
insertResult = db.run(insertQuery, insertParams);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter la problématique.` : `Unable to add issue.`);
} else {
console.error("An unknown error occurred.");
@@ -73,14 +98,23 @@ export default class IssueRepository {
return issueId;
}
/**
* Deletes an issue from the database.
* @param userId - The unique identifier of the user/author.
* @param issueId - The unique identifier of the issue to delete.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the issue was successfully deleted, false otherwise.
*/
public static deleteIssue(userId: string, issueId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_issues WHERE author_id=? AND issue_id=?', [userId, issueId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'DELETE FROM book_issues WHERE author_id=? AND issue_id=?';
const params: SQLiteValue[] = [userId, issueId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer la problématique.` : `Unable to delete issue.`);
} else {
console.error("An unknown error occurred.");
@@ -88,26 +122,47 @@ export default class IssueRepository {
}
}
}
/**
* Fetches all complete issue records for a specific book.
* @param userId - The unique identifier of the user/author.
* @param bookId - The unique identifier of the book.
* @param lang - The language for error messages ('fr' or 'en').
* @returns A promise resolving to an array of complete issue records.
*/
static async fetchBookIssues(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookIssuesTable[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update FROM book_issues WHERE author_id=? AND book_id=?', [userId, bookId]) as BookIssuesTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update FROM book_issues WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const issues: BookIssuesTable[] = db.all(query, params) as BookIssuesTable[];
return issues;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les problématiques.` : `Unable to retrieve issues.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Fetches all synced issues for a specific user.
* @param userId - The unique identifier of the user/author.
* @param lang - The language for error messages ('fr' or 'en').
* @returns An array of synced issue records.
*/
static fetchSyncedIssues(userId: string, lang: 'fr' | 'en'): SyncedIssueResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT issue_id, book_id, name, last_update FROM book_issues WHERE author_id = ?', [userId]) as SyncedIssueResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT issue_id, book_id, name, last_update FROM book_issues WHERE author_id = ?';
const params: SQLiteValue[] = [userId];
const syncedIssues: SyncedIssueResult[] = db.all(query, params) as SyncedIssueResult[];
return syncedIssues;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les problématiques synchronisées.` : `Unable to retrieve synced issues.`);
} else {
console.error("An unknown error occurred.");
@@ -116,51 +171,77 @@ export default class IssueRepository {
}
}
/**
* Inserts a synced issue from remote into the local database.
* @param issueId - The unique identifier of the issue.
* @param authorId - The unique identifier of the author.
* @param bookId - The unique identifier of the book.
* @param name - The encrypted name of the issue.
* @param hashedIssueName - The hashed name of the issue.
* @param lastUpdate - The timestamp of the last update.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the issue was successfully inserted, false otherwise.
*/
static insertSyncIssue(issueId: string, authorId: string, bookId: string, name: string, hashedIssueName: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_issues (issue_id, author_id, book_id, name, hashed_issue_name, last_update)
VALUES (?, ?, ?, ?, ?, ?)`,
[issueId, authorId, bookId, name, hashedIssueName, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `INSERT INTO book_issues (issue_id, author_id, book_id, name, hashed_issue_name, last_update) VALUES (?, ?, ?, ?, ?, ?)`;
const params: SQLiteValue[] = [issueId, authorId, bookId, name, hashedIssueName, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer la problématique.` : `Unable to insert issue.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static async fetchCompleteIssueById(id: string, lang: "fr" | "en"):Promise<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 {
const db: Database = System.getDb();
return db.all(
`SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update
FROM book_issues
WHERE issue_id = ?`,
[id]
) as BookIssuesTable[];
} catch (e:unknown){
if (e instanceof Error) {
const query: string = `SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update FROM book_issues WHERE issue_id = ?`;
const params: SQLiteValue[] = [id];
const issues: BookIssuesTable[] = db.all(query, params) as BookIssuesTable[];
return issues;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le problème complet.` : `Unable to retrieve complete issue.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static updateIssue(userId: string, bookId: string, issueId: string, name: string, hashedName: string, lastUpdate: number, lang: "fr" | "en"):boolean {
/**
* Updates an existing issue in the database.
* @param userId - The unique identifier of the user/author.
* @param bookId - The unique identifier of the book.
* @param issueId - The unique identifier of the issue to update.
* @param name - The new encrypted name of the issue.
* @param hashedName - The new hashed name of the issue.
* @param lastUpdate - The timestamp of the update.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the issue was successfully updated, false otherwise.
*/
static updateIssue(userId: string, bookId: string, issueId: string, name: string, hashedName: string, lastUpdate: number, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const query:string = `UPDATE book_issues SET name = ?, hashed_issue_name = ?, last_update = FROM_UNIXTIME(?) WHERE issue_id = UUID_TO_BIN(?) AND author_id = UUID_TO_BIN(?) AND book_id = UUID_TO_BIN(?)`;
const params:(string|number)[] = [name, hashedName, lastUpdate, issueId, userId, bookId];
const result:RunResult = db.run(query, params);
return result.changes > 0;
} catch (e:unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `UPDATE book_issues SET name = ?, hashed_issue_name = ?, last_update = ? WHERE issue_id = ? AND author_id = ? AND book_id = ?`;
const params: SQLiteValue[] = [name, hashedName, lastUpdate, issueId, userId, bookId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour la problématique.` : `Unable to update issue.`);
} else {
console.error("An unknown error occurred.");
@@ -168,14 +249,25 @@ export default class IssueRepository {
}
}
}
static issueExist(userId: string, bookId: string, issue_id: string,lang: "fr" | "en"): boolean {
/**
* Checks if an issue exists in the database.
* @param userId - The unique identifier of the user/author.
* @param bookId - The unique identifier of the book.
* @param issueId - The unique identifier of the issue to check.
* @param lang - The language for error messages ('fr' or 'en').
* @returns True if the issue exists, false otherwise.
*/
static issueExist(userId: string, bookId: string, issueId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `book_issues` WHERE `issue_id`=? AND `author_id`=? AND `book_id`=?', [issue_id, userId, bookId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM book_issues WHERE issue_id=? AND author_id=? AND book_id=?';
const params: SQLiteValue[] = [issueId, userId, bookId];
const existingIssue: QueryResult | null = db.get(query, params) || null;
return existingIssue !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du problème.` : `Unable to check issue existence.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");

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";
export interface LocationQueryResult extends Record<string, SQLiteValue> {
@@ -79,92 +79,166 @@ export interface SyncedLocationSubElementResult extends Record<string, SQLiteVal
}
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[] {
let result: LocationQueryResult[];
try {
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=?';
result = db.all(query, [userId, bookId]) as LocationQueryResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
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 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.`);
} else {
console.error("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 {
let result: RunResult;
try {
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()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update)
VALUES (?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [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.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de 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 {
let result: RunResult;
try {
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()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [elementId, 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.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'é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 {
let result: RunResult;
try {
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()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [subElementId, elementId, userId, 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.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du 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 {
try {
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]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
UPDATE location_sub_element
SET sub_elem_name = ?, original_name = ?, sub_elem_description = ?, last_update = ?
WHERE sub_element_id = ? AND user_id = ?
`;
const params: SQLiteValue[] = [encryptedName, originalName, encryptDescription, lastUpdate, id, userId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour le sous-élément d'emplacement.` : `Unable to update location sub-element.`);
} else {
console.error("An unknown error occurred.");
@@ -173,14 +247,31 @@ export default class LocationRepo {
}
}
/**
* Updates an existing location element's name and description.
* @param userId - The user's unique identifier
* @param id - The element's unique identifier
* @param encryptedName - The new encrypted element name
* @param originalName - The new original (unencrypted) element name
* @param encryptedDescription - The new encrypted description
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the update affected at least one row
*/
static updateLocationElement(userId: string, id: string, encryptedName: string, originalName: string, encryptedDescription: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE location_element SET element_name=?, original_name=?, element_description=?, last_update=? WHERE element_id=? AND user_id=?', [encryptedName, originalName, encryptedDescription, lastUpdate, id, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
UPDATE location_element
SET element_name = ?, original_name = ?, element_description = ?, last_update = ?
WHERE element_id = ? AND user_id = ?
`;
const params: SQLiteValue[] = [encryptedName, originalName, encryptedDescription, lastUpdate, id, userId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'élément d'emplacement.` : `Unable to update location element.`);
} else {
console.error("An unknown error occurred.");
@@ -189,14 +280,30 @@ export default class LocationRepo {
}
}
/**
* Updates an existing location section's name.
* @param userId - The user's unique identifier
* @param id - The location section's unique identifier
* @param encryptedName - The new encrypted location name
* @param originalName - The new original (unencrypted) location name
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the update affected at least one row
*/
static updateLocationSection(userId: string, id: string, encryptedName: string, originalName: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_location SET loc_name=?, loc_original_name=?, last_update=? WHERE loc_id=? AND user_id=?', [encryptedName, originalName, lastUpdate, id, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
UPDATE book_location
SET loc_name = ?, loc_original_name = ?, last_update = ?
WHERE loc_id = ? AND user_id = ?
`;
const params: SQLiteValue[] = [encryptedName, originalName, lastUpdate, id, userId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour la section d'emplacement.` : `Unable to update location section.`);
} else {
console.error("An unknown error occurred.");
@@ -205,14 +312,23 @@ export default class LocationRepo {
}
}
/**
* Deletes a location section by its ID.
* @param userId - The user's unique identifier
* @param locationId - The location section's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the deletion affected at least one row
*/
static deleteLocationSection(userId: string, locationId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_location WHERE loc_id=? AND user_id=?', [locationId, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'DELETE FROM book_location WHERE loc_id = ? AND user_id = ?';
const params: SQLiteValue[] = [locationId, userId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer la section d'emplacement.` : `Unable to delete location section.`);
} else {
console.error("An unknown error occurred.");
@@ -221,14 +337,23 @@ export default class LocationRepo {
}
}
/**
* Deletes a location element by its ID.
* @param userId - The user's unique identifier
* @param elementId - The element's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the deletion affected at least one row
*/
static deleteLocationElement(userId: string, elementId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM location_element WHERE element_id=? AND user_id=?', [elementId, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'DELETE FROM location_element WHERE element_id = ? AND user_id = ?';
const params: SQLiteValue[] = [elementId, userId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer l'élément d'emplacement.` : `Unable to delete location element.`);
} else {
console.error("An unknown error occurred.");
@@ -237,14 +362,23 @@ export default class LocationRepo {
}
}
/**
* Deletes a location sub-element by its ID.
* @param userId - The user's unique identifier
* @param subElementId - The sub-element's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the deletion affected at least one row
*/
static deleteLocationSubElement(userId: string, subElementId: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM location_sub_element WHERE sub_element_id=? AND user_id=?', [subElementId, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'DELETE FROM location_sub_element WHERE sub_element_id = ? AND user_id = ?';
const params: SQLiteValue[] = [subElementId, userId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer le sous-élément d'emplacement.` : `Unable to delete location sub-element.`);
} else {
console.error("An unknown error occurred.");
@@ -253,32 +387,53 @@ export default class LocationRepo {
}
}
/**
* Fetches all location elements and sub-elements for tagging purposes.
* @param userId - The user's unique identifier
* @param bookId - The book's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of location elements with their sub-elements
*/
static fetchLocationTags(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationElementQueryResult[] {
let result: LocationElementQueryResult[];
try {
const db: Database = System.getDb();
const query = 'SELECT se.sub_element_id AS sub_element_id, se.sub_elem_name, se.sub_elem_description, el.element_id AS element_id, el.element_name, el.element_description FROM location_sub_element AS se RIGHT JOIN location_element AS el ON se.element_id = el.element_id LEFT JOIN book_location AS lo ON el.location = lo.loc_id WHERE lo.book_id = ? AND lo.user_id = ?';
result = db.all(query, [bookId, userId]) as LocationElementQueryResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
SELECT se.sub_element_id AS sub_element_id, se.sub_elem_name, se.sub_elem_description,
el.element_id AS element_id, el.element_name, el.element_description
FROM location_sub_element AS se
RIGHT JOIN location_element AS el ON se.element_id = el.element_id
LEFT JOIN book_location AS lo ON el.location = lo.loc_id
WHERE lo.book_id = ? AND lo.user_id = ?
`;
const params: SQLiteValue[] = [bookId, userId];
const locationTags: LocationElementQueryResult[] = db.all(query, params) as LocationElementQueryResult[];
return locationTags;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les tags d'emplacement.` : `Unable to retrieve location tags.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
return result;
}
/**
* Fetches locations by their tag IDs (element or sub-element IDs).
* @param userId - The user's unique identifier
* @param locations - An array of location tag IDs to search for
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of locations matching the provided tags
* @throws Error if no tags are provided or no locations are found
*/
static fetchLocationsByTags(userId: string, locations: string[], lang: 'fr' | 'en' = 'fr'): LocationByTagResult[] {
if (locations.length === 0) {
throw new Error(lang === 'fr' ? `Aucun tag fourni.` : `No tags provided.`);
}
let result: LocationByTagResult[];
try {
const db: Database = System.getDb();
const locationIds: string = locations.map((): string => '?').join(',');
const locationPlaceholders: string = locations.map((): string => '?').join(',');
const query: string = `
SELECT el.element_name,
el.element_description,
@@ -287,32 +442,42 @@ export default class LocationRepo {
FROM location_element AS el
LEFT JOIN location_sub_element AS se ON el.element_id = se.element_id
WHERE el.user_id = ?
AND (el.element_id IN (${locationIds}) OR se.sub_element_id IN (${locationIds}))
AND (el.element_id IN (${locationPlaceholders}) OR se.sub_element_id IN (${locationPlaceholders}))
`;
const values: any[] = [userId, ...locations, ...locations];
result = db.all(query, values) as LocationByTagResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const params: SQLiteValue[] = [userId, ...locations, ...locations];
const locationsByTags: LocationByTagResult[] = db.all(query, params) as LocationByTagResult[];
if (locationsByTags.length === 0) {
throw new Error(lang === 'fr' ? `Aucun emplacement trouvé avec ces tags.` : `No locations found with these tags.`);
}
return locationsByTags;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les emplacements par tags.` : `Unable to retrieve locations by tags.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.length === 0) {
throw new Error(lang === 'fr' ? `Aucun emplacement trouvé avec ces tags.` : `No locations found with these tags.`);
}
return result;
}
static isLocationExist(userId: string, locId: string,lang: "fr" | "en"): boolean {
/**
* Checks if a location exists in the database.
* @param userId - The user's unique identifier
* @param locId - The location's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the location exists, false otherwise
*/
static isLocationExist(userId: string, locId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `book_location` WHERE `loc_id`=? AND `user_id`=?', [locId, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM `book_location` WHERE `loc_id` = ? AND `user_id` = ?';
const params: SQLiteValue[] = [locId, userId];
const existingLocation: QueryResult | null = db.get(query, params) || null;
return existingLocation !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'emplacement.` : `Unable to check location existence.`);
} else {
console.error("An unknown error occurred.");
@@ -321,14 +486,23 @@ export default class LocationRepo {
}
}
static isLocationElementExist(userId: string, elementId: string,lang: "fr" | "en"): boolean {
/**
* Checks if a location element exists in the database.
* @param userId - The user's unique identifier
* @param elementId - The element's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the location element exists, false otherwise
*/
static isLocationElementExist(userId: string, elementId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `location_element` WHERE `element_id`=? AND `user_id`=?', [elementId, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM `location_element` WHERE `element_id` = ? AND `user_id` = ?';
const params: SQLiteValue[] = [elementId, userId];
const existingElement: QueryResult | null = db.get(query, params) || null;
return existingElement !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément d'emplacement.` : `Unable to check location element existence.`);
} else {
console.error("An unknown error occurred.");
@@ -337,14 +511,23 @@ export default class LocationRepo {
}
}
static isLocationSubElementExist(userId: string, subElementId: string,lang: "fr" | "en"): boolean {
/**
* Checks if a location sub-element exists in the database.
* @param userId - The user's unique identifier
* @param subElementId - The sub-element's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the location sub-element exists, false otherwise
*/
static isLocationSubElementExist(userId: string, subElementId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `location_sub_element` WHERE `sub_element_id`=? AND `user_id`=?', [subElementId, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM `location_sub_element` WHERE `sub_element_id` = ? AND `user_id` = ?';
const params: SQLiteValue[] = [subElementId, userId];
const existingSubElement: QueryResult | null = db.get(query, params) || null;
return existingSubElement !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du sous-élément d'emplacement.` : `Unable to check location sub-element existence.`);
} else {
console.error("An unknown error occurred.");
@@ -352,26 +535,56 @@ 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[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update FROM book_location WHERE user_id=? AND book_id=?', [userId, bookId]) as BookLocationTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update
FROM book_location
WHERE user_id = ? AND book_id = ?
`;
const params: SQLiteValue[] = [userId, bookId];
const bookLocations: BookLocationTable[] = db.all(query, params) as BookLocationTable[];
return bookLocations;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les lieux.` : `Unable to retrieve locations.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static async fetchLocationElements(userId: string,locationId:string, lang: 'fr' | 'en'): Promise<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 {
const db: Database = System.getDb();
return db.all('SELECT element_id, location, user_id, element_name, original_name, element_description, last_update FROM location_element WHERE user_id=? AND location=?', [userId, locationId]) as LocationElementTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
SELECT element_id, location, user_id, element_name, original_name, element_description, last_update
FROM location_element
WHERE user_id = ? AND location = ?
`;
const params: SQLiteValue[] = [userId, locationId];
const locationElements: LocationElementTable[] = db.all(query, params) as LocationElementTable[];
return locationElements;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de lieu.` : `Unable to retrieve location elements.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -379,26 +592,50 @@ export default class LocationRepo {
}
}
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 {
const db: Database = System.getDb();
return db.all('SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update FROM location_sub_element WHERE user_id=? AND element_id=?', [userId, elementId]) as LocationSubElementTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update
FROM location_sub_element
WHERE user_id = ? AND element_id = ?
`;
const params: SQLiteValue[] = [userId, elementId];
const locationSubElements: LocationSubElementTable[] = db.all(query, params) as LocationSubElementTable[];
return locationSubElements;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les sous-éléments de lieu.` : `Unable to retrieve location sub-elements.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Fetches all synced locations for a user (used for synchronization).
* @param userId - The user's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced location records
*/
static fetchSyncedLocations(userId: string, lang: 'fr' | 'en'): SyncedLocationResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT loc_id, book_id, loc_name, last_update FROM book_location WHERE user_id = ?', [userId]) as SyncedLocationResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT loc_id, book_id, loc_name, last_update FROM book_location WHERE user_id = ?';
const params: SQLiteValue[] = [userId];
const syncedLocations: SyncedLocationResult[] = db.all(query, params) as SyncedLocationResult[];
return syncedLocations;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les lieux synchronisés.` : `Unable to retrieve synced locations.`);
} else {
console.error("An unknown error occurred.");
@@ -407,13 +644,22 @@ export default class LocationRepo {
}
}
/**
* Fetches all synced location elements for a user (used for synchronization).
* @param userId - The user's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced location element records
*/
static fetchSyncedLocationElements(userId: string, lang: 'fr' | 'en'): SyncedLocationElementResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT element_id, location, element_name, last_update FROM location_element WHERE user_id = ?', [userId]) as SyncedLocationElementResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT element_id, location, element_name, last_update FROM location_element WHERE user_id = ?';
const params: SQLiteValue[] = [userId];
const syncedLocationElements: SyncedLocationElementResult[] = db.all(query, params) as SyncedLocationElementResult[];
return syncedLocationElements;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de lieu synchronisés.` : `Unable to retrieve synced location elements.`);
} else {
console.error("An unknown error occurred.");
@@ -422,13 +668,22 @@ export default class LocationRepo {
}
}
/**
* Fetches all synced location sub-elements for a user (used for synchronization).
* @param userId - The user's unique identifier
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced location sub-element records
*/
static fetchSyncedLocationSubElements(userId: string, lang: 'fr' | 'en'): SyncedLocationSubElementResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT sub_element_id, element_id, sub_elem_name, last_update FROM location_sub_element WHERE user_id = ?', [userId]) as SyncedLocationSubElementResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT sub_element_id, element_id, sub_elem_name, last_update FROM location_sub_element WHERE user_id = ?';
const params: SQLiteValue[] = [userId];
const syncedLocationSubElements: SyncedLocationSubElementResult[] = db.all(query, params) as SyncedLocationSubElementResult[];
return syncedLocationSubElements;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les sous-éléments de lieu synchronisés.` : `Unable to retrieve synced location sub-elements.`);
} else {
console.error("An unknown error occurred.");
@@ -436,36 +691,63 @@ export default class LocationRepo {
}
}
}
/**
* Inserts a synced location from the remote server.
* @param locId - The location's unique identifier
* @param bookId - The book's unique identifier
* @param userId - The user's unique identifier
* @param locName - The encrypted location name
* @param locOriginalName - The original (unencrypted) location name
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the insertion affected at least one row
*/
static insertSyncLocation(locId: string, bookId: string, userId: string, locName: string, locOriginalName: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update)
VALUES (?, ?, ?, ?, ?, ?)`,
[locId, bookId, userId, locName, locOriginalName, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update)
VALUES (?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [locId, bookId, userId, locName, locOriginalName, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le lieu.` : `Unable to insert location.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Inserts a synced location element from the remote server.
* @param elementId - The element's unique identifier
* @param location - The parent location's unique identifier
* @param userId - The user's unique identifier
* @param elementName - The encrypted element name
* @param originalName - The original (unencrypted) element name
* @param elementDescription - The encrypted element description (can be null)
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the insertion affected at least one row
*/
static insertSyncLocationElement(elementId: string, location: string, userId: string, elementName: string, originalName: string, elementDescription: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[elementId, location, userId, elementName, originalName, elementDescription, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [elementId, location, userId, elementName, originalName, elementDescription, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer l'élément du lieu.` : `Unable to insert location element.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -473,35 +755,57 @@ export default class LocationRepo {
}
}
/**
* Inserts a synced location sub-element from the remote server.
* @param subElementId - The sub-element's unique identifier
* @param elementId - The parent element's unique identifier
* @param userId - The user's unique identifier
* @param subElemName - The encrypted sub-element name
* @param originalName - The original (unencrypted) sub-element name
* @param subElemDescription - The encrypted sub-element description (can be null)
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the insertion affected at least one row
*/
static insertSyncLocationSubElement(subElementId: string, elementId: string, userId: string, subElemName: string, originalName: string, subElemDescription: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[subElementId, elementId, userId, subElemName, originalName, subElemDescription, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [subElementId, elementId, userId, subElemName, originalName, subElemDescription, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le sous-élément du lieu.` : `Unable to insert location sub-element.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static async fetchCompleteLocationById(id: string, lang: "fr" | "en"):Promise<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 {
const db: Database = System.getDb();
return db.all(
`SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update
const query: string = `
SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update
FROM book_location
WHERE loc_id = ?`,
[id]
) as BookLocationTable[];
} catch (e:unknown){
if (e instanceof Error) {
WHERE loc_id = ?
`;
const params: SQLiteValue[] = [id];
const completeLocation: BookLocationTable[] = db.all(query, params) as BookLocationTable[];
return completeLocation;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le lieu complet.` : `Unable to retrieve complete location.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -509,17 +813,25 @@ export default class LocationRepo {
}
}
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 {
const db: Database = System.getDb();
return db.all(
`SELECT element_id, location, user_id, element_name, original_name, element_description, last_update
const query: string = `
SELECT element_id, location, user_id, element_name, original_name, element_description, last_update
FROM location_element
WHERE element_id = ?`,
[id]
) as LocationElementTable[];
} catch (e:unknown){
if (e instanceof Error) {
WHERE element_id = ?
`;
const params: SQLiteValue[] = [id];
const completeLocationElement: LocationElementTable[] = db.all(query, params) as LocationElementTable[];
return completeLocationElement;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de lieu complet.` : `Unable to retrieve complete location element.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -527,17 +839,25 @@ export default class LocationRepo {
}
}
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 {
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
const query: string = `
SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update
FROM location_sub_element
WHERE sub_element_id = ?`,
[id]
) as LocationSubElementTable[];
} catch (e:unknown){
if (e instanceof Error) {
WHERE sub_element_id = ?
`;
const params: SQLiteValue[] = [id];
const completeLocationSubElement: LocationSubElementTable[] = db.all(query, params) as LocationSubElementTable[];
return completeLocationSubElement;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le sous-élément de lieu complet.` : `Unable to retrieve complete location sub-element.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");

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";
export interface BookPlotPointsTable extends Record<string, SQLiteValue> {
@@ -27,13 +27,23 @@ export interface PlotPointQuery extends Record<string, SQLiteValue> {
}
export default class PlotPointRepository {
public static fetchAllPlotPoints(userId:string,bookId:string, lang: 'fr' | 'en'):PlotPointQuery[]{
/**
* Fetches all plot points for a specific book.
* @param userId - The ID of the user/author
* @param bookId - The ID of the book
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of plot points with their basic information
*/
public static fetchAllPlotPoints(userId: string, bookId: string, lang: 'fr' | 'en'): PlotPointQuery[] {
try {
const db: Database = System.getDb();
return db.all('SELECT plot_point_id, title, summary, linked_incident_id FROM book_plot_points WHERE author_id=? AND book_id=?', [userId, bookId]) as PlotPointQuery[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT plot_point_id, title, summary, linked_incident_id FROM book_plot_points WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const plotPoints: PlotPointQuery[] = db.all(query, params) as PlotPointQuery[];
return plotPoints;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les points d'intrigue.` : `Unable to retrieve plot points.`);
} else {
console.error("An unknown error occurred.");
@@ -41,30 +51,46 @@ export default class PlotPointRepository {
}
}
}
/**
* Inserts a new plot point into the database.
* @param plotPointId - The unique ID for the new plot point
* @param userId - The ID of the user/author
* @param bookId - The ID of the book
* @param encryptedName - The encrypted title of the plot point
* @param hashedName - The hashed title for duplicate checking
* @param incidentId - The ID of the linked incident (can be empty string)
* @param lang - The language for error messages ('fr' or 'en')
* @returns The ID of the newly created plot point
*/
static insertNewPlotPoint(plotPointId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, incidentId: string, lang: 'fr' | 'en'): string {
let existingResult: QueryResult | null;
let existingPlotPoint: QueryResult | null;
let insertResult: RunResult;
try {
const db: Database = System.getDb();
existingResult = db.get('SELECT plot_point_id FROM book_plot_points WHERE author_id=? AND book_id=? AND hashed_title=?', [userId, bookId, hashedName]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const checkQuery: string = 'SELECT plot_point_id FROM book_plot_points WHERE author_id=? AND book_id=? AND hashed_title=?';
const checkParams: SQLiteValue[] = [userId, bookId, hashedName];
existingPlotPoint = db.get(checkQuery, checkParams);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du point d'intrigue.` : `Unable to verify plot point existence.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (existingResult !== null) {
if (existingPlotPoint !== null) {
throw new Error(lang === 'fr' ? `Ce point de l'intrigue existe déjà.` : `This plot point already exists.`);
}
try {
const db: Database = System.getDb();
insertResult = db.run('INSERT INTO book_plot_points (plot_point_id,title,hashed_title,author_id,book_id,linked_incident_id,last_update) VALUES (?,?,?,?,?,?,?)', [plotPointId, encryptedName, hashedName, userId, bookId, incidentId, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const insertQuery: string = 'INSERT INTO book_plot_points (plot_point_id,title,hashed_title,author_id,book_id,linked_incident_id,last_update) VALUES (?,?,?,?,?,?,?)';
const insertParams: SQLiteValue[] = [plotPointId, encryptedName, hashedName, userId, bookId, incidentId, System.timeStampInSeconds()];
insertResult = db.run(insertQuery, insertParams);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter le point d'intrigue.` : `Unable to add plot point.`);
} else {
console.error("An unknown error occurred.");
@@ -77,14 +103,23 @@ export default class PlotPointRepository {
return plotPointId;
}
static deletePlotPoint(userId: string, plotNumId: string, lang: 'fr' | 'en'): boolean {
/**
* Deletes a plot point from the database.
* @param userId - The ID of the user/author
* @param plotPointId - The ID of the plot point to delete
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the plot point was deleted, false otherwise
*/
static deletePlotPoint(userId: string, plotPointId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_plot_points WHERE author_id=? AND plot_point_id=?', [userId, plotNumId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'DELETE FROM book_plot_points WHERE author_id=? AND plot_point_id=?';
const params: SQLiteValue[] = [userId, plotPointId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer le point d'intrigue.` : `Unable to delete plot point.`);
} else {
console.error("An unknown error occurred.");
@@ -92,14 +127,29 @@ export default class PlotPointRepository {
}
}
}
public static updatePlotPoint(userId: string, bookId: string, plotPointId: string, encryptedPlotPointName: string, plotPointHashedName: string, plotPointSummary: string, lastUpdate:number, lang: 'fr' | 'en'): boolean {
/**
* Updates an existing plot point in the database.
* @param userId - The ID of the user/author
* @param bookId - The ID of the book
* @param plotPointId - The ID of the plot point to update
* @param encryptedPlotPointName - The new encrypted title
* @param plotPointHashedName - The new hashed title
* @param plotPointSummary - The new summary
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the plot point was updated, false otherwise
*/
public static updatePlotPoint(userId: string, bookId: string, plotPointId: string, encryptedPlotPointName: string, plotPointHashedName: string, plotPointSummary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_plot_points SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND plot_point_id=?', [encryptedPlotPointName, plotPointHashedName, plotPointSummary, lastUpdate, userId, bookId, plotPointId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'UPDATE book_plot_points SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND plot_point_id=?';
const params: SQLiteValue[] = [encryptedPlotPointName, plotPointHashedName, plotPointSummary, lastUpdate, userId, bookId, plotPointId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour le point d'intrigue.` : `Unable to update plot point.`);
} else {
console.error("An unknown error occurred.");
@@ -107,26 +157,47 @@ export default class PlotPointRepository {
}
}
}
/**
* Fetches all plot points for a book with complete information for synchronization.
* @param userId - The ID of the user/author
* @param bookId - The ID of the book
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of complete plot point records
*/
static async fetchBookPlotPoints(userId: string, bookId: string, lang: 'fr' | 'en'): Promise<BookPlotPointsTable[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update FROM book_plot_points WHERE author_id=? AND book_id=?', [userId, bookId]) as BookPlotPointsTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update FROM book_plot_points WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const plotPoints: BookPlotPointsTable[] = db.all(query, params) as BookPlotPointsTable[];
return plotPoints;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les points d'intrigue.` : `Unable to retrieve plot points.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Fetches all synced plot points for a user across all books.
* @param userId - The ID of the user/author
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced plot point records with minimal information
*/
static fetchSyncedPlotPoints(userId: string, lang: 'fr' | 'en'): SyncedPlotPointResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT plot_point_id, book_id, title, last_update FROM book_plot_points WHERE author_id = ?', [userId]) as SyncedPlotPointResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT plot_point_id, book_id, title, last_update FROM book_plot_points WHERE author_id = ?';
const params: SQLiteValue[] = [userId];
const syncedPlotPoints: SyncedPlotPointResult[] = db.all(query, params) as SyncedPlotPointResult[];
return syncedPlotPoints;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les points d'intrigue synchronisés.` : `Unable to retrieve synced plot points.`);
} else {
console.error("An unknown error occurred.");
@@ -134,49 +205,80 @@ export default class PlotPointRepository {
}
}
}
/**
* Inserts a plot point during synchronization from remote data.
* @param plotPointId - The unique ID of the plot point
* @param title - The encrypted title
* @param hashedTitle - The hashed title for duplicate checking
* @param summary - The encrypted summary (can be null)
* @param linkedIncidentId - The ID of the linked incident (can be null)
* @param authorId - The ID of the author
* @param bookId - The ID of the book
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the plot point was inserted, false otherwise
*/
static insertSyncPlotPoint(plotPointId: string, title: string, hashedTitle: string, summary: string | null, linkedIncidentId: string | null, authorId: string, bookId: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_plot_points (plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[plotPointId, title, hashedTitle, summary, linkedIncidentId, authorId, bookId, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `INSERT INTO book_plot_points (plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
const params: SQLiteValue[] = [plotPointId, title, hashedTitle, summary, linkedIncidentId, authorId, bookId, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le point d'intrigue.` : `Unable to insert plot point.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static async fetchCompletePlotPointById(id: string, lang: "fr" | "en"):Promise<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 {
const db: Database = System.getDb();
return db.all(
`SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update
const query: string = `SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update
FROM book_plot_points
WHERE plot_point_id = ?`,
[id]
) as BookPlotPointsTable[];
} catch (e:unknown){
if (e instanceof Error) {
WHERE plot_point_id = ?`;
const params: SQLiteValue[] = [plotPointId];
const plotPoint: BookPlotPointsTable[] = db.all(query, params) as BookPlotPointsTable[];
return plotPoint;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le point d'intrigue complet.` : `Unable to retrieve complete plot point.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static plotPointExist(userId: string, bookId: string, plot_point_id: string,lang: "fr" | "en"): boolean {
/**
* Checks if a plot point exists in the database.
* @param userId - The ID of the user/author
* @param bookId - The ID of the book
* @param plotPointId - The ID of the plot point to check
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the plot point exists, false otherwise
*/
static plotPointExist(userId: string, bookId: string, plotPointId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result:QueryResult|null = db.get('SELECT 1 FROM book_plot_points WHERE author_id =? AND book_id =? AND plot_point_id =?', [userId, bookId, plot_point_id]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM book_plot_points WHERE author_id =? AND book_id =? AND plot_point_id =?';
const params: SQLiteValue[] = [userId, bookId, plotPointId];
const existingPlotPoint: QueryResult | null = db.get(query, params) || null;
return existingPlotPoint !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du point de intrigue.` : `Unable to check plot point existence.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");

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";
export interface UserInfosQueryResponse extends Record<string, SQLiteValue> {
@@ -45,15 +45,39 @@ export interface GuideTourResult extends Record<string, SQLiteValue> {
export default class UserRepo {
public static insertUser(uuId: string, firstName: string, lastName: string, username: string, originUsername: string, email: string, originEmail: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
/**
* Inserts a new user into the database.
* @param uuId - The unique identifier for the user
* @param firstName - The user's first name
* @param lastName - The user's last name
* @param username - The user's username
* @param originUsername - The original username from the source platform
* @param email - The user's email address
* @param originEmail - The original email from the source platform
* @param lang - The language for error messages ('fr' or 'en')
* @returns The user's UUID if insertion was successful
* @throws Error if the user cannot be registered
*/
public static insertUser(
uuId: string,
firstName: string,
lastName: string,
username: string,
originUsername: string,
email: string,
originEmail: string,
lang: 'fr' | 'en' = 'fr'
): string {
let insertResult: RunResult;
try {
const db: Database = System.getDb();
const query = `INSERT INTO erit_users (user_id, first_name, last_name, username, email, origin_email,
origin_username, plateform, term_accepted,
account_verified, reg_date)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
const values: (string | null | number)[] = [
const query: string = `
INSERT INTO erit_users (
user_id, first_name, last_name, username, email, origin_email,
origin_username, plateform, term_accepted, account_verified, reg_date
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [
uuId,
firstName,
lastName,
@@ -61,56 +85,113 @@ export default class UserRepo {
email,
originEmail,
originUsername,
'desktop', // plateform
0, // term_accepted
1, // account_verified
Date.now() // reg_date (current timestamp)
'desktop',
0,
1,
Date.now()
];
result = db.run(query, values);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'enregistrer l'utilisateur.` : `Unable to register user.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.changes > 0) {
if (insertResult.changes > 0) {
return uuId;
} else {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'enregistrement de l'utilisateur.` : `Error registering user.`);
}
}
/**
* Fetches user information from the database.
* @param userId - The unique identifier of the user to fetch
* @param lang - The language for error messages ('fr' or 'en')
* @returns The user information object
* @throws Error if the user is not found or cannot be retrieved
*/
public static fetchUserInfos(userId: string, lang: 'fr' | 'en' = 'fr'): UserInfosQueryResponse {
let result;
let userInfo: Record<string, SQLiteValue> | undefined;
try {
const db: Database = System.getDb();
result = db.get('SELECT first_name, last_name, username, email, plateform, term_accepted, account_verified, author_name, erite_points AS rite_points, user_group FROM erit_users WHERE user_id=?', [userId]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
SELECT first_name, last_name, username, email, plateform, term_accepted,
account_verified, author_name, erite_points AS rite_points, user_group
FROM erit_users
WHERE user_id = ?
`;
const params: SQLiteValue[] = [userId];
userInfo = db.get(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les informations utilisateur.` : `Unable to retrieve user information.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result) {
if (!userInfo) {
throw new Error(lang === 'fr' ? `Utilisateur non trouvé.` : `User not found.`);
}
return result as UserInfosQueryResponse;
return userInfo as UserInfosQueryResponse;
}
public static updateUserInfos(userId: string, firstName: string, lastName: string, username: string, originUsername: string, email: string, originEmail: string, originalAuthorName: string, authorName: string, lang: 'fr' | 'en' = 'fr'): boolean {
/**
* Updates user information in the database.
* @param userId - The unique identifier of the user to update
* @param firstName - The new first name
* @param lastName - The new last name
* @param username - The new username
* @param originUsername - The original username from the source platform
* @param email - The new email address
* @param originEmail - The original email from the source platform
* @param originalAuthorName - The original author name
* @param authorName - The new author name
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the update was successful, false otherwise
* @throws Error if the update fails
*/
public static updateUserInfos(
userId: string,
firstName: string,
lastName: string,
username: string,
originUsername: string,
email: string,
originEmail: string,
originalAuthorName: string,
authorName: string,
lang: 'fr' | 'en' = 'fr'
): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE `erit_users` SET `first_name`=?, `last_name`=?, `username`=?, email=?,`origin_username`=?, origin_author_name=? ,author_name=? WHERE user_id=? AND `origin_email`=?', [firstName, lastName, username, email, originUsername, originalAuthorName, authorName, userId, originEmail]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
UPDATE erit_users
SET first_name = ?, last_name = ?, username = ?, email = ?,
origin_username = ?, origin_author_name = ?, author_name = ?
WHERE user_id = ? AND origin_email = ?
`;
const params: SQLiteValue[] = [
firstName,
lastName,
username,
email,
originUsername,
originalAuthorName,
authorName,
userId,
originEmail
];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour les informations utilisateur.` : `Unable to update user information.`);
} else {
console.error("An unknown error occurred.");
@@ -119,23 +200,36 @@ export default class UserRepo {
}
}
/**
* Fetches account information for a user.
* @param userId - The unique identifier of the user
* @param lang - The language for error messages ('fr' or 'en')
* @returns The user account information object
* @throws Error if the account is not found or cannot be retrieved
*/
public static fetchAccountInformation(userId: string, lang: 'fr' | 'en' = 'fr'): UserAccountQuery {
let result;
let accountInfo: Record<string, SQLiteValue> | undefined;
try {
const db: Database = System.getDb();
result = db.get('SELECT first_name, last_name, username, author_name, email FROM erit_users WHERE user_id=?', [userId]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `
SELECT first_name, last_name, username, author_name, email
FROM erit_users
WHERE user_id = ?
`;
const params: SQLiteValue[] = [userId];
accountInfo = db.get(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les informations du compte.` : `Unable to retrieve account information.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result) {
if (!accountInfo) {
throw new Error(lang === 'fr' ? `Compte non trouvé.` : `Account not found.`);
}
return result as UserAccountQuery;
return accountInfo as UserAccountQuery;
}
}

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";
export interface BookWorldTable extends Record<string, SQLiteValue> {
@@ -62,14 +62,24 @@ export interface WorldElementValue {
}
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 {
const db: Database = System.getDb();
const result = db.get('SELECT world_id FROM book_world WHERE author_id=? AND book_id=? AND hashed_name=?', [userId,bookId,worldName]);
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT world_id FROM book_world WHERE author_id=? AND book_id=? AND hashed_name=?';
const params: SQLiteValue[] = [userId, bookId, worldName];
const world: QueryResult | null = db.get(query, params);
return world !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du monde.` : `Unable to verify world existence.`);
} else {
console.error("An unknown error occurred.");
@@ -78,32 +88,55 @@ export default class WorldRepository {
}
}
/**
* Inserts a new world into the database.
* @param worldId - The unique identifier for the new world
* @param userId - The unique identifier of the author
* @param bookId - The unique identifier of the book
* @param encryptedName - The encrypted name of the world
* @param hashedName - The hashed name of the world for uniqueness checks
* @param lang - The language for error messages ('fr' or 'en')
* @returns The world ID if insertion was successful
*/
public static insertNewWorld(worldId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string {
let result: RunResult;
let insertResult: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO book_world (world_id,author_id, book_id, name, hashed_name, last_update) VALUES (?,?,?,?,?,?)', [worldId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'INSERT INTO book_world (world_id, author_id, book_id, name, hashed_name, last_update) VALUES (?, ?, ?, ?, ?, ?)';
const params: SQLiteValue[] = [worldId, userId, bookId, encryptedName, hashedName, System.timeStampInSeconds()];
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter le monde.` : `Unable to add world.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Erreur lors de l'ajout du monde.` : `Error adding world.`);
}
return worldId;
}
public static fetchWorlds(userId: string, bookId: string, lang: 'fr' | 'en'):WorldQuery[] {
/**
* Fetches all worlds and their elements for a specific user and book.
* @param userId - The unique identifier of the user
* @param bookId - The unique identifier of the book
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of world query results with joined element data
*/
public static fetchWorlds(userId: string, bookId: string, lang: 'fr' | 'en'): WorldQuery[] {
try {
const db: Database = System.getDb();
return db.all('SELECT world.world_id AS world_id, world.name AS world_name, world.history, world.politics, world.economy, world.religion, world.languages, element.element_id AS element_id, element.name AS element_name, element.description AS element_description, element.element_type FROM book_world AS world LEFT JOIN book_world_elements AS element ON world.world_id=element.world_id WHERE world.author_id=? AND world.book_id=?', [userId, bookId]) as WorldQuery[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `SELECT world.world_id AS world_id, world.name AS world_name, world.history, world.politics, world.economy, world.religion, world.languages, element.element_id AS element_id, element.name AS element_name, element.description AS element_description, element.element_type FROM book_world AS world LEFT JOIN book_world_elements AS element ON world.world_id=element.world_id WHERE world.author_id=? AND world.book_id=?`;
const params: SQLiteValue[] = [userId, bookId];
const worlds: WorldQuery[] = db.all(query, params) as WorldQuery[];
return worlds;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les mondes.` : `Unable to retrieve worlds.`);
} else {
console.error("An unknown error occurred.");
@@ -112,14 +145,31 @@ export default class WorldRepository {
}
}
/**
* Updates a world's data in the database.
* @param userId - The unique identifier of the author
* @param worldId - The unique identifier of the world to update
* @param encryptName - The new encrypted name
* @param hashedName - The new hashed name
* @param encryptHistory - The new encrypted history
* @param encryptPolitics - The new encrypted politics
* @param encryptEconomy - The new encrypted economy
* @param encryptReligion - The new encrypted religion
* @param encryptLanguages - The new encrypted languages
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the update was successful, false otherwise
*/
public static updateWorld(userId: string, worldId: string, encryptName: string, hashedName: string, encryptHistory: string, encryptPolitics: string, encryptEconomy: string, encryptReligion: string, encryptLanguages: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_world SET name=?, hashed_name=?, history=?, politics=?, economy=?, religion=?, languages=?, last_update=? WHERE author_id=? AND world_id=?', [encryptName, hashedName, encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, lastUpdate, userId, worldId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'UPDATE book_world SET name=?, hashed_name=?, history=?, politics=?, economy=?, religion=?, languages=?, last_update=? WHERE author_id=? AND world_id=?';
const params: SQLiteValue[] = [encryptName, hashedName, encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, lastUpdate, userId, worldId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour le monde.` : `Unable to update world.`);
} else {
console.error("An unknown error occurred.");
@@ -128,19 +178,28 @@ export default class WorldRepository {
}
}
/**
* Updates multiple world elements in the database.
* @param userId - The unique identifier of the user
* @param elements - An array of world element values to update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if all updates were successful, false otherwise
*/
public static updateWorldElements(userId: string, elements: WorldElementValue[], lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const query: string = 'UPDATE book_world_elements SET name=?, description=?, element_type=?, last_update=? WHERE user_id=? AND element_id=?';
for (const element of elements) {
const result: RunResult = db.run('UPDATE book_world_elements SET name=?, description=?, element_type=?, last_update=? WHERE user_id=? AND element_id=?', [element.name, element.description, element.type, System.timeStampInSeconds(), userId, element.id]);
if (result.changes <= 0) {
const params: SQLiteValue[] = [element.name, element.description, element.type, System.timeStampInSeconds(), userId, element.id];
const updateResult: RunResult = db.run(query, params);
if (updateResult.changes <= 0) {
return false;
}
}
return true;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour les éléments du monde.` : `Unable to update world elements.`);
} else {
console.error("An unknown error occurred.");
@@ -149,14 +208,23 @@ export default class WorldRepository {
}
}
/**
* Checks if a world element with the given hashed name exists.
* @param worldNumId - The unique identifier of the world
* @param hashedName - The hashed name of the element to check
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the element exists, false otherwise
*/
public static checkElementExist(worldNumId: string, hashedName: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result = db.get('SELECT element_id FROM book_world_elements WHERE world_id=? AND original_name=?', [worldNumId, hashedName]);
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT element_id FROM book_world_elements WHERE world_id=? AND original_name=?';
const params: SQLiteValue[] = [worldNumId, hashedName];
const element: QueryResult | null = db.get(query, params);
return element !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément.` : `Unable to verify element existence.`);
} else {
console.error("An unknown error occurred.");
@@ -165,34 +233,56 @@ export default class WorldRepository {
}
}
/**
* Inserts a new world element into the database.
* @param userId - The unique identifier of the user
* @param elementId - The unique identifier for the new element
* @param elementType - The type of the element
* @param worldId - The unique identifier of the parent world
* @param encryptedName - The encrypted name of the element
* @param hashedName - The hashed name of the element for uniqueness checks
* @param lang - The language for error messages ('fr' or 'en')
* @returns The element ID if insertion was successful
*/
public static insertNewElement(userId: string, elementId: string, elementType: number, worldId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string {
let result: RunResult;
let insertResult: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO book_world_elements (element_id,world_id,user_id, name, original_name, element_type, last_update) VALUES (?,?,?,?,?,?,?)', [elementId, worldId, userId, encryptedName, hashedName, elementType, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'INSERT INTO book_world_elements (element_id, world_id, user_id, name, original_name, element_type, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)';
const params: SQLiteValue[] = [elementId, worldId, userId, encryptedName, hashedName, elementType, System.timeStampInSeconds()];
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'ajouter l'élément.` : `Unable to add element.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!result || result.changes === 0) {
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Erreur lors de l'ajout de l'élément.` : `Error adding element.`);
}
return elementId;
}
/**
* Deletes a world element from the database.
* @param userId - The unique identifier of the user
* @param elementId - The unique identifier of the element to delete
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the deletion was successful, false otherwise
*/
public static deleteElement(userId: string, elementId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('DELETE FROM book_world_elements WHERE user_id=? AND element_id=?', [userId, elementId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'DELETE FROM book_world_elements WHERE user_id=? AND element_id=?';
const params: SQLiteValue[] = [userId, elementId];
const deleteResult: RunResult = db.run(query, params);
return deleteResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de supprimer l'élément.` : `Unable to delete element.`);
} else {
console.error("An unknown error occurred.");
@@ -201,13 +291,23 @@ 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[]> {
try {
const db: Database = System.getDb();
return db.all('SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update FROM book_world WHERE author_id=? AND book_id=?', [userId, bookId]) as BookWorldTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update FROM book_world WHERE author_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const worlds: BookWorldTable[] = db.all(query, params) as BookWorldTable[];
return worlds;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les mondes.` : `Unable to retrieve worlds.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -215,13 +315,23 @@ export default class WorldRepository {
}
}
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 {
const db: Database = System.getDb();
return db.all('SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update FROM book_world_elements WHERE user_id=? AND world_id=?', [userId, worldId]) as BookWorldElementsTable[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update FROM book_world_elements WHERE user_id=? AND world_id=?';
const params: SQLiteValue[] = [userId, worldId];
const elements: BookWorldElementsTable[] = db.all(query, params) as BookWorldElementsTable[];
return elements;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments du monde.` : `Unable to retrieve world elements.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -229,13 +339,22 @@ export default class WorldRepository {
}
}
/**
* Fetches all synced worlds for a specific user.
* @param userId - The unique identifier of the user
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced world results
*/
static fetchSyncedWorlds(userId: string, lang: 'fr' | 'en'): SyncedWorldResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT world_id, book_id, name, last_update FROM book_world WHERE author_id = ?', [userId]) as SyncedWorldResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT world_id, book_id, name, last_update FROM book_world WHERE author_id = ?';
const params: SQLiteValue[] = [userId];
const syncedWorlds: SyncedWorldResult[] = db.all(query, params) as SyncedWorldResult[];
return syncedWorlds;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les mondes synchronisés.` : `Unable to retrieve synced worlds.`);
} else {
console.error("An unknown error occurred.");
@@ -244,13 +363,22 @@ export default class WorldRepository {
}
}
/**
* Fetches all synced world elements for a specific user.
* @param userId - The unique identifier of the user
* @param lang - The language for error messages ('fr' or 'en')
* @returns An array of synced world element results
*/
static fetchSyncedWorldElements(userId: string, lang: 'fr' | 'en'): SyncedWorldElementResult[] {
try {
const db: Database = System.getDb();
return db.all('SELECT element_id, world_id, name, last_update FROM book_world_elements WHERE user_id = ?', [userId]) as SyncedWorldElementResult[];
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT element_id, world_id, name, last_update FROM book_world_elements WHERE user_id = ?';
const params: SQLiteValue[] = [userId];
const syncedElements: SyncedWorldElementResult[] = db.all(query, params) as SyncedWorldElementResult[];
return syncedElements;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de récupérer les éléments de monde synchronisés.` : `Unable to retrieve synced world elements.`);
} else {
console.error("An unknown error occurred.");
@@ -259,18 +387,32 @@ export default class WorldRepository {
}
}
/**
* Inserts a synced world into the database.
* @param worldId - The unique identifier for the world
* @param name - The encrypted name of the world
* @param hashedName - The hashed name of the world
* @param authorId - The unique identifier of the author
* @param bookId - The unique identifier of the book
* @param history - The encrypted history (optional)
* @param politics - The encrypted politics (optional)
* @param economy - The encrypted economy (optional)
* @param religion - The encrypted religion (optional)
* @param languages - The encrypted languages (optional)
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the insertion was successful, false otherwise
*/
static insertSyncWorld(worldId: string, name: string, hashedName: string, authorId: string, bookId: string, history: string | null, politics: string | null, economy: string | null, religion: string | null, languages: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_world (world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[worldId, name, hashedName, authorId, bookId, history, politics, economy, religion, languages, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `INSERT INTO book_world (world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
const params: SQLiteValue[] = [worldId, name, hashedName, authorId, bookId, history, politics, economy, religion, languages, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer le monde.` : `Unable to insert world.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -278,18 +420,29 @@ export default class WorldRepository {
}
}
/**
* Inserts a synced world element into the database.
* @param elementId - The unique identifier for the element
* @param worldId - The unique identifier of the parent world
* @param userId - The unique identifier of the user
* @param elementType - The type of the element
* @param name - The encrypted name of the element
* @param originalName - The original hashed name
* @param description - The encrypted description (optional)
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the insertion was successful, false otherwise
*/
static insertSyncWorldElement(elementId: string, worldId: string, userId: string, elementType: number, name: string, originalName: string, description: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run(
`INSERT INTO book_world_elements (element_id, world_id, user_id, element_type, name, original_name, description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[elementId, worldId, userId, elementType, name, originalName, description, lastUpdate]
);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `INSERT INTO book_world_elements (element_id, world_id, user_id, element_type, name, original_name, description, last_update) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
const params: SQLiteValue[] = [elementId, worldId, userId, elementType, name, originalName, description, lastUpdate];
const insertResult: RunResult = db.run(query, params);
return insertResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible d'insérer l'élément du monde.` : `Unable to insert world element.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -297,17 +450,21 @@ export default class WorldRepository {
}
}
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 {
const db: Database = System.getDb();
return db.all(
`SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update
FROM book_world
WHERE world_id = ?`,
[id]
) as BookWorldTable[];
} catch (e:unknown){
if (e instanceof Error) {
const query: string = `SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update FROM book_world WHERE world_id = ?`;
const params: SQLiteValue[] = [id];
const worlds: BookWorldTable[] = db.all(query, params) as BookWorldTable[];
return worlds;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer le monde complet.` : `Unable to retrieve complete world.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -315,33 +472,48 @@ export default class WorldRepository {
}
}
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 {
const db: Database = System.getDb();
return db.all(
`SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update
FROM book_world_elements
WHERE element_id = ?`,
[id]
) as BookWorldElementsTable[];
} catch (e:unknown){
if (e instanceof Error) {
const query: string = `SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update FROM book_world_elements WHERE element_id = ?`;
const params: SQLiteValue[] = [id];
const elements: BookWorldElementsTable[] = db.all(query, params) as BookWorldElementsTable[];
return elements;
} catch (error: unknown) {
if (error instanceof Error) {
throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de monde complet.` : `Unable to retrieve complete world element.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
static updateWorldElement(userId: string, elementId: string, name: string, description: string, lastUpdate: number,lang: "fr" | "en"):boolean {
/**
* Updates a single world element's name and description.
* @param userId - The unique identifier of the user
* @param elementId - The unique identifier of the element to update
* @param name - The new encrypted name
* @param description - The new encrypted description
* @param lastUpdate - The timestamp of the last update
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the update was successful, false otherwise
*/
static updateWorldElement(userId: string, elementId: string, name: string, description: string, lastUpdate: number, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const query:string = `UPDATE book_world_elements SET name = ?, description = ?, last_update = FROM_UNIXTIME(?) WHERE element_id = UUID_TO_BIN(?) AND user_id = UUID_TO_BIN(?)`;
const params:(string|number)[] = [name, description, lastUpdate, elementId, userId];
const result:RunResult = db.run(query, params);
return result.changes > 0;
} catch (e:unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = `UPDATE book_world_elements SET name = ?, description = ?, last_update = ? WHERE element_id = ? AND user_id = ?`;
const params: SQLiteValue[] = [name, description, lastUpdate, elementId, userId];
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'élément du monde.` : `Unable to update world element.`);
} else {
console.error("An unknown error occurred.");
@@ -350,14 +522,24 @@ export default class WorldRepository {
}
}
static worldExist(userId: string, bookId: string, world_id: string,lang: "fr" | "en"): boolean {
/**
* Checks if a world exists for a specific user and book.
* @param userId - The unique identifier of the user
* @param bookId - The unique identifier of the book
* @param worldId - The unique identifier of the world
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the world exists, false otherwise
*/
static worldExist(userId: string, bookId: string, worldId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `book_world` WHERE `world_id`=? AND `author_id`=? AND `book_id`=?', [world_id, userId, bookId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM book_world WHERE world_id=? AND author_id=? AND book_id=?';
const params: SQLiteValue[] = [worldId, userId, bookId];
const world: QueryResult | null = db.get(query, params) || null;
return world !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence du monde.` : `Unable to check world existence.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
@@ -365,14 +547,24 @@ export default class WorldRepository {
}
}
static worldElementExist(userId: string, world_id: string, element_id: string, lang: "fr" | "en"): boolean {
/**
* Checks if a world element exists for a specific user and world.
* @param userId - The unique identifier of the user
* @param worldId - The unique identifier of the world
* @param elementId - The unique identifier of the element
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the element exists, false otherwise
*/
static worldElementExist(userId: string, worldId: string, elementId: string, lang: "fr" | "en"): boolean {
try {
const db: Database = System.getDb();
const result: QueryResult | null = db.get('SELECT 1 FROM `book_world_elements` WHERE `element_id`=? AND `world_id`=? AND `user_id`=?', [element_id, world_id, userId]) || null;
return result !== null;
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
const query: string = 'SELECT 1 FROM book_world_elements WHERE element_id=? AND world_id=? AND user_id=?';
const params: SQLiteValue[] = [elementId, worldId, userId];
const element: QueryResult | null = db.get(query, params) || null;
return element !== null;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de vérifier l'existence de l'élément du monde.` : `Unable to check world element existence.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");