import { getUserEncryptionKey } from "../keyManager.js"; import System from "../System.js"; import PlotPoint, { PlotPointProps, PlotPointStory } from "./PlotPoint.js"; import Incident, { IncidentProps, IncidentStory } from "./Incident.js"; import ActRepository, { ActQuery } from "../repositories/act.repository.js"; import Chapter, { ChapterProps } from "./Chapter.js"; import IncidentRepository from "../repositories/incident.repository.js"; import PlotPointRepository from "../repositories/plotpoint.repository.js"; import ChapterRepo from "../repositories/chapter.repository.js"; export interface ActProps { id: number; summary: string | null; incidents?: IncidentProps[]; plotPoints?: PlotPointProps[]; chapters?: ActChapter[]; } export interface ActStory { actId: number; summary: string; chapterSummary: string; chapterGoal: string; incidents: IncidentStory[]; plotPoints: PlotPointStory[]; } export interface ActChapter { chapterInfoId: number; chapterId: string; title: string; chapterOrder: number; actId: number; incidentId: string | null; plotPointId: string | null; summary: string; goal: string; } export interface SyncedActSummary { id: string; lastUpdate: number; } export default class Act { /** * Retrieves all acts data for a specific book, including chapters, incidents, and plot points. * Decrypts summaries using the user's encryption key. * @param userId - The unique identifier of the user * @param bookId - The unique identifier of the book * @param lang - The language for localization ('fr' or 'en'), defaults to 'fr' * @returns A promise resolving to an array of Act objects with their associated data */ public static async getActsData(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): Promise { const userEncryptionKey: string = getUserEncryptionKey(userId); const actChapters: ActChapter[] = Chapter.getAllChapterFromActs(userId, bookId, lang); const actQueries: ActQuery[] = ActRepository.fetchAllActs(userId, bookId, lang); const bookIncidents: IncidentProps[] = await Incident.getIncitentsIncidents(userId, bookId, actChapters); const bookPlotPoints: PlotPointProps[] = await PlotPoint.getPlotPoints(userId, bookId, actChapters); const acts: ActProps[] = []; acts.push({ id: 1, summary: '', chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 1) }); acts.push({ id: 2, summary: '', incidents: bookIncidents ? bookIncidents : [], }); acts.push({ id: 3, summary: '', plotPoints: bookPlotPoints ? bookPlotPoints : [], }); acts.push({ id: 4, summary: '', chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 4) }); acts.push({ id: 5, summary: '', chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 5) }); if (actQueries.length > 0) { for (const actQuery of actQueries) { acts[actQuery.act_index - 1].summary = actQuery.summary && userEncryptionKey ? System.decryptDataWithUserKey(actQuery.summary, userEncryptionKey) : ''; } } return acts; } /** * Updates multiple acts including their summaries, incidents, plot points, and chapter information. * Encrypts all sensitive data before storing in the database. * @param acts - Array of act properties to update * @param userId - The unique identifier of the user * @param bookId - The unique identifier of the book * @param userKey - The user's encryption key for data encryption * @param lang - The language for localization ('fr' or 'en'), defaults to 'fr' * @returns A promise resolving to true when all updates are complete */ public static async updateAct(acts: ActProps[], userId: string, bookId: string, userKey: string, lang: 'fr' | 'en' = 'fr'): Promise { for (const act of acts) { const actIncidents: IncidentProps[] = act.incidents ? act.incidents : []; const actId: number = act.id; if (actId === 1 || actId === 4 || actId === 5) { const encryptedActSummary: string = act.summary ? System.encryptDataWithUserKey(act.summary, userKey) : ''; try { ActRepository.updateActSummary(userId, bookId, actId, encryptedActSummary, System.timeStampInSeconds(), lang); } catch (error: unknown) { const newActSummaryId: string = System.createUniqueId(); ActRepository.insertActSummary(newActSummaryId, userId, bookId, actId, encryptedActSummary, lang); } if (act.chapters) { Chapter.updateChapterInfos(act.chapters, userId, actId, bookId, null, null, lang); } } else if (actId === 2) { for (const incident of actIncidents) { const encryptedIncidentSummary: string = incident.summary ? System.encryptDataWithUserKey(incident.summary, userKey) : ''; const incidentId: string = incident.incidentId; const incidentTitle: string = incident.title; const hashedIncidentTitle: string = System.hashElement(incidentTitle); const encryptedIncidentTitle: string = System.encryptDataWithUserKey(incidentTitle, userKey); IncidentRepository.updateIncident(userId, bookId, incidentId, encryptedIncidentTitle, hashedIncidentTitle, encryptedIncidentSummary, System.timeStampInSeconds(), lang); if (incident.chapters) { Chapter.updateChapterInfos(incident.chapters, userId, actId, bookId, incidentId, null, lang); } } } else { const actPlotPoints: PlotPointProps[] = act.plotPoints ? act.plotPoints : []; for (const plotPoint of actPlotPoints) { const encryptedPlotPointSummary: string = plotPoint.summary ? System.encryptDataWithUserKey(plotPoint.summary, userKey) : ''; const plotPointId: string = plotPoint.plotPointId; const plotPointTitle: string = plotPoint.title; const hashedPlotPointTitle: string = System.hashElement(plotPointTitle); const encryptedPlotPointTitle: string = System.encryptDataWithUserKey(plotPointTitle, userKey); PlotPointRepository.updatePlotPoint(userId, bookId, plotPointId, encryptedPlotPointTitle, hashedPlotPointTitle, encryptedPlotPointSummary, System.timeStampInSeconds(), lang); if (plotPoint.chapters) { Chapter.updateChapterInfos(plotPoint.chapters, userId, actId, bookId, null, plotPointId, lang); } } } } return true; } /** * Updates the story structure including acts and main chapters. * Encrypts chapter titles and updates their order in the database. * @param userId - The unique identifier of the user * @param bookId - The unique identifier of the book * @param acts - Array of act properties to update * @param mainChapters - Array of main chapter properties to update * @param lang - The language for localization ('fr' or 'en'), defaults to 'fr' * @returns True when all updates are complete */ public static updateStory(userId: string, bookId: string, acts: ActProps[], mainChapters: ChapterProps[], lang: 'fr' | 'en' = 'fr'): boolean { const userEncryptionKey: string = getUserEncryptionKey(userId); Act.updateAct(acts, userId, bookId, userEncryptionKey, lang).then(); for (const chapter of mainChapters) { const chapterId: string = chapter.chapterId; const chapterTitle: string = chapter.title; const chapterOrder: number = chapter.chapterOrder; Chapter.updateChapter(userId, chapterId, chapterTitle, chapterOrder, lang); } return true; } }