Files
ERitors-Scribe-Desktop/electron/database/models/Book.ts
natreex d018e75be4 Remove DataService and OfflineDataService, refactor book and character operations to use streamlined handlers in LocalSystem
- Delete `data.service.ts` and `offline-data.service.ts`, consolidating functionality into `LocalSystem`.
- Refactor book, character, and conversation operations to adopt unified, multilingual, and session-enabled IPC handlers in `LocalSystem`.
- Simplify redundant legacy methods, enhancing maintainability and consistency.
2025-11-18 21:02:38 -05:00

934 lines
43 KiB
TypeScript

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<BookProps[]> {
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<BookProps> => {
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<string>{
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<boolean> {
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<string> {
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<BookProps> {
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<GuideLine | null> {
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<boolean> {
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<PlotPoint[]>{
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<Incident[]>{
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<Act[]> {
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<Issue[]>{
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<boolean> {
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 = '';
}
}
}