import { Database, RunResult, SQLiteValue } from 'node-sqlite3-wasm'; import System from '../System.js'; export interface RemovedItemRecord extends Record { removal_id: string; table_name: string; entity_id: string; book_id: string | null; user_id: string; deleted_at: number; } /** * Repository for tracking deleted items for sync purposes. */ export default class RemovedItemsRepository { /** * Inserts a removal record into the database. * @param removalId - The unique ID for this removal record. * @param tableName - The name of the table from which the item is deleted. * @param entityId - The UUID of the deleted entity. * @param bookId - Book ID (null for series items). * @param userId - The user ID who owns the item. * @param deletedAt - Timestamp of deletion. * @param lang - The language for error messages ('fr' or 'en'). * @returns True if inserted successfully. */ public static insert( removalId: string, tableName: string, entityId: string, bookId: string | null, userId: string, deletedAt: number, lang: 'fr' | 'en' ): boolean { try { const db: Database = System.getDb(); const query: string = ` INSERT INTO removed_items (removal_id, table_name, entity_id, book_id, user_id, deleted_at) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(table_name, entity_id) DO UPDATE SET deleted_at = excluded.deleted_at `; const params: SQLiteValue[] = [removalId, tableName, entityId, bookId, userId, deletedAt]; const result: RunResult = db.run(query, params); return result.changes > 0; } catch (error: unknown) { if (error instanceof Error) { console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible d'enregistrer la suppression.` : `Unable to record deletion.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } /** * Retrieves deletions since a specific timestamp. * Used to get deletions that occurred since last sync. * @param userId - The user ID. * @param since - Timestamp to get deletions after. * @param lang - The language for error messages ('fr' or 'en'). * @returns Array of removed item records. */ public static getDeletionsSince(userId: string, since: number, lang: 'fr' | 'en'): RemovedItemRecord[] { try { const db: Database = System.getDb(); const query: string = 'SELECT * FROM removed_items WHERE user_id = ? AND deleted_at > ?'; const params: SQLiteValue[] = [userId, since]; const records: RemovedItemRecord[] = db.all(query, params) as RemovedItemRecord[]; return records; } catch (error: unknown) { if (error instanceof Error) { console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les suppressions.` : `Unable to retrieve deletions.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } /** * Checks if an entity was previously deleted. * @param tableName - The table name. * @param entityId - The entity ID. * @param lang - The language for error messages ('fr' or 'en'). * @returns True if the entity was deleted locally. */ public static wasDeleted(tableName: string, entityId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); const query: string = 'SELECT 1 FROM removed_items WHERE table_name = ? AND entity_id = ? LIMIT 1'; const params: SQLiteValue[] = [tableName, entityId]; const result = db.get(query, params); return result !== null; } catch (error: unknown) { if (error instanceof Error) { console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de vérifier si l'élément a été supprimé.` : `Unable to check if item was deleted.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } /** * Retrieves all tracked deletions for a specific book. * @param userId - The user ID. * @param bookId - The book ID. * @param lang - The language for error messages ('fr' or 'en'). * @returns Array of removed item records for that book. */ public static getDeletionsForBook(userId: string, bookId: string, lang: 'fr' | 'en'): RemovedItemRecord[] { try { const db: Database = System.getDb(); const query: string = 'SELECT * FROM removed_items WHERE user_id = ? AND book_id = ?'; const params: SQLiteValue[] = [userId, bookId]; const records: RemovedItemRecord[] = db.all(query, params) as RemovedItemRecord[]; return records; } catch (error: unknown) { if (error instanceof Error) { console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de récupérer les suppressions pour ce livre.` : `Unable to retrieve deletions for this book.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } /** * Clears all deletion records for a user. * WARNING: Only use this when wiping user data completely. * @param userId - The user ID. * @param lang - The language for error messages ('fr' or 'en'). * @returns True if cleared successfully. */ public static clearAllForUser(userId: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); const query: string = 'DELETE FROM removed_items WHERE user_id = ?'; const params: SQLiteValue[] = [userId]; const result: RunResult = db.run(query, params); return result.changes > 0; } catch (error: unknown) { if (error instanceof Error) { console.error(`DB Error: ${error.message}`); throw new Error(lang === 'fr' ? `Impossible de supprimer les enregistrements de suppression.` : `Unable to clear deletion records.`); } else { console.error("An unknown error occurred."); throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); } } } }