Files
ERitors-Scribe-Desktop/electron/database/repositories/guideline.repository.ts
natreex fc42269c8c Add synchronization support for guidelines and AI guidelines
- Implemented syncing for `GuideLine` and `AIGuideLine` entities with encryption and decryption workflows.
- Refined `GuidelineRepo` methods to check existence and update/insert records for both guideline types.
- Updated timestamp handling in `GuideLine` and `AIGuideLine` models for accurate tracking.
- Corrected QuillSense localization keys for labels and descriptions.
- Improved database queries and modularized logic for consistency.
2026-01-14 09:17:56 -05:00

568 lines
26 KiB
TypeScript

import { Database, RunResult, SQLiteValue } from "node-sqlite3-wasm";
import System from "../System.js";
export interface BookAIGuideLineTable extends Record<string, SQLiteValue> {
user_id: string;
book_id: string;
global_resume: string | null;
themes: string | null;
verbe_tense: number | null;
narrative_type: number | null;
langue: number | null;
dialogue_type: number | null;
tone: string | null;
atmosphere: string | null;
current_resume: string | null;
last_update: number;
}
export interface BookGuideLineTable extends Record<string, SQLiteValue> {
user_id: string;
book_id: string;
tone: string | null;
atmosphere: string | null;
writing_style: string | null;
themes: string | null;
symbolism: string | null;
motifs: string | null;
narrative_voice: string | null;
pacing: string | null;
intended_audience: string | null;
key_messages: string | null;
last_update: number;
}
export interface SyncedGuideLineResult extends Record<string, SQLiteValue> {
book_id: string;
last_update: number;
}
export interface SyncedAIGuideLineResult extends Record<string, SQLiteValue> {
book_id: string;
last_update: number;
}
export interface GuideLineQuery extends Record<string, SQLiteValue> {
tone: string;
atmosphere: string;
writing_style: string;
themes: string;
symbolism: string;
motifs: string;
narrative_voice: string;
pacing: string;
intended_audience: string;
key_messages: string;
}
export interface GuideLineAIQuery extends Record<string, SQLiteValue> {
user_id: string;
book_id: string;
global_resume: string | null;
themes: string | null;
verbe_tense: number | null;
narrative_type: number | null;
langue: number | null;
dialogue_type: number | null;
tone: string | null;
atmosphere: string | null;
current_resume: string | null;
meta: string;
}
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 guidelines: GuideLineQuery[];
try {
const db: Database = System.getDb();
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 guidelines;
}
/**
* 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
*/
/**
* 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 lastUpdate - The timestamp of the last update
* @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 | null, encryptedAtmosphere: string | null, encryptedWritingStyle: string | null, encryptedThemes: string | null, encryptedSymbolism: string | null, encryptedMotifs: string | null, encryptedNarrativeVoice: string | null, encryptedPacing: string | null, encryptedKeyMessages: string | null, encryptedIntendedAudience: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const updateQuery: string = 'UPDATE book_guide_line SET tone=?, atmosphere=?, writing_style=?, themes=?, symbolism=?, motifs=?, narrative_voice=?, pacing=?, intended_audience=?, key_messages=?, last_update=? WHERE user_id=? AND book_id=?';
const updateParams: SQLiteValue[] = [
encryptedTone,
encryptedAtmosphere,
encryptedWritingStyle,
encryptedThemes,
encryptedSymbolism,
encryptedMotifs,
encryptedNarrativeVoice,
encryptedPacing,
encryptedIntendedAudience,
encryptedKeyMessages,
lastUpdate,
userId,
bookId
];
const updateResult: RunResult = db.run(updateQuery, updateParams);
if (updateResult.changes > 0) {
return true;
} else {
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,
lastUpdate
];
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 de mettre à jour la ligne directrice.` : `Unable to update guideline.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* 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 lastUpdate - The timestamp of the last update
* @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 | null, dialogueType: number | null, encryptedPlotSummary: string | null, encryptedToneAtmosphere: string | null, verbTense: number | null, language: number | null, encryptedThemes: string | null, lastUpdate: number, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
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,
dialogueType,
encryptedPlotSummary,
encryptedToneAtmosphere,
verbTense,
language,
encryptedThemes,
lastUpdate,
userId,
bookId
];
const updateResult: RunResult = db.run(updateQuery, updateParams);
if (updateResult.changes > 0) {
return true;
} else {
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,
narrativeType,
language,
dialogueType,
encryptedToneAtmosphere,
encryptedToneAtmosphere,
encryptedPlotSummary,
lastUpdate
];
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'insérer la ligne directrice IA.` : `Unable to insert AI guideline.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* 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 aiGuideline: GuideLineAIQuery | null;
try {
const db: Database = System.getDb();
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 (!aiGuideline) {
throw new Error(lang === 'fr' ? `Ligne directrice IA non trouvée.` : `AI guideline not found.`);
}
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();
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();
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();
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.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* 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();
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.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Checks if a guideline exists 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 True if the guideline exists, false otherwise.
*/
static guideLineExist(userId: string, bookId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const query: string = 'SELECT 1 FROM book_guide_line WHERE user_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const result: Record<string, SQLiteValue> | undefined = db.get(query, params) as Record<string, SQLiteValue> | undefined;
return result !== undefined;
} 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 ligne directrice.` : `Unable to check guideline existence.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* Checks if an AI guideline exists 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 True if the AI guideline exists, false otherwise.
*/
static aiGuideLineExist(userId: string, bookId: string, lang: 'fr' | 'en'): boolean {
try {
const db: Database = System.getDb();
const query: string = 'SELECT 1 FROM book_ai_guide_line WHERE user_id=? AND book_id=?';
const params: SQLiteValue[] = [userId, bookId];
const result: Record<string, SQLiteValue> | undefined = db.get(query, params) as Record<string, SQLiteValue> | undefined;
return result !== undefined;
} 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 ligne directrice IA.` : `Unable to check AI guideline existence.`);
} else {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
/**
* 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 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.");
}
}
}
/**
* 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 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.");
}
}
}
}