import BookRepository, { BookCoverQuery, BookQuery, ChapterBookResult, GuideLineAIQuery, GuideLineQuery, WorldElementValue } from '../repositories/book.repository.js'; import type { IssueQuery, ActQuery, PlotPointQuery, IncidentQuery, WorldQuery } from '../repositories/book.repository.js'; import System from '../System.js'; import { getUserEncryptionKey } from '../keyManager.js'; import path from "path"; import fs from "fs"; import BookRepo from "../repositories/book.repository.js"; import Chapter, {ActChapter, ChapterContentData, ChapterProps} from "./Chapter.js"; import UserRepo from "../repositories/user.repository.js"; import ChapterRepo from "@/electron/database/repositories/chapter.repository"; import {mainStyle} from "@/electron/database/models/EpubStyle"; export interface BookProps{ id:string; type:string; authorId:string; title:string; subTitle?:string; summary?:string; serieId?:number; desiredReleaseDate?:string; desiredWordCount?:number; wordCount?:number; coverImage?:string; bookMeta?:string; } export interface GuideLine{ tone:string; atmosphere:string; writingStyle:string; themes:string; symbolism: string; motifs: string; narrativeVoice: string; pacing: string; intendedAudience: string; keyMessages: string; } interface PlotPoint { plotPointId: string, title:string, summary:string, linkedIncidentId: string | null, chapters?:ActChapter[] } interface Incident { incidentId: string, title:string, summary:string, chapters?:ActChapter[] } export interface ExportData { buffer: Buffer; fileName: string; } export interface Act { id: number; summary: string | null; incidents?:Incident[]; plotPoints?:PlotPoint[], chapters?:ActChapter[] } export interface Issue { id: string, name: string } export interface WorldElement { id: string; name: string; description: string; type?:number; } export interface WorldProps { id: string; name: string; history: string; politics: string; economy: string; religion: string; languages: string; laws: WorldElement[]; biomes: WorldElement[]; issues: WorldElement[]; customs: WorldElement[]; kingdoms: WorldElement[]; climate: WorldElement[]; resources: WorldElement[]; wildlife: WorldElement[]; arts: WorldElement[]; ethnicGroups: WorldElement[]; socialClasses: WorldElement[]; importantCharacters: WorldElement[]; } export interface GuideLineAI { narrativeType: number|null; dialogueType: number|null; globalResume: string|null; atmosphere: string|null; verbeTense: number|null; langue: number|null; currentResume: string|null; themes: string|null; } export interface CompleteBookData { bookId: string; title: string; subTitle: string; summary: string; coverImage: string; userInfos: { firstName: string; lastName: string; authorName: string; }, chapters: CompleteChapterContent[]; } export interface CompleteChapterContent { id: string; title: string; content: string; order: number; version?: number; } export default class Book { private readonly id: string; private type:string; private authorId: string; private title: string; private subTitle: string; private summary: string; private serieId: number; private desiredReleaseDate: string; private desiredWordCount:number; private wordCount: number; private cover: string; constructor(id:string,authorId?:string) { this.id = id; if (authorId){ this.authorId = authorId; } else { this.authorId = ''; } this.title = ''; this.subTitle = ''; this.summary = ''; this.serieId = 0; this.desiredReleaseDate = ''; this.desiredWordCount = 0; this.wordCount = 0; this.cover = ''; this.type = ''; } public static async getBooks(userId: string, lang: 'fr' | 'en' = 'fr'): Promise { const userKey: string | null = getUserEncryptionKey(userId); if (!userKey) { throw new Error( lang === 'fr' ? "Clé d'encryption utilisateur non trouvée." : 'User encryption key not found.' ); } const books:BookQuery[] = BookRepository.fetchBooks(userId, lang); if (!books || books.length === 0) { return []; } return await Promise.all( books.map(async (book: BookQuery):Promise => { return { id: book.book_id, type: book.type, authorId: book.author_id, title: System.decryptDataWithUserKey(book.title, userKey), subTitle: book.sub_title ? System.decryptDataWithUserKey(book.sub_title, userKey) : '', summary: book.summary ? System.decryptDataWithUserKey(book.summary, userKey) : '', serieId: book.serie_id || 0, desiredReleaseDate: book.desired_release_date || '', desiredWordCount: book.desired_word_count || 0, wordCount: book.words_count || 0, coverImage: book.cover_image ? await this.getPicture(userId, userKey, book.cover_image) : '', }; }) ?? [] ); } public static async getCoverPicture(userId:string,bookId:string, lang: 'fr' | 'en' = 'fr'):Promise{ const query:BookCoverQuery = BookRepo.fetchBookCover(userId,bookId,lang); if (query){ const userKey:string = getUserEncryptionKey(userId); return System.decryptDataWithUserKey(query.cover_image,userKey); } else { return ''; } } public static async deleteCoverPicture(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): Promise { const coverName:string = await Book.getCoverPicture(userId,bookId,lang); return BookRepo.updateBookCover(bookId, '', userId, lang); } public static getPicture(userId: string, userKey: string, image: string, lang: 'fr' | 'en' = 'fr'): string { if (!image) return ''; try { const decryptedFileName: string = System.decryptDataWithUserKey(image, userKey); const userDirectory: string = path.join(''); fs.accessSync(userDirectory, fs.constants.R_OK); const data = fs.readFileSync(userDirectory); return data.toString('base64'); } catch (err) { return ''; } } public static async addBook(bookId: string | null, userId: string, title: string, subTitle: string, summary: string, type: string, serie: number, publicationDate: string, desiredWordCount: number, lang: 'fr' | 'en' = 'fr'): Promise { let id:string = ''; const userKey:string|null = getUserEncryptionKey(userId); const encryptedTitle:string = System.encryptDataWithUserKey(title, userKey); const encryptedSubTitle:string = subTitle ? System.encryptDataWithUserKey(subTitle, userKey) : ''; const encryptedSummary:string = summary ? System.encryptDataWithUserKey(summary, userKey) : ''; const hashedTitle:string = System.hashElement(title); const hashedSubTitle:string = subTitle ? System.hashElement(subTitle) : ''; if (BookRepo.verifyBookExist(hashedTitle,hashedSubTitle,userId,lang)){ throw new Error(lang === "fr" ? `Tu as déjà un livre intitulé ${title} - ${subTitle}.` : `You already have a book named ${title} - ${subTitle}.`); } if (bookId){ id = bookId; } else { id = System.createUniqueId(); } return BookRepo.insertBook(id,userId,encryptedTitle,hashedTitle,encryptedSubTitle,hashedSubTitle,encryptedSummary,type,serie,publicationDate,desiredWordCount,lang); } public static async getBook(userId:string,bookId: string): Promise { const book:Book = new Book(bookId); await book.getBookInfos(userId); return { id: book.getId(), type: book.getType(), authorId: book.getAuthorId(), title: book.getTitle(), subTitle: book.getSubTitle(), summary: book.getSummary(), serieId: book.getSerieId(), desiredReleaseDate: book.getDesiredReleaseDate(), desiredWordCount: book.getDesiredWordCount(), wordCount: book.getWordCount(), coverImage: book.getCover() }; } public static async getGuideLine(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): Promise { const guideLineResponse: GuideLineQuery[] = BookRepo.fetchGuideLine(userId, bookId,lang); if (guideLineResponse.length === 0) { return null; } const guideLine: GuideLineQuery = guideLineResponse[0]; const userKey: string = getUserEncryptionKey(userId); return { tone: guideLine.tone ? System.decryptDataWithUserKey(guideLine.tone, userKey) : '', atmosphere: guideLine.atmosphere ? System.decryptDataWithUserKey(guideLine.atmosphere, userKey) : '', writingStyle: guideLine.writing_style ? System.decryptDataWithUserKey(guideLine.writing_style, userKey) : '', themes: guideLine.themes ? System.decryptDataWithUserKey(guideLine.themes, userKey) : '', symbolism: guideLine.symbolism ? System.decryptDataWithUserKey(guideLine.symbolism, userKey) : '', motifs: guideLine.motifs ? System.decryptDataWithUserKey(guideLine.motifs, userKey) : '', narrativeVoice: guideLine['narrative-voice'] ? System.decryptDataWithUserKey(guideLine['narrative-voice'] as string, userKey) : '', pacing: guideLine.pacing ? System.decryptDataWithUserKey(guideLine.pacing, userKey) : '', intendedAudience: guideLine.intended_audience ? System.decryptDataWithUserKey(guideLine.intended_audience, userKey) : '', keyMessages: guideLine.key_messages ? System.decryptDataWithUserKey(guideLine.key_messages, userKey) : '', }; } public static async updateGuideLine(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, keyMessages: string | null, intendedAudience: string | null, lang: 'fr' | 'en' = 'fr'): Promise { const userKey: string = getUserEncryptionKey(userId); const encryptedTone: string = tone ? System.encryptDataWithUserKey(tone, userKey) : ''; const encryptedAtmosphere: string = atmosphere ? System.encryptDataWithUserKey(atmosphere, userKey) : ''; const encryptedWritingStyle: string = writingStyle ? System.encryptDataWithUserKey(writingStyle, userKey) : ''; const encryptedThemes: string = themes ? System.encryptDataWithUserKey(themes, userKey) : ''; const encryptedSymbolism: string = symbolism ? System.encryptDataWithUserKey(symbolism, userKey) : ''; const encryptedMotifs: string = motifs ? System.encryptDataWithUserKey(motifs, userKey) : ''; const encryptedNarrativeVoice: string = narrativeVoice ? System.encryptDataWithUserKey(narrativeVoice, userKey) : ''; const encryptedPacing: string = pacing ? System.encryptDataWithUserKey(pacing, userKey) : ''; const encryptedKeyMessages: string = keyMessages ? System.encryptDataWithUserKey(keyMessages, userKey) : ''; const encryptedIntendedAudience: string = intendedAudience ? System.encryptDataWithUserKey(intendedAudience, userKey) : ''; return BookRepo.updateGuideLine(userId, bookId, encryptedTone, encryptedAtmosphere, encryptedWritingStyle, encryptedThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedKeyMessages, encryptedIntendedAudience, lang); } public static addNewIncident(userId: string, bookId: string, name: string, lang: 'fr' | 'en' = 'fr'): string { const userKey: string = getUserEncryptionKey(userId); const encryptedName:string = System.encryptDataWithUserKey(name,userKey); const hashedName:string = System.hashElement(name); const incidentId: string = System.createUniqueId(); return BookRepo.insertNewIncident(incidentId, userId, bookId, encryptedName, hashedName, lang); } public static async getPlotPoints(userId:string, bookId: string,actChapters:ActChapter[], lang: 'fr' | 'en' = 'fr'):Promise{ const query:PlotPointQuery[] = BookRepo.fetchAllPlotPoints(userId,bookId,lang); const userKey:string = getUserEncryptionKey(userId); let plotPoints:PlotPoint[] = []; if (query.length>0){ for (const plot of query) { let chapters:ActChapter[] = []; for (const chapter of actChapters) { if (chapter.plotPointId === plot.plot_point_id){ chapters.push(chapter); } } plotPoints.push({ plotPointId: plot.plot_point_id, title: plot.title ? System.decryptDataWithUserKey(plot.title,userKey) : '', summary : plot.summary ? System.decryptDataWithUserKey(plot.summary,userKey) : '', linkedIncidentId: plot.linked_incident_id, chapters:chapters }) } } return plotPoints; } public static async getIncitentsIncidents(userId:string,bookId: string,actChapters:ActChapter[], lang: 'fr' | 'en' = 'fr'):Promise{ const query:IncidentQuery[] = BookRepo.fetchAllIncitentIncidents(userId,bookId,lang); let incidents:Incident[] = []; const userKey:string = getUserEncryptionKey(userId); if (query.length>0){ for (const incident of query) { let chapters:ActChapter[] = []; for (const chapter of actChapters) { if (chapter.incidentId === incident.incident_id){ chapters.push(chapter); } } incidents.push({ incidentId: incident.incident_id, title: incident.title ? System.decryptDataWithUserKey(incident.title,userKey) : '', summary : incident.summary ? System.decryptDataWithUserKey(incident.summary,userKey) : '', chapters:chapters }) } } return incidents; } public static removeIncident(userId: string, bookId: string, incidentId: string, lang: 'fr' | 'en' = 'fr'): boolean { return BookRepo.deleteIncident(userId, bookId, incidentId, lang); } public static async getActsData(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): Promise { const userKey:string = getUserEncryptionKey(userId); const actChapters:ActChapter[] = Chapter.getAllChapterFromActs(userId, bookId, lang); const query: ActQuery[] = BookRepo.fetchAllActs(userId, bookId, lang); const incidents: Incident[] = await Book.getIncitentsIncidents(userId, bookId, actChapters); const plotsPoint: PlotPoint[] = await Book.getPlotPoints(userId, bookId, actChapters); let acts: Act[] = []; acts.push({ id: 1, summary: '', chapters: actChapters.filter((chapter: ActChapter) => chapter.actId === 1) }) acts.push({ id: 2, summary: '', incidents: incidents ? incidents : [], }) acts.push({ id: 3, summary: '', plotPoints: plotsPoint ? plotsPoint : [], }) 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 (query.length > 0) { for (const act of query) { acts[act.act_index - 1].summary = act.summary && userKey ? System.decryptDataWithUserKey(act.summary, userKey) : ''; } } return acts; } public static async getIssuesFromBook(userId:string,bookId:string, lang: 'fr' | 'en' = 'fr'):Promise{ const query:IssueQuery[] = BookRepo.fetchIssuesFromBook(userId,bookId,lang); const userKey:string = getUserEncryptionKey(userId); let issues:Issue[] = []; if (query.length>0){ for (const issue of query) { issues.push({ id:issue.issue_id, name: System.decryptDataWithUserKey(issue.name,userKey) }) } } return issues; } static updateBookBasicInformation(userId: string, title: string, subTitle: string, summary: string, publicationDate: string, wordCount: number, bookId: string, lang: 'fr' | 'en' = 'fr'): boolean { const userKey:string = getUserEncryptionKey(userId); const encryptedTitle:string = System.encryptDataWithUserKey(title, userKey); const encryptedSubTitle:string = subTitle ? System.encryptDataWithUserKey(subTitle, userKey) : ''; const encryptedSummary:string = summary ? System.encryptDataWithUserKey(summary, userKey) : ''; const hashedTitle:string = System.hashElement(title); const hashedSubTitle:string = subTitle ? System.hashElement(subTitle) : ''; return BookRepo.updateBookBasicInformation(userId, encryptedTitle, hashedTitle, encryptedSubTitle, hashedSubTitle, encryptedSummary, publicationDate, wordCount, bookId, lang); } static addNewPlotPoint(userId: string, bookId: string, incidentId: string, name: string, lang: 'fr' | 'en' = 'fr'): string { const userKey:string = getUserEncryptionKey(userId); const encryptedName:string = System.encryptDataWithUserKey(name, userKey); const hashedName:string = System.hashElement(name); const plotPointId: string = System.createUniqueId(); return BookRepo.insertNewPlotPoint(plotPointId, userId, bookId, encryptedName, hashedName, incidentId, lang); } static removePlotPoint(userId: string, plotId: string, lang: 'fr' | 'en' = 'fr'): boolean{ return BookRepo.deletePlotPoint(userId, plotId, lang); } public static addNewIssue(userId: string, bookId: string, name: string, lang: 'fr' | 'en' = 'fr'): string { const userKey:string = getUserEncryptionKey(userId); const encryptedName:string = System.encryptDataWithUserKey(name,userKey); const hashedName:string = System.hashElement(name); const issueId: string = System.createUniqueId(); return BookRepo.insertNewIssue(issueId, userId, bookId, encryptedName, hashedName,lang); } public static removeIssue(userId: string, issueId: string, lang: 'fr' | 'en' = 'fr'): boolean{ return BookRepo.deleteIssue(userId, issueId,lang); } public static async updateAct(acts: Act[], userId: string, bookId: string, userKey: string, lang: 'fr' | 'en' = 'fr'): Promise { for (const act of acts) { const incidents: Incident[] = act.incidents ? act.incidents : []; const actId: number = act.id; if (actId === 1 || actId === 4 || actId === 5) { const actSummary: string = act.summary ? System.encryptDataWithUserKey(act.summary, userKey) : ''; try { BookRepo.updateActSummary(userId, bookId, actId, actSummary,lang); } catch (e: unknown) { const actSummaryId: string = System.createUniqueId(); BookRepo.insertActSummary(actSummaryId, userId, bookId, actId, actSummary,lang); } if (act.chapters) { Chapter.updateChapterInfos(act.chapters, userId, actId, bookId, null, null, lang); } } else if (actId === 2) { for (const incident of incidents) { const incidentSummary: string = incident.summary ? System.encryptDataWithUserKey(incident.summary, userKey) : ''; const incidentId: string = incident.incidentId; const incidentName: string = incident.title; const incidentHashedName: string = System.hashElement(incidentName); const encryptedIncidentName: string = System.encryptDataWithUserKey(incidentName, userKey); BookRepo.updateIncident(userId, bookId, incidentId, encryptedIncidentName, incidentHashedName, incidentSummary, lang); if (incident.chapters) { Chapter.updateChapterInfos(incident.chapters, userId, actId, bookId, incidentId, null, lang); } } } else { const plotPoints: PlotPoint[] = act.plotPoints ? act.plotPoints : []; for (const plotPoint of plotPoints) { const plotPointSummary: string = plotPoint.summary ? System.encryptDataWithUserKey(plotPoint.summary, userKey) : ''; const plotPointId: string = plotPoint.plotPointId; const plotPointName: string = plotPoint.title; const plotPointHashedName: string = System.hashElement(plotPointName); const encryptedPlotPointName: string = System.encryptDataWithUserKey(plotPointName, userKey); BookRepo.updatePlotPoint(userId, bookId, plotPointId, encryptedPlotPointName, plotPointHashedName, plotPointSummary, lang); if (plotPoint.chapters) { Chapter.updateChapterInfos(plotPoint.chapters, userId, actId, bookId, null, plotPointId, lang); } } } } return true; } public static updateStory(userId: string, bookId: string, acts: Act[], mainChapters: ChapterProps[], lang: 'fr' | 'en' = 'fr'): boolean { const userKey: string = getUserEncryptionKey(userId); Book.updateAct(acts, userId, bookId, userKey, lang); for (const chapter of mainChapters) { const chapterId: string = chapter.chapterId; const chapterTitle: string = chapter.title; const chapterHashedTitle: string = System.hashElement(chapterTitle); const encryptedTitle: string = System.encryptDataWithUserKey(chapterTitle, userKey); const chapterOrder: number = chapter.chapterOrder; ChapterRepo.updateChapter(userId, chapterId, encryptedTitle, chapterHashedTitle, chapterOrder, lang); } return true; } public static addNewWorld(userId: string, bookId: string, worldName: string, lang: 'fr' | 'en' = 'fr'): string { const userKey: string = getUserEncryptionKey(userId); const hashedName: string = System.hashElement(worldName); if (BookRepo.checkWorldExist(userId, bookId, hashedName, lang)) { throw new Error(lang === "fr" ? `Tu as déjà un monde ${worldName}.` : `You already have a world named ${worldName}.`); } const encryptedName: string = System.encryptDataWithUserKey(worldName, userKey); const worldId: string = System.createUniqueId(); return BookRepo.insertNewWorld(worldId, userId, bookId, encryptedName, hashedName, lang); } public static getWorlds(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): WorldProps[] { const worldElements: WorldQuery[] = BookRepo.fetchWorlds(userId, bookId, lang); const userKey: string = getUserEncryptionKey(userId); let worlds: WorldProps[] = [] for (const element of worldElements) { const existWorld: WorldProps | undefined = worlds.find((world: WorldProps) => world.id === element.world_id); if (!existWorld) { const newWorld: WorldProps = { id: element.world_id, name: System.decryptDataWithUserKey(element.world_name, userKey), history: element.history ? System.decryptDataWithUserKey(element.history, userKey) : '', politics: element.politics ? System.decryptDataWithUserKey(element.politics, userKey) : '', economy: element.economy ? System.decryptDataWithUserKey(element.economy, userKey) : '', religion: element.religion ? System.decryptDataWithUserKey(element.religion, userKey) : '', languages: element.languages ? System.decryptDataWithUserKey(element.languages, userKey) : '', laws: [], biomes: [], issues: [], customs: [], kingdoms: [], climate: [], resources: [], wildlife: [], arts: [], ethnicGroups: [], socialClasses: [], importantCharacters: [], }; worlds.push(newWorld); if (element.element_type) { const newElement = { id: element.element_id as string, name: element.element_name ? System.decryptDataWithUserKey(element.element_name, userKey) : '', description: element.element_description ? System.decryptDataWithUserKey(element.element_description, userKey) : '' }; switch (element.element_type) { case 1: worlds[worlds.length - 1].laws.push(newElement); break; case 2: worlds[worlds.length - 1].biomes.push(newElement); break; case 3: worlds[worlds.length - 1].issues.push(newElement); break; case 4: worlds[worlds.length - 1].customs.push(newElement); break; case 5: worlds[worlds.length - 1].kingdoms.push(newElement); break; case 6: worlds[worlds.length - 1].climate.push(newElement); break; case 7: worlds[worlds.length - 1].resources.push(newElement); break; case 8: worlds[worlds.length - 1].wildlife.push(newElement); break; case 9: worlds[worlds.length - 1].arts.push(newElement); break; case 10: worlds[worlds.length - 1].ethnicGroups.push(newElement); break; case 11: worlds[worlds.length - 1].socialClasses.push(newElement); break; case 12: worlds[worlds.length - 1].importantCharacters.push(newElement); break; } } } else { const existingElement = { id: element.element_id as string, name: element.element_name ? System.decryptDataWithUserKey(element.element_name, userKey) : '', description: element.element_description ? System.decryptDataWithUserKey(element.element_description, userKey) : '' }; switch (element.element_type) { case 1: existWorld.laws.push(existingElement); break; case 2: existWorld.biomes.push(existingElement); break; case 3: existWorld.issues.push(existingElement); break; case 4: existWorld.customs.push(existingElement); break; case 5: existWorld.kingdoms.push(existingElement); break; case 6: existWorld.climate.push(existingElement); break; case 7: existWorld.resources.push(existingElement); break; case 8: existWorld.wildlife.push(existingElement); break; case 9: existWorld.arts.push(existingElement); break; case 10: existWorld.ethnicGroups.push(existingElement); break; case 11: existWorld.socialClasses.push(existingElement); break; case 12: existWorld.importantCharacters.push(existingElement); break; } } } return worlds; } public static updateWorld(userId: string, world: WorldProps, lang: 'fr' | 'en' = 'fr'): boolean { const userKey: string = getUserEncryptionKey(userId); const encryptName: string = world.name ? System.encryptDataWithUserKey(world.name, userKey) : ''; const encryptHistory: string = world.history ? System.encryptDataWithUserKey(world.history, userKey) : ''; const encryptPolitics: string = world.politics ? System.encryptDataWithUserKey(world.politics, userKey) : ''; const encryptEconomy: string = world.economy ? System.encryptDataWithUserKey(world.economy, userKey) : ''; const encryptReligion: string = world.religion ? System.encryptDataWithUserKey(world.religion, userKey) : ''; const encryptLanguages: string = world.languages ? System.encryptDataWithUserKey(world.languages, userKey) : ''; let elements: WorldElementValue[] = []; const elementTypes: { key: keyof WorldProps, elements: WorldElement[] }[] = [ {key: 'laws', elements: world.laws}, {key: 'biomes', elements: world.biomes}, {key: 'issues', elements: world.issues}, {key: 'customs', elements: world.customs}, {key: 'kingdoms', elements: world.kingdoms}, {key: 'climate', elements: world.climate}, {key: 'resources', elements: world.resources}, {key: 'wildlife', elements: world.wildlife}, {key: 'arts', elements: world.arts}, {key: 'ethnicGroups', elements: world.ethnicGroups}, {key: 'socialClasses', elements: world.socialClasses}, {key: 'importantCharacters', elements: world.importantCharacters} ]; elementTypes.forEach(({key, elements: elementsList}) => { elements = elements.concat(elementsList.map((element: WorldElement) => { const encryptedName: string = System.encryptDataWithUserKey(element.name, userKey); const hashedName: string = System.hashElement(element.name); const encryptedDescription: string = element.description ? System.encryptDataWithUserKey(element.description, userKey) : ''; const elementType: number = Book.getElementTypes(key); return { id: element.id, name: encryptedName, hashedName: hashedName, description: encryptedDescription, type: elementType }; })); }); BookRepo.updateWorld(userId, world.id, encryptName, System.hashElement(world.name), encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, lang); return BookRepo.updateWorldElements(userId, elements, lang); } public static addNewElementToWorld(userId: string, worldId: string, elementName: string, elementType: string, lang: 'fr' | 'en' = 'fr'): string { const userKey: string = getUserEncryptionKey(userId); const hashedName: string = System.hashElement(elementName); if (BookRepo.checkElementExist(worldId, hashedName, lang)) { throw new Error(lang === "fr" ? `Vous avez déjà un élément avec ce nom ${elementName}.` : `You already have an element named ${elementName}.`); } const elementTypeId: number = Book.getElementTypes(elementType); const encryptedName: string = System.encryptDataWithUserKey(elementName, userKey); const elementId: string = System.createUniqueId(); return BookRepo.insertNewElement(userId, elementId, elementTypeId, worldId, encryptedName, hashedName, lang); } public static getElementTypes(elementType:string):number{ switch (elementType){ case 'laws': return 1; case 'biomes': return 2; case 'issues': return 3; case 'customs': return 4; case 'kingdoms': return 5; case 'climate': return 6; case 'resources': return 7; case 'wildlife': return 8; case 'arts': return 9; case 'ethnicGroups': return 10; case 'socialClasses': return 11; case 'importantCharacters': return 12; default: return 0; } } public static removeElementFromWorld(userId: string, elementId: string, lang: 'fr' | 'en' = 'fr'): boolean { return BookRepo.deleteElement(userId, elementId, lang); } public static removeBook(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): boolean { return BookRepo.deleteBook(userId, bookId, lang); } static getGuideLineAI(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): GuideLineAI { const userKey: string = getUserEncryptionKey(userId); try { const guideLine: GuideLineAIQuery = BookRepo.fetchGuideLineAI(userId, bookId, lang); return { narrativeType: guideLine.narrative_type, dialogueType: guideLine.dialogue_type, globalResume: guideLine.global_resume ? System.decryptDataWithUserKey(guideLine.global_resume, userKey) : '', atmosphere: guideLine.atmosphere ? System.decryptDataWithUserKey(guideLine.atmosphere, userKey) : '', verbeTense: guideLine.verbe_tense, themes: guideLine.themes ? System.decryptDataWithUserKey(guideLine.themes, userKey) : '', currentResume: guideLine.current_resume ? System.decryptDataWithUserKey(guideLine.current_resume, userKey) : '', langue: guideLine.langue } } catch (e: unknown) { if (e instanceof Error && e.message.includes('not found')) { return { narrativeType: 0, dialogueType: 0, globalResume: '', atmosphere: '', verbeTense: 0, themes: '', currentResume: '', langue: 0 } } if (e instanceof Error) { throw new Error(e.message); } else { console.error(lang === 'fr' ? "Erreur inconnue lors de la récupération de la ligne directrice de l'IA." : "Unknown error while fetching AI guideline."); throw new Error(lang === 'fr' ? "Erreur inconnue lors de la récupération de la ligne directrice de l'IA." : "Unknown error while fetching AI guideline."); } } } public static setAIGuideLine(userId: string, bookId: string, narrativeType: number, dialogueType: number, plotSummary: string, toneAtmosphere: string, verbTense: number, language: number, themes: string, lang: 'fr' | 'en' = 'fr'): boolean { const userKey: string = getUserEncryptionKey(userId); const encryptedPlotSummary: string = plotSummary ? System.encryptDataWithUserKey(plotSummary, userKey) : ''; const encryptedToneAtmosphere: string = toneAtmosphere ? System.encryptDataWithUserKey(toneAtmosphere, userKey) : ''; const encryptedThemes: string = themes ? System.encryptDataWithUserKey(themes, userKey) : ''; return BookRepo.insertAIGuideLine(userId, bookId, narrativeType, dialogueType, encryptedPlotSummary, encryptedToneAtmosphere, verbTense, language, encryptedThemes, lang); } public getId(): string { return this.id; } public getAuthorId(): string { return this.authorId; } public getTitle(): string { return this.title; } public getSubTitle(): string { return this.subTitle; } public getSummary(): string { return this.summary; } public getSerieId(): number { return this.serieId; } public getDesiredReleaseDate(): string { return this.desiredReleaseDate; } public getDesiredWordCount(): number { return this.desiredWordCount; } public getWordCount(): number { return this.wordCount; } public getCover(): string { return this.cover; } public getType(): string { return this.type; } static completeBookData(userId: string, id: string, lang: 'fr' | 'en' = 'fr'): CompleteBookData { const data = BookRepo.fetchBook(id, userId, lang); const chapters: ChapterBookResult[] = BookRepo.fetchCompleteBookChapters(id, lang); const userKey: string = getUserEncryptionKey(userId); const userInfos = UserRepo.fetchAccountInformation(userId, lang); const bookTitle: string = data.title ? System.decryptDataWithUserKey(data.title, userKey) : ''; const decryptedChapters: any[] = []; for (const chapter of chapters) { if (!chapter.meta_chapter) continue; if (!chapter.meta_chapter_content) continue; decryptedChapters.push({ id: '', title: chapter.title ? System.decryptDataWithUserKey(chapter.title, userKey) : '', content: chapter.content ? System.decryptDataWithUserKey(chapter.content, userKey) : '', order: chapter.chapter_order }) } const coverImage: string = data.cover_image ? Book.getPicture(userId, userKey, data.cover_image, lang) : ''; return { bookId: id, title: bookTitle, subTitle: data.sub_title ? System.decryptDataWithUserKey(data.sub_title, userKey) : '', summary: data.summary ? System.decryptDataWithUserKey(data.summary, userKey) : '', coverImage: coverImage, userInfos: { firstName: userInfos.first_name ? System.decryptDataWithUserKey(userInfos.first_name, userKey) : '', lastName: userInfos.last_name ? System.decryptDataWithUserKey(userInfos.last_name, userKey) : '', authorName: userInfos.author_name ? System.decryptDataWithUserKey(userInfos.author_name, userKey) : '', }, chapters: decryptedChapters }; } static getChaptersOrSheet(bookChapters: CompleteChapterContent[]): ChapterContentData[] { const chapters: ChapterContentData[] = []; const haveSheet: CompleteChapterContent | undefined = bookChapters.find((chapter: CompleteChapterContent): boolean => chapter.order === -1); const haveChapter: CompleteChapterContent | undefined = bookChapters.find((chapter: CompleteChapterContent): boolean => chapter.order > 0); if (haveSheet && !haveChapter) { chapters.push({ title: haveSheet.title, chapterOrder: haveSheet.order, content: System.htmlToText(Chapter.tipTapToHtml(JSON.parse(haveSheet.content))), wordsCount: 0, version: haveSheet.version || 0 }); } else if (haveChapter) { for (const chapter of bookChapters) { if (chapter.order < 0) continue; chapters.push({ title: chapter.title, chapterOrder: chapter.order, content: System.htmlToText(Chapter.tipTapToHtml(JSON.parse(chapter.content))), wordsCount: 0, version: chapter.version || 0 }); } } return chapters; } static getAllChapters(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterContentData[] { try { const book: CompleteBookData = Book.completeBookData(userId, bookId, lang); return Book.getChaptersOrSheet(book.chapters); } catch (e: unknown) { return []; } } public getBookInfos(userId: string, lang: 'fr' | 'en' = 'fr'): void { const book: BookQuery = BookRepo.fetchBook(this.id, userId, lang); const userKey: string = getUserEncryptionKey(userId); if (book) { this.authorId = book.author_id; this.type = book.type; this.title = book.title ? System.decryptDataWithUserKey(book.title, userKey) : ''; this.subTitle = book.sub_title ? System.decryptDataWithUserKey(book.sub_title, userKey) : ''; this.summary = book.summary ? System.decryptDataWithUserKey(book.summary, userKey) : ''; this.serieId = book.serie_id ?? 0; this.desiredReleaseDate = book.desired_release_date ?? ''; this.desiredWordCount = book.desired_word_count ?? 0; this.wordCount = book.words_count ?? 0; this.cover = book.cover_image ? Book.getPicture(userId, userKey, book.cover_image, lang) : ''; } else { this.authorId = ''; this.title = ''; this.subTitle = ''; this.summary = ''; this.serieId = 0; this.desiredReleaseDate = ''; this.wordCount = 0; this.cover = ''; } } }