Files
ERitors-Scribe-Desktop/electron/database/models/Book.ts
natreex 7f34421212 Add error handling, enhance syncing, and refactor deletion logic
- Introduce new error messages for syncing and book deletion in `en.json`.
- Update `DeleteBook` to support local-only deletion and synced book management.
- Refine offline/online behavior with `deleteLocalToo` checkbox and update related state handling.
- Extend repository and IPC methods to handle optional IDs for updates.
- Add `SyncQueueContext` for queueing offline changes and improving synchronization workflows.
- Enhance refined text generation logic in `DraftCompanion` and `GhostWriter` components.
- Replace PUT with PATCH for world updates to align with API expectations.
- Streamline `AlertBox` by integrating dynamic translation keys for deletion prompts.
2026-01-10 15:50:03 -05:00

2264 lines
118 KiB
TypeScript

import type {
ActQuery,
BookActSummariesTable, BookAIGuideLineTable, BookChapterContentTable, BookChapterInfosTable, BookChaptersTable,
BookCharactersAttributesTable,
BookCharactersTable, BookGuideLineTable, BookIncidentsTable, BookIssuesTable, BookLocationTable,
BookPlotPointsTable, BookWorldElementsTable, BookWorldTable,
EritBooksTable,
IncidentQuery,
IssueQuery, LocationElementTable, LocationSubElementTable,
PlotPointQuery,
SyncedActSummaryResult,
SyncedAIGuideLineResult,
SyncedBookResult,
SyncedChapterContentResult,
SyncedChapterInfoResult,
SyncedChapterResult,
SyncedCharacterAttributeResult,
SyncedCharacterResult,
SyncedGuideLineResult,
SyncedIncidentResult,
SyncedIssueResult,
SyncedLocationElementResult,
SyncedLocationResult,
SyncedLocationSubElementResult,
SyncedPlotPointResult,
SyncedWorldElementResult,
SyncedWorldResult,
WorldQuery
} from '../repositories/book.repository.js';
import BookRepository from '../repositories/book.repository.js';
import BookRepo, {
BookCoverQuery,
BookQuery,
ChapterBookResult,
GuideLineAIQuery,
GuideLineQuery,
WorldElementValue
} from '../repositories/book.repository.js';
import System from '../System.js';
import {getUserEncryptionKey} from '../keyManager.js';
import path from "path";
import fs from "fs";
import Chapter, {ActChapter, ChapterContentData, ChapterProps} from "./Chapter.js";
import UserRepo from "../repositories/user.repository.js";
import ChapterRepo from "../repositories/chapter.repository.js";
import CharacterRepo from "../repositories/character.repository.js";
import LocationRepo from "../repositories/location.repository.js";
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 CompleteBook {
eritBooks: EritBooksTable[];
actSummaries: BookActSummariesTable[];
aiGuideLine: BookAIGuideLineTable[];
chapters: BookChaptersTable[];
chapterContents: BookChapterContentTable[];
chapterInfos: BookChapterInfosTable[];
characters: BookCharactersTable[];
characterAttributes: BookCharactersAttributesTable[];
guideLine: BookGuideLineTable[];
incidents: BookIncidentsTable[];
issues: BookIssuesTable[];
locations: BookLocationTable[];
plotPoints: BookPlotPointsTable[];
worlds: BookWorldTable[];
worldElements: BookWorldElementsTable[];
locationElements: LocationElementTable[];
locationSubElements: LocationSubElementTable[];
}
export interface SyncedBook {
id: string;
type: string;
title: string;
subTitle: string | null;
lastUpdate: number;
chapters: SyncedChapter[];
characters: SyncedCharacter[];
locations: SyncedLocation[];
worlds: SyncedWorld[];
incidents: SyncedIncident[];
plotPoints: SyncedPlotPoint[];
issues: SyncedIssue[];
actSummaries: SyncedActSummary[];
guideLine: SyncedGuideLine | null;
aiGuideLine: SyncedAIGuideLine | null;
}
export interface BookSyncCompare {
id: string;
chapters: string[];
chapterContents: string[];
chapterInfos: string[];
characters: string[];
characterAttributes: string[];
locations: string[];
locationElements: string[];
locationSubElements: string[];
worlds: string[];
worldElements: string[];
incidents: string[];
plotPoints: string[];
issues: string[];
actSummaries: string[];
guideLine: boolean;
aiGuideLine: boolean;
}
export interface SyncedChapter {
id: string;
name: string;
lastUpdate: number;
contents: SyncedChapterContent[];
info: SyncedChapterInfo | null;
}
export interface SyncedChapterContent {
id: string;
lastUpdate: number;
}
export interface SyncedChapterInfo {
id: string;
lastUpdate: number;
}
export interface SyncedCharacter {
id: string;
name: string;
lastUpdate: number;
attributes: SyncedCharacterAttribute[];
}
export interface SyncedCharacterAttribute {
id: string;
name: string;
lastUpdate: number;
}
export interface SyncedLocation {
id: string;
name: string;
lastUpdate: number;
elements: SyncedLocationElement[];
}
export interface SyncedLocationElement {
id: string;
name: string;
lastUpdate: number;
subElements: SyncedLocationSubElement[];
}
export interface SyncedLocationSubElement {
id: string;
name: string;
lastUpdate: number;
}
export interface SyncedWorld {
id: string;
name: string;
lastUpdate: number;
elements: SyncedWorldElement[];
}
export interface SyncedWorldElement {
id: string;
name: string;
lastUpdate: number;
}
export interface SyncedIncident {
id: string;
name: string;
lastUpdate: number;
}
export interface SyncedPlotPoint {
id: string;
name: string;
lastUpdate: number;
}
export interface SyncedIssue {
id: string;
name: string;
lastUpdate: number;
}
export interface SyncedActSummary {
id: string;
lastUpdate: number;
}
export interface SyncedGuideLine {
lastUpdate: number;
}
export interface SyncedAIGuideLine {
lastUpdate: number;
}
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, lang: 'fr' | 'en'): 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', existingIncidentId?: string): string {
const userKey: string = getUserEncryptionKey(userId);
const encryptedName:string = System.encryptDataWithUserKey(name,userKey);
const hashedName:string = System.hashElement(name);
const incidentId: string = existingIncidentId || 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', existingPlotPointId?: string): string {
const userKey:string = getUserEncryptionKey(userId);
const encryptedName:string = System.encryptDataWithUserKey(name, userKey);
const hashedName:string = System.hashElement(name);
const plotPointId: string = existingPlotPointId || 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', existingIssueId?: string): string {
const userKey:string = getUserEncryptionKey(userId);
const encryptedName:string = System.encryptDataWithUserKey(name,userKey);
const hashedName:string = System.hashElement(name);
const issueId: string = existingIssueId || 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,System.timeStampInSeconds(),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, System.timeStampInSeconds(), 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, System.timeStampInSeconds(), 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, System.timeStampInSeconds(), lang);
}
return true;
}
public static addNewWorld(userId: string, bookId: string, worldName: string, lang: 'fr' | 'en' = 'fr', existingWorldId?: string): string {
const userKey: string = getUserEncryptionKey(userId);
const hashedName: string = System.hashElement(worldName);
if (!existingWorldId && 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 = existingWorldId || 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, System.timeStampInSeconds(), lang);
return BookRepo.updateWorldElements(userId, elements, lang);
}
public static addNewElementToWorld(userId: string, worldId: string, elementName: string, elementType: string, lang: 'fr' | 'en' = 'fr', existingElementId?: string): string {
const userKey: string = getUserEncryptionKey(userId);
const hashedName: string = System.hashElement(elementName);
if (!existingElementId && 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 = existingElementId || 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) {
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 = '';
}
}
static async getSyncedBooks(userId: string, lang: 'fr' | 'en'):Promise<SyncedBook[]> {
const userKey: string = getUserEncryptionKey(userId);
const [
allBooks,
allChapters,
allChapterContents,
allChapterInfos,
allCharacters,
allCharacterAttributes,
allLocations,
allLocationElements,
allLocationSubElements,
allWorlds,
allWorldElements,
allIncidents,
allPlotPoints,
allIssues,
allActSummaries,
allGuidelines,
allAIGuidelines
]: [
SyncedBookResult[],
SyncedChapterResult[],
SyncedChapterContentResult[],
SyncedChapterInfoResult[],
SyncedCharacterResult[],
SyncedCharacterAttributeResult[],
SyncedLocationResult[],
SyncedLocationElementResult[],
SyncedLocationSubElementResult[],
SyncedWorldResult[],
SyncedWorldElementResult[],
SyncedIncidentResult[],
SyncedPlotPointResult[],
SyncedIssueResult[],
SyncedActSummaryResult[],
SyncedGuideLineResult[],
SyncedAIGuideLineResult[]
] = await Promise.all([
BookRepo.fetchSyncedBooks(userId,lang),
BookRepo.fetchSyncedChapters(userId,lang),
BookRepo.fetchSyncedChapterContents(userId,lang),
BookRepo.fetchSyncedChapterInfos(userId,lang),
BookRepo.fetchSyncedCharacters(userId,lang),
BookRepo.fetchSyncedCharacterAttributes(userId,lang),
BookRepo.fetchSyncedLocations(userId,lang),
BookRepo.fetchSyncedLocationElements(userId,lang),
BookRepo.fetchSyncedLocationSubElements(userId,lang),
BookRepo.fetchSyncedWorlds(userId,lang),
BookRepo.fetchSyncedWorldElements(userId,lang),
BookRepo.fetchSyncedIncidents(userId,lang),
BookRepo.fetchSyncedPlotPoints(userId,lang),
BookRepo.fetchSyncedIssues(userId,lang),
BookRepo.fetchSyncedActSummaries(userId,lang),
BookRepo.fetchSyncedGuideLine(userId,lang),
BookRepo.fetchSyncedAIGuideLine(userId,lang)
]);
return allBooks.map((book: SyncedBookResult): SyncedBook => {
const bookId: string = book.book_id;
const chapters: SyncedChapter[] = allChapters
.filter((chapter: SyncedChapterResult): boolean => chapter.book_id === bookId)
.map((chapter: SyncedChapterResult): SyncedChapter => {
const chapterId: string = chapter.chapter_id;
const contents: SyncedChapterContent[] = allChapterContents
.filter((content: SyncedChapterContentResult): boolean => content.chapter_id === chapterId)
.map((content: SyncedChapterContentResult): SyncedChapterContent => ({
id: content.content_id,
lastUpdate: content.last_update
}));
const infoData: SyncedChapterInfoResult | undefined = allChapterInfos.find((info: SyncedChapterInfoResult): boolean => info.chapter_id === chapterId);
const info: SyncedChapterInfo | null = infoData ? {
id: infoData.chapter_info_id,
lastUpdate: infoData.last_update
} : null;
return {
id: chapterId,
name: System.decryptDataWithUserKey(chapter.title, userKey),
lastUpdate: chapter.last_update,
contents,
info
};
});
const characters: SyncedCharacter[] = allCharacters
.filter((character: SyncedCharacterResult): boolean => character.book_id === bookId)
.map((character: SyncedCharacterResult): SyncedCharacter => {
const characterId: string = character.character_id;
const attributes: SyncedCharacterAttribute[] = allCharacterAttributes
.filter((attribute: SyncedCharacterAttributeResult): boolean => attribute.character_id === characterId)
.map((attribute: SyncedCharacterAttributeResult): SyncedCharacterAttribute => ({
id: attribute.attr_id,
name: System.decryptDataWithUserKey(attribute.attribute_name, userKey),
lastUpdate: attribute.last_update
}));
return {
id: characterId,
name: System.decryptDataWithUserKey(character.first_name, userKey),
lastUpdate: character.last_update,
attributes
};
});
const locations: SyncedLocation[] = allLocations
.filter((location: SyncedLocationResult): boolean => location.book_id === bookId)
.map((location: SyncedLocationResult): SyncedLocation => {
const locationId: string = location.loc_id;
const elements: SyncedLocationElement[] = allLocationElements
.filter((element: SyncedLocationElementResult): boolean => element.location === locationId)
.map((element: SyncedLocationElementResult): SyncedLocationElement => {
const elementId: string = element.element_id;
const subElements: SyncedLocationSubElement[] = allLocationSubElements
.filter((subElement: SyncedLocationSubElementResult): boolean => subElement.element_id === elementId)
.map((subElement: SyncedLocationSubElementResult): SyncedLocationSubElement => ({
id: subElement.sub_element_id,
name: System.decryptDataWithUserKey(subElement.sub_elem_name, userKey),
lastUpdate: subElement.last_update
}));
return {
id: elementId,
name: System.decryptDataWithUserKey(element.element_name, userKey),
lastUpdate: element.last_update,
subElements
};
});
return {
id: locationId,
name: System.decryptDataWithUserKey(location.loc_name, userKey),
lastUpdate: location.last_update,
elements
};
});
const worlds: SyncedWorld[] = allWorlds
.filter((world: SyncedWorldResult): boolean => world.book_id === bookId)
.map((world: SyncedWorldResult): SyncedWorld => {
const worldId: string = world.world_id;
const elements: SyncedWorldElement[] = allWorldElements
.filter((worldElement: SyncedWorldElementResult): boolean => worldElement.world_id === worldId)
.map((worldElement: SyncedWorldElementResult): SyncedWorldElement => ({
id: worldElement.element_id,
name: System.decryptDataWithUserKey(worldElement.name, userKey),
lastUpdate: worldElement.last_update
}));
return {
id: worldId,
name: System.decryptDataWithUserKey(world.name, userKey),
lastUpdate: world.last_update,
elements
};
});
const incidents: SyncedIncident[] = allIncidents
.filter((incident: SyncedIncidentResult): boolean => incident.book_id === bookId)
.map((incident: SyncedIncidentResult): SyncedIncident => ({
id: incident.incident_id,
name: System.decryptDataWithUserKey(incident.title, userKey),
lastUpdate: incident.last_update
}));
const plotPoints: SyncedPlotPoint[] = allPlotPoints
.filter((plotPoint: SyncedPlotPointResult): boolean => plotPoint.book_id === bookId)
.map((plotPoint: SyncedPlotPointResult): SyncedPlotPoint => ({
id: plotPoint.plot_point_id,
name: System.decryptDataWithUserKey(plotPoint.title, userKey),
lastUpdate: plotPoint.last_update
}));
const issues: SyncedIssue[] = allIssues
.filter((issue: SyncedIssueResult): boolean => issue.book_id === bookId)
.map((issue: SyncedIssueResult): SyncedIssue => ({
id: issue.issue_id,
name: System.decryptDataWithUserKey(issue.name, userKey),
lastUpdate: issue.last_update
}));
const actSummaries: SyncedActSummary[] = allActSummaries
.filter((actSummary: SyncedActSummaryResult): boolean => actSummary.book_id === bookId)
.map((actSummary: SyncedActSummaryResult): SyncedActSummary => ({
id: actSummary.act_sum_id,
lastUpdate: actSummary.last_update
}));
const guidelineData: SyncedGuideLineResult | undefined = allGuidelines.find((guideline: SyncedGuideLineResult): boolean => guideline.book_id === bookId);
const guideLine: SyncedGuideLine | null = guidelineData ? {
lastUpdate: guidelineData.last_update
} : null;
const aiGuidelineData: SyncedAIGuideLineResult | undefined = allAIGuidelines.find((aiGuideline: SyncedAIGuideLineResult): boolean => aiGuideline.book_id === bookId);
const aiGuideLine: SyncedAIGuideLine | null = aiGuidelineData ? {
lastUpdate: aiGuidelineData.last_update
} : null;
return {
id: bookId,
type: book.type,
title: System.decryptDataWithUserKey(book.title, userKey),
subTitle: book.sub_title ? System.decryptDataWithUserKey(book.sub_title, userKey) : null,
lastUpdate: book.last_update,
chapters,
characters,
locations,
worlds,
incidents,
plotPoints,
issues,
actSummaries,
guideLine,
aiGuideLine
};
});
}
static async uploadBookForSync(userId:string,bookId: string,lang: "fr" | "en"): Promise<CompleteBook> {
const userKey: string = getUserEncryptionKey(userId);
const [
eritBooksRaw,
actSummariesRaw,
aiGuideLineRaw,
chaptersRaw,
charactersRaw,
guideLineRaw,
incidentsRaw,
issuesRaw,
locationsRaw,
plotPointsRaw,
worldsRaw
]: [
EritBooksTable[],
BookActSummariesTable[],
BookAIGuideLineTable[],
BookChaptersTable[],
BookCharactersTable[],
BookGuideLineTable[],
BookIncidentsTable[],
BookIssuesTable[],
BookLocationTable[],
BookPlotPointsTable[],
BookWorldTable[]
] = await Promise.all([
BookRepo.fetchEritBooksTable(userId, bookId,lang),
BookRepo.fetchBookActSummaries(userId, bookId,lang),
BookRepo.fetchBookAIGuideLine(userId, bookId,lang),
BookRepo.fetchBookChapters(userId, bookId,lang),
BookRepo.fetchBookCharacters(userId, bookId,lang),
BookRepo.fetchBookGuideLineTable(userId, bookId,lang),
BookRepo.fetchBookIncidents(userId, bookId,lang),
BookRepo.fetchBookIssues(userId, bookId,lang),
BookRepo.fetchBookLocations(userId, bookId,lang),
BookRepo.fetchBookPlotPoints(userId, bookId,lang),
BookRepo.fetchBookWorlds(userId, bookId,lang)
]);
const [
chapterContentsNested,
chapterInfosNested,
characterAttributesNested,
worldElementsNested,
locationElementsNested
]: [
BookChapterContentTable[][],
BookChapterInfosTable[][],
BookCharactersAttributesTable[][],
BookWorldElementsTable[][],
LocationElementTable[][]
] = await Promise.all([
Promise.all(chaptersRaw.map((chapter: BookChaptersTable): Promise<BookChapterContentTable[]> => BookRepo.fetchBookChapterContents(userId, chapter.chapter_id,lang))),
Promise.all(chaptersRaw.map((chapter: BookChaptersTable): Promise<BookChapterInfosTable[]> => BookRepo.fetchBookChapterInfos(userId, chapter.chapter_id,lang))),
Promise.all(charactersRaw.map((character: BookCharactersTable): Promise<BookCharactersAttributesTable[]> => BookRepo.fetchBookCharactersAttributes(userId, character.character_id,lang))),
Promise.all(worldsRaw.map((world: BookWorldTable): Promise<BookWorldElementsTable[]> => BookRepo.fetchBookWorldElements(userId, world.world_id,lang))),
Promise.all(locationsRaw.map((location: BookLocationTable): Promise<LocationElementTable[]> => BookRepo.fetchLocationElements(userId, location.loc_id,lang)))
]);
const chapterContentsRaw: BookChapterContentTable[] = chapterContentsNested.flat();
const chapterInfosRaw: BookChapterInfosTable[] = chapterInfosNested.flat();
const characterAttributesRaw: BookCharactersAttributesTable[] = characterAttributesNested.flat();
const worldElementsRaw: BookWorldElementsTable[] = worldElementsNested.flat();
const locationElementsRaw: LocationElementTable[] = locationElementsNested.flat();
const locationSubElementsNested: LocationSubElementTable[][] = await Promise.all(
locationElementsRaw.map((element: LocationElementTable): Promise<LocationSubElementTable[]> => BookRepo.fetchLocationSubElements(userId, element.element_id,lang))
);
const locationSubElementsRaw: LocationSubElementTable[] = locationSubElementsNested.flat();
const eritBooks: EritBooksTable[] = eritBooksRaw.map((book: EritBooksTable): EritBooksTable => ({
...book,
title: System.decryptDataWithUserKey(book.title, userKey),
sub_title: book.sub_title ? System.decryptDataWithUserKey(book.sub_title, userKey) : null,
summary: book.summary ? System.decryptDataWithUserKey(book.summary, userKey) : null,
cover_image: book.cover_image ? System.decryptDataWithUserKey(book.cover_image, userKey) : null
}));
const actSummaries: BookActSummariesTable[] = actSummariesRaw.map((actSummary: BookActSummariesTable): BookActSummariesTable => ({
...actSummary,
summary: actSummary.summary ? System.decryptDataWithUserKey(actSummary.summary, userKey) : null
}));
const aiGuideLine: BookAIGuideLineTable[] = aiGuideLineRaw.map((guideLine: BookAIGuideLineTable): BookAIGuideLineTable => ({
...guideLine,
global_resume: guideLine.global_resume ? System.decryptDataWithUserKey(guideLine.global_resume, userKey) : null,
themes: guideLine.themes ? System.decryptDataWithUserKey(guideLine.themes, userKey) : null,
tone: guideLine.tone ? System.decryptDataWithUserKey(guideLine.tone, userKey) : null,
atmosphere: guideLine.atmosphere ? System.decryptDataWithUserKey(guideLine.atmosphere, userKey) : null,
current_resume: guideLine.current_resume ? System.decryptDataWithUserKey(guideLine.current_resume, userKey) : null
}));
const chapters: BookChaptersTable[] = chaptersRaw.map((chapter: BookChaptersTable): BookChaptersTable => ({
...chapter,
title: System.decryptDataWithUserKey(chapter.title, userKey)
}));
const chapterContents: BookChapterContentTable[] = chapterContentsRaw.map((chapterContent: BookChapterContentTable): BookChapterContentTable => ({
...chapterContent,
content: chapterContent.content ? JSON.parse(System.decryptDataWithUserKey(chapterContent.content, userKey)) : null
}));
const chapterInfos: BookChapterInfosTable[] = chapterInfosRaw.map((chapterInfo: BookChapterInfosTable): BookChapterInfosTable => ({
...chapterInfo,
summary: chapterInfo.summary ? System.decryptDataWithUserKey(chapterInfo.summary, userKey) : null,
goal: chapterInfo.goal ? System.decryptDataWithUserKey(chapterInfo.goal, userKey) : null
}));
const characters: BookCharactersTable[] = charactersRaw.map((character: BookCharactersTable): BookCharactersTable => ({
...character,
first_name: System.decryptDataWithUserKey(character.first_name, userKey),
last_name: character.last_name ? System.decryptDataWithUserKey(character.last_name, userKey) : null,
category: System.decryptDataWithUserKey(character.category, userKey),
title: character.title ? System.decryptDataWithUserKey(character.title, userKey) : null,
role: character.role ? System.decryptDataWithUserKey(character.role, userKey) : null,
biography: character.biography ? System.decryptDataWithUserKey(character.biography, userKey) : null,
history: character.history ? System.decryptDataWithUserKey(character.history, userKey) : null
}));
const characterAttributes: BookCharactersAttributesTable[] = characterAttributesRaw.map((attribute: BookCharactersAttributesTable): BookCharactersAttributesTable => ({
...attribute,
attribute_name: System.decryptDataWithUserKey(attribute.attribute_name, userKey),
attribute_value: System.decryptDataWithUserKey(attribute.attribute_value, userKey)
}));
const guideLine: BookGuideLineTable[] = guideLineRaw.map((guide: BookGuideLineTable): BookGuideLineTable => ({
...guide,
tone: guide.tone ? System.decryptDataWithUserKey(guide.tone, userKey) : null,
atmosphere: guide.atmosphere ? System.decryptDataWithUserKey(guide.atmosphere, userKey) : null,
writing_style: guide.writing_style ? System.decryptDataWithUserKey(guide.writing_style, userKey) : null,
themes: guide.themes ? System.decryptDataWithUserKey(guide.themes, userKey) : null,
symbolism: guide.symbolism ? System.decryptDataWithUserKey(guide.symbolism, userKey) : null,
motifs: guide.motifs ? System.decryptDataWithUserKey(guide.motifs, userKey) : null,
narrative_voice: guide.narrative_voice ? System.decryptDataWithUserKey(guide.narrative_voice, userKey) : null,
pacing: guide.pacing ? System.decryptDataWithUserKey(guide.pacing, userKey) : null,
intended_audience: guide.intended_audience ? System.decryptDataWithUserKey(guide.intended_audience, userKey) : null,
key_messages: guide.key_messages ? System.decryptDataWithUserKey(guide.key_messages, userKey) : null
}));
const incidents: BookIncidentsTable[] = incidentsRaw.map((incident: BookIncidentsTable): BookIncidentsTable => ({
...incident,
title: System.decryptDataWithUserKey(incident.title, userKey),
summary: incident.summary ? System.decryptDataWithUserKey(incident.summary, userKey) : null
}));
const issues: BookIssuesTable[] = issuesRaw.map((issue: BookIssuesTable): BookIssuesTable => ({
...issue,
name: System.decryptDataWithUserKey(issue.name, userKey)
}));
const locations: BookLocationTable[] = locationsRaw.map((location: BookLocationTable): BookLocationTable => ({
...location,
loc_name: System.decryptDataWithUserKey(location.loc_name, userKey)
}));
const plotPoints: BookPlotPointsTable[] = plotPointsRaw.map((plotPoint: BookPlotPointsTable): BookPlotPointsTable => ({
...plotPoint,
title: System.decryptDataWithUserKey(plotPoint.title, userKey),
summary: plotPoint.summary ? System.decryptDataWithUserKey(plotPoint.summary, userKey) : null
}));
const worlds: BookWorldTable[] = worldsRaw.map((world: BookWorldTable): BookWorldTable => ({
...world,
name: System.decryptDataWithUserKey(world.name, userKey),
history: world.history ? System.decryptDataWithUserKey(world.history, userKey) : null,
politics: world.politics ? System.decryptDataWithUserKey(world.politics, userKey) : null,
economy: world.economy ? System.decryptDataWithUserKey(world.economy, userKey) : null,
religion: world.religion ? System.decryptDataWithUserKey(world.religion, userKey) : null,
languages: world.languages ? System.decryptDataWithUserKey(world.languages, userKey) : null
}));
const worldElements: BookWorldElementsTable[] = worldElementsRaw.map((worldElement: BookWorldElementsTable): BookWorldElementsTable => ({
...worldElement,
name: System.decryptDataWithUserKey(worldElement.name, userKey),
description: worldElement.description ? System.decryptDataWithUserKey(worldElement.description, userKey) : null
}));
const locationElements: LocationElementTable[] = locationElementsRaw.map((locationElement: LocationElementTable): LocationElementTable => ({
...locationElement,
element_name: System.decryptDataWithUserKey(locationElement.element_name, userKey),
element_description: locationElement.element_description ? System.decryptDataWithUserKey(locationElement.element_description, userKey) : null
}));
const locationSubElements: LocationSubElementTable[] = locationSubElementsRaw.map((locationSubElement: LocationSubElementTable): LocationSubElementTable => ({
...locationSubElement,
sub_elem_name: System.decryptDataWithUserKey(locationSubElement.sub_elem_name, userKey),
sub_elem_description: locationSubElement.sub_elem_description ? System.decryptDataWithUserKey(locationSubElement.sub_elem_description, userKey) : null
}));
return {
eritBooks,
actSummaries,
aiGuideLine,
chapters,
chapterContents,
chapterInfos,
characters,
characterAttributes,
guideLine,
incidents,
issues,
locations,
plotPoints,
worlds,
worldElements,
locationElements,
locationSubElements
};
}
static async saveCompleteBook(userId: string, data: CompleteBook, lang: "fr" | "en"):Promise<boolean> {
const userKey: string = getUserEncryptionKey(userId);
const book: EritBooksTable = data.eritBooks[0];
const encryptedBookTitle: string = System.encryptDataWithUserKey(book.title, userKey);
const encryptedBookSubTitle: string | null = book.sub_title ? System.encryptDataWithUserKey(book.sub_title, userKey) : null;
const encryptedBookSummary: string | null = book.summary ? System.encryptDataWithUserKey(book.summary, userKey) : null;
const encryptedBookCoverImage: string | null = book.cover_image ? System.encryptDataWithUserKey(book.cover_image, userKey) : null;
const bookInserted: boolean = BookRepo.insertSyncBook(
book.book_id,
userId,
book.type,
encryptedBookTitle,
book.hashed_title,
encryptedBookSubTitle,
book.hashed_sub_title,
encryptedBookSummary,
book.serie_id,
book.desired_release_date,
book.desired_word_count,
book.words_count,
encryptedBookCoverImage,
book.last_update,
lang
);
if (!bookInserted) return false;
const chaptersInserted: boolean = data.chapters.every((chapter: BookChaptersTable): boolean => {
const encryptedTitle: string = System.encryptDataWithUserKey(chapter.title, userKey);
return BookRepo.insertSyncChapter(chapter.chapter_id, chapter.book_id, userId, encryptedTitle, chapter.hashed_title, chapter.words_count, chapter.chapter_order, chapter.last_update, lang);
});
if (!chaptersInserted) return false;
const incidentsInserted: boolean = data.incidents.every((incident: BookIncidentsTable): boolean => {
const encryptedIncidentTitle: string = System.encryptDataWithUserKey(incident.title, userKey);
const encryptedIncidentSummary: string | null = incident.summary ? System.encryptDataWithUserKey(incident.summary, userKey) : null;
return BookRepo.insertSyncIncident(incident.incident_id, userId, incident.book_id, encryptedIncidentTitle, incident.hashed_title, encryptedIncidentSummary, incident.last_update, lang);
});
if (!incidentsInserted) return false;
const plotPointsInserted: boolean = data.plotPoints.every((plotPoint: BookPlotPointsTable): boolean => {
const encryptedPlotTitle: string = System.encryptDataWithUserKey(plotPoint.title, userKey);
const encryptedPlotSummary: string | null = plotPoint.summary ? System.encryptDataWithUserKey(plotPoint.summary, userKey) : null;
return BookRepo.insertSyncPlotPoint(plotPoint.plot_point_id, encryptedPlotTitle, plotPoint.hashed_title, encryptedPlotSummary, plotPoint.linked_incident_id, userId, plotPoint.book_id, plotPoint.last_update, lang);
});
if (!plotPointsInserted) return false;
const chapterContentsInserted: boolean = data.chapterContents.every((content: BookChapterContentTable): boolean => {
const encryptedContent: string | null = content.content ? System.encryptDataWithUserKey(JSON.stringify(content.content), userKey) : null;
return BookRepo.insertSyncChapterContent(content.content_id, content.chapter_id, userId, content.version, encryptedContent, content.words_count, content.time_on_it, content.last_update, lang);
});
if (!chapterContentsInserted) return false;
const chapterInfosInserted: boolean = data.chapterInfos.every((info: BookChapterInfosTable): boolean => {
const encryptedSummary: string | null = info.summary ? System.encryptDataWithUserKey(info.summary, userKey) : null;
const encryptedGoal: string | null = info.goal ? System.encryptDataWithUserKey(info.goal, userKey) : null;
return BookRepo.insertSyncChapterInfo(info.chapter_info_id, info.chapter_id, info.act_id, info.incident_id, info.plot_point_id, info.book_id, userId, encryptedSummary, encryptedGoal, info.last_update, lang);
});
if (!chapterInfosInserted) return false;
const charactersInserted: boolean = data.characters.every((character: BookCharactersTable): boolean => {
const encryptedFirstName: string = System.encryptDataWithUserKey(character.first_name, userKey);
const encryptedLastName: string | null = character.last_name ? System.encryptDataWithUserKey(character.last_name, userKey) : null;
const encryptedCategory: string = System.encryptDataWithUserKey(character.category, userKey);
const encryptedCharTitle: string | null = character.title ? System.encryptDataWithUserKey(character.title, userKey) : null;
const encryptedImage: string | null = character.image ? System.encryptDataWithUserKey(character.image, userKey) : null;
const encryptedRole: string | null = character.role ? System.encryptDataWithUserKey(character.role, userKey) : null;
const encryptedBiography: string | null = character.biography ? System.encryptDataWithUserKey(character.biography, userKey) : null;
const encryptedCharHistory: string | null = character.history ? System.encryptDataWithUserKey(character.history, userKey) : null;
return BookRepo.insertSyncCharacter(character.character_id, character.book_id, userId, encryptedFirstName, encryptedLastName, encryptedCategory, encryptedCharTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedCharHistory, character.last_update, lang);
});
if (!charactersInserted) return false;
const characterAttributesInserted: boolean = data.characterAttributes.every((attr: BookCharactersAttributesTable): boolean => {
const encryptedAttrName: string = System.encryptDataWithUserKey(attr.attribute_name, userKey);
const encryptedAttrValue: string = System.encryptDataWithUserKey(attr.attribute_value, userKey);
return BookRepo.insertSyncCharacterAttribute(attr.attr_id, attr.character_id, userId, encryptedAttrName, encryptedAttrValue, attr.last_update, lang);
});
if (!characterAttributesInserted) return false;
const locationsInserted: boolean = data.locations.every((location: BookLocationTable): boolean => {
const encryptedLocName: string = System.encryptDataWithUserKey(location.loc_name, userKey);
return BookRepo.insertSyncLocation(location.loc_id, location.book_id, userId, encryptedLocName, location.loc_original_name, location.last_update, lang);
});
if (!locationsInserted) return false;
const locationElementsInserted: boolean = data.locationElements.every((element: LocationElementTable): boolean => {
const encryptedLocElemName: string = System.encryptDataWithUserKey(element.element_name, userKey);
const encryptedLocElemDesc: string | null = element.element_description ? System.encryptDataWithUserKey(element.element_description, userKey) : null;
return BookRepo.insertSyncLocationElement(element.element_id, element.location, userId, encryptedLocElemName, element.original_name, encryptedLocElemDesc, element.last_update, lang);
});
if (!locationElementsInserted) return false;
const locationSubElementsInserted: boolean = data.locationSubElements.every((subElement: LocationSubElementTable): boolean => {
const encryptedSubElemName: string = System.encryptDataWithUserKey(subElement.sub_elem_name, userKey);
const encryptedSubElemDesc: string | null = subElement.sub_elem_description ? System.encryptDataWithUserKey(subElement.sub_elem_description, userKey) : null;
return BookRepo.insertSyncLocationSubElement(subElement.sub_element_id, subElement.element_id, userId, encryptedSubElemName, subElement.original_name, encryptedSubElemDesc, subElement.last_update, lang);
});
if (!locationSubElementsInserted) return false;
const worldsInserted: boolean = data.worlds.every((world: BookWorldTable): boolean => {
const encryptedWorldName: string = System.encryptDataWithUserKey(world.name, userKey);
const encryptedWorldHistory: string | null = world.history ? System.encryptDataWithUserKey(world.history, userKey) : null;
const encryptedPolitics: string | null = world.politics ? System.encryptDataWithUserKey(world.politics, userKey) : null;
const encryptedEconomy: string | null = world.economy ? System.encryptDataWithUserKey(world.economy, userKey) : null;
const encryptedReligion: string | null = world.religion ? System.encryptDataWithUserKey(world.religion, userKey) : null;
const encryptedLanguages: string | null = world.languages ? System.encryptDataWithUserKey(world.languages, userKey) : null;
return BookRepo.insertSyncWorld(world.world_id, encryptedWorldName, world.hashed_name, userId, world.book_id, encryptedWorldHistory, encryptedPolitics, encryptedEconomy, encryptedReligion, encryptedLanguages, world.last_update, lang);
});
if (!worldsInserted) return false;
const worldElementsInserted: boolean = data.worldElements.every((element: BookWorldElementsTable): boolean => {
const encryptedElemName: string = System.encryptDataWithUserKey(element.name, userKey);
const encryptedElemDesc: string | null = element.description ? System.encryptDataWithUserKey(element.description, userKey) : null;
return BookRepo.insertSyncWorldElement(element.element_id, element.world_id, userId, element.element_type, encryptedElemName, element.original_name, encryptedElemDesc, element.last_update, lang);
});
if (!worldElementsInserted) return false;
const actSummariesInserted: boolean = data.actSummaries.every((actSummary: BookActSummariesTable): boolean => {
const encryptedSummary: string | null = actSummary.summary ? System.encryptDataWithUserKey(actSummary.summary, userKey) : null;
return BookRepo.insertSyncActSummary(actSummary.act_sum_id, actSummary.book_id, userId, actSummary.act_index, encryptedSummary, actSummary.last_update, lang);
});
if (!actSummariesInserted) return false;
const aiGuidelinesInserted: boolean = data.aiGuideLine.every((aiGuide: BookAIGuideLineTable): boolean => {
const encryptedGlobalResume: string | null = aiGuide.global_resume ? System.encryptDataWithUserKey(aiGuide.global_resume, userKey) : null;
const encryptedAIThemes: string | null = aiGuide.themes ? System.encryptDataWithUserKey(aiGuide.themes, userKey) : null;
const encryptedAITone: string | null = aiGuide.tone ? System.encryptDataWithUserKey(aiGuide.tone, userKey) : null;
const encryptedAIAtmosphere: string | null = aiGuide.atmosphere ? System.encryptDataWithUserKey(aiGuide.atmosphere, userKey) : null;
const encryptedCurrentResume: string | null = aiGuide.current_resume ? System.encryptDataWithUserKey(aiGuide.current_resume, userKey) : null;
return BookRepo.insertSyncAIGuideLine(userId, aiGuide.book_id, encryptedGlobalResume, encryptedAIThemes, aiGuide.verbe_tense, aiGuide.narrative_type, aiGuide.langue, aiGuide.dialogue_type, encryptedAITone, encryptedAIAtmosphere, encryptedCurrentResume, aiGuide.last_update, lang);
});
if (!aiGuidelinesInserted) return false;
const guidelinesInserted: boolean = data.guideLine.every((guide: BookGuideLineTable): boolean => {
const encryptedGuideTone: string | null = guide.tone ? System.encryptDataWithUserKey(guide.tone, userKey) : null;
const encryptedGuideAtmosphere: string | null = guide.atmosphere ? System.encryptDataWithUserKey(guide.atmosphere, userKey) : null;
const encryptedWritingStyle: string | null = guide.writing_style ? System.encryptDataWithUserKey(guide.writing_style, userKey) : null;
const encryptedGuideThemes: string | null = guide.themes ? System.encryptDataWithUserKey(guide.themes, userKey) : null;
const encryptedSymbolism: string | null = guide.symbolism ? System.encryptDataWithUserKey(guide.symbolism, userKey) : null;
const encryptedMotifs: string | null = guide.motifs ? System.encryptDataWithUserKey(guide.motifs, userKey) : null;
const encryptedNarrativeVoice: string | null = guide.narrative_voice ? System.encryptDataWithUserKey(guide.narrative_voice, userKey) : null;
const encryptedPacing: string | null = guide.pacing ? System.encryptDataWithUserKey(guide.pacing, userKey) : null;
const encryptedIntendedAudience: string | null = guide.intended_audience ? System.encryptDataWithUserKey(guide.intended_audience, userKey) : null;
const encryptedKeyMessages: string | null = guide.key_messages ? System.encryptDataWithUserKey(guide.key_messages, userKey) : null;
return BookRepo.insertSyncGuideLine(userId, guide.book_id, encryptedGuideTone, encryptedGuideAtmosphere, encryptedWritingStyle, encryptedGuideThemes, encryptedSymbolism, encryptedMotifs, encryptedNarrativeVoice, encryptedPacing, encryptedIntendedAudience, encryptedKeyMessages, guide.last_update, lang);
});
if (!guidelinesInserted) return false;
return data.issues.every((issue: BookIssuesTable): boolean => {
const encryptedIssueName: string = System.encryptDataWithUserKey(issue.name, userKey);
return BookRepo.insertSyncIssue(issue.issue_id, userId, issue.book_id, encryptedIssueName, issue.hashed_issue_name, issue.last_update, lang);
});
}
static async getCompleteSyncBook(userId: string, data: BookSyncCompare, lang: "fr" | "en"):Promise<CompleteBook> {
const userKey: string = getUserEncryptionKey(userId);
const bookData: EritBooksTable[] = [];
const chaptersData: BookChaptersTable[] = [];
const plotPointsData: BookPlotPointsTable[] = [];
const incidentsData: BookIncidentsTable[] = [];
const chapterContentsData: BookChapterContentTable[] = [];
const chapterInfosData: BookChapterInfosTable[] = [];
const charactersData: BookCharactersTable[] = [];
const characterAttributesData: BookCharactersAttributesTable[] = [];
const locationsData: BookLocationTable[] = [];
const locationElementsData: LocationElementTable[] = [];
const locationSubElementsData: LocationSubElementTable[] = [];
const worldsData: BookWorldTable[] = [];
const worldElementsData: BookWorldElementsTable[] = [];
const actSummariesData: BookActSummariesTable[] = [];
const guideLineData: BookGuideLineTable[] = [];
const aiGuideLineData: BookAIGuideLineTable[] = [];
const issuesData: BookIssuesTable[] = [];
const actSummaries: string[] = data.actSummaries;
const chapters: string[] = data.chapters;
const plotPoints: string[] = data.plotPoints;
const incidents: string[] = data.incidents;
const chapterContents: string[] = data.chapterContents;
const chapterInfos: string[] = data.chapterInfos;
const characters: string[] = data.characters;
const characterAttributes: string[] = data.characterAttributes;
const locations: string[] = data.locations;
const locationElements: string[] = data.locationElements;
const locationSubElements: string[] = data.locationSubElements;
const worlds: string[] = data.worlds;
const worldElements: string[] = data.worldElements;
const issues: string[] = data.issues;
if (actSummaries && actSummaries.length > 0) {
for (const id of actSummaries) {
const actSummary: BookActSummariesTable[] = await BookRepo.fetchCompleteActSummaryById(id, lang);
if (actSummary.length>0) {
const actSummaryData: BookActSummariesTable = actSummary[0];
actSummariesData.push({
...actSummaryData,
summary: actSummaryData.summary ? System.decryptDataWithUserKey(actSummaryData.summary, userKey) : null
});
}
}
}
if (chapters && chapters.length > 0) {
for (const id of chapters) {
const chapter: BookChaptersTable[] = await BookRepo.fetchCompleteChapterById(id, lang);
if (chapter.length>0) {
const chapterData: BookChaptersTable = chapter[0];
chaptersData.push({
...chapterData,
title: System.decryptDataWithUserKey(chapterData.title, userKey)
});
}
}
}
if (plotPoints && plotPoints.length > 0) {
for (const id of plotPoints) {
const plotPoint: BookPlotPointsTable[] = await BookRepo.fetchCompletePlotPointById(id, lang);
if (plotPoint.length>0) {
const plotPointData: BookPlotPointsTable = plotPoint[0];
plotPointsData.push({
...plotPointData,
title: System.decryptDataWithUserKey(plotPointData.title, userKey),
summary: plotPointData.summary ? System.decryptDataWithUserKey(plotPointData.summary, userKey) : null
});
}
}
}
if (incidents && incidents.length > 0) {
for (const id of incidents) {
const incident: BookIncidentsTable[] = await BookRepo.fetchCompleteIncidentById(id, lang);
if (incident.length>0) {
const incidentData: BookIncidentsTable = incident[0];
incidentsData.push({
...incidentData,
title: System.decryptDataWithUserKey(incidentData.title, userKey),
summary: incidentData.summary ? System.decryptDataWithUserKey(incidentData.summary, userKey) : null
});
}
}
}
if (chapterContents && chapterContents.length > 0) {
for (const id of chapterContents) {
const chapterContent: BookChapterContentTable[] = await BookRepo.fetchCompleteChapterContentById(id, lang);
if (chapterContent.length>0) {
const chapterContentData: BookChapterContentTable = chapterContent[0];
chapterContentsData.push({
...chapterContentData,
content: chapterContentData.content ? JSON.parse(System.decryptDataWithUserKey(chapterContentData.content, userKey)) : null
});
}
}
}
if (chapterInfos && chapterInfos.length > 0) {
for (const id of chapterInfos) {
const chapterInfo: BookChapterInfosTable[] = await BookRepo.fetchCompleteChapterInfoById(id, lang);
if (chapterInfo.length>0) {
const chapterInfoData: BookChapterInfosTable = chapterInfo[0];
chapterInfosData.push({
...chapterInfoData,
summary: chapterInfoData.summary ? System.decryptDataWithUserKey(chapterInfoData.summary, userKey) : null,
goal: chapterInfoData.goal ? System.decryptDataWithUserKey(chapterInfoData.goal, userKey) : null
});
}
}
}
if (characters && characters.length > 0) {
for (const id of characters) {
const character: BookCharactersTable[] = await BookRepo.fetchCompleteCharacterById(id, lang);
if (character.length>0) {
const characterData: BookCharactersTable = character[0];
charactersData.push({
...characterData,
first_name: System.decryptDataWithUserKey(characterData.first_name, userKey),
last_name: characterData.last_name ? System.decryptDataWithUserKey(characterData.last_name, userKey) : null,
category: System.decryptDataWithUserKey(characterData.category, userKey),
title: characterData.title ? System.decryptDataWithUserKey(characterData.title, userKey) : null,
role: characterData.role ? System.decryptDataWithUserKey(characterData.role, userKey) : null,
biography: characterData.biography ? System.decryptDataWithUserKey(characterData.biography, userKey) : null,
history: characterData.history ? System.decryptDataWithUserKey(characterData.history, userKey) : null
});
}
}
}
if (characterAttributes && characterAttributes.length > 0) {
for (const id of characterAttributes) {
const characterAttribute: BookCharactersAttributesTable[] = await BookRepo.fetchCompleteCharacterAttributeById(id, lang);
if (characterAttribute.length>0) {
const characterAttributeData: BookCharactersAttributesTable = characterAttribute[0];
characterAttributesData.push({
...characterAttributeData,
attribute_name: System.decryptDataWithUserKey(characterAttributeData.attribute_name, userKey),
attribute_value: System.decryptDataWithUserKey(characterAttributeData.attribute_value, userKey)
});
}
}
}
if (locations && locations.length > 0) {
for (const id of locations) {
const location: BookLocationTable[] = await BookRepo.fetchCompleteLocationById(id, lang);
if (location.length>0) {
const locationData: BookLocationTable = location[0];
locationsData.push({
...locationData,
loc_name: System.decryptDataWithUserKey(locationData.loc_name, userKey)
});
}
}
}
if (locationElements && locationElements.length > 0) {
for (const id of locationElements) {
const locationElement: LocationElementTable[] = await BookRepo.fetchCompleteLocationElementById(id, lang);
if (locationElement.length>0) {
const locationElementData: LocationElementTable = locationElement[0];
locationElementsData.push({
...locationElementData,
element_name: System.decryptDataWithUserKey(locationElementData.element_name, userKey),
element_description: locationElementData.element_description ? System.decryptDataWithUserKey(locationElementData.element_description, userKey) : null
});
}
}
}
if (locationSubElements && locationSubElements.length > 0) {
for (const id of locationSubElements) {
const locationSubElement: LocationSubElementTable[] = await BookRepo.fetchCompleteLocationSubElementById(id, lang);
if (locationSubElement.length>0) {
const locationSubElementData: LocationSubElementTable = locationSubElement[0];
locationSubElementsData.push({
...locationSubElementData,
sub_elem_name: System.decryptDataWithUserKey(locationSubElementData.sub_elem_name, userKey),
sub_elem_description: locationSubElementData.sub_elem_description ? System.decryptDataWithUserKey(locationSubElementData.sub_elem_description, userKey) : null
});
}
}
}
if (worlds && worlds.length > 0) {
for (const id of worlds) {
const world: BookWorldTable[] = await BookRepo.fetchCompleteWorldById(id, lang);
if (world.length>0) {
const worldData: BookWorldTable = world[0];
worldsData.push({
...worldData,
name: System.decryptDataWithUserKey(worldData.name, userKey),
history: worldData.history ? System.decryptDataWithUserKey(worldData.history, userKey) : null,
politics: worldData.politics ? System.decryptDataWithUserKey(worldData.politics, userKey) : null,
economy: worldData.economy ? System.decryptDataWithUserKey(worldData.economy, userKey) : null,
religion: worldData.religion ? System.decryptDataWithUserKey(worldData.religion, userKey) : null,
languages: worldData.languages ? System.decryptDataWithUserKey(worldData.languages, userKey) : null
});
}
}
}
if (worldElements && worldElements.length > 0) {
for (const id of worldElements) {
const worldElement: BookWorldElementsTable[] = await BookRepo.fetchCompleteWorldElementById(id, lang);
if (worldElement.length>0) {
const worldElementData: BookWorldElementsTable = worldElement[0];
worldElementsData.push({
...worldElementData,
name: System.decryptDataWithUserKey(worldElementData.name, userKey),
description: worldElementData.description ? System.decryptDataWithUserKey(worldElementData.description, userKey) : null
});
}
}
}
if (issues && issues.length > 0) {
for (const id of issues) {
const issue: BookIssuesTable[] = await BookRepo.fetchCompleteIssueById(id, lang);
if (issue.length>0) {
const issueData: BookIssuesTable = issue[0];
issuesData.push({
...issueData,
name: System.decryptDataWithUserKey(issueData.name, userKey)
});
}
}
}
const book: EritBooksTable[] = await BookRepo.fetchCompleteBookById(data.id, lang);
if (book.length>0) {
const bookDataItem: EritBooksTable = book[0];
bookData.push({
...bookDataItem,
title: System.decryptDataWithUserKey(bookDataItem.title, userKey),
sub_title: bookDataItem.sub_title ? System.decryptDataWithUserKey(bookDataItem.sub_title, userKey) : null,
summary: bookDataItem.summary ? System.decryptDataWithUserKey(bookDataItem.summary, userKey) : null,
cover_image: bookDataItem.cover_image ? System.decryptDataWithUserKey(bookDataItem.cover_image, userKey) : null
});
}
return {
eritBooks: bookData,
chapters: chaptersData,
plotPoints: plotPointsData,
incidents: incidentsData,
chapterContents: chapterContentsData,
chapterInfos: chapterInfosData,
characters: charactersData,
characterAttributes: characterAttributesData,
locations: locationsData,
locationElements: locationElementsData,
locationSubElements: locationSubElementsData,
worlds: worldsData,
worldElements: worldElementsData,
actSummaries: actSummariesData,
guideLine: guideLineData,
aiGuideLine: aiGuideLineData,
issues: issuesData
};
}
static async syncBookFromServerToClient(userId:string,completeBook: CompleteBook,lang:"fr"|"en"):Promise<boolean> {
const userKey: string = getUserEncryptionKey(userId);
const actSummaries: BookActSummariesTable[] = completeBook.actSummaries;
const chapters: BookChaptersTable[] = completeBook.chapters;
const plotPoints: BookPlotPointsTable[] = completeBook.plotPoints;
const incidents: BookIncidentsTable[] = completeBook.incidents;
const chapterContents: BookChapterContentTable[] = completeBook.chapterContents;
const chapterInfos: BookChapterInfosTable[] = completeBook.chapterInfos;
const characters: BookCharactersTable[] = completeBook.characters;
const characterAttributes: BookCharactersAttributesTable[] = completeBook.characterAttributes;
const locations: BookLocationTable[] = completeBook.locations;
const locationElements: LocationElementTable[] = completeBook.locationElements;
const locationSubElements: LocationSubElementTable[] = completeBook.locationSubElements;
const worlds: BookWorldTable[] = completeBook.worlds;
const worldElements: BookWorldElementsTable[] = completeBook.worldElements;
const issues: BookIssuesTable[] = completeBook.issues;
const bookId: string = completeBook.eritBooks.length > 0 ? completeBook.eritBooks[0].book_id : '';
if (chapters && chapters.length > 0) {
for (const chapter of chapters) {
const isExist: boolean = ChapterRepo.isChapterExist(userId, chapter.chapter_id,lang);
const title: string = System.encryptDataWithUserKey(chapter.title, userKey)
if (isExist) {
const updated: boolean = ChapterRepo.updateChapter(userId, chapter.chapter_id, title, chapter.hashed_title, chapter.chapter_order, chapter.last_update, lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncChapter(chapter.chapter_id, chapter.book_id, userId, title, chapter.hashed_title, chapter.words_count || 0, chapter.chapter_order, chapter.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (actSummaries && actSummaries.length > 0) {
for (const act of actSummaries) {
const isExist: boolean = BookRepo.actSummarizeExist(userId, bookId, act.act_index,lang);
const summary: string = System.encryptDataWithUserKey(act.summary ? act.summary : '', userKey)
if (isExist) {
const updated: boolean = BookRepo.updateActSummary(userId, bookId, act.act_index, summary, act.last_update,lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncActSummary(act.act_sum_id, userId, bookId, act.act_index, summary, act.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (plotPoints && plotPoints.length > 0) {
for (const plotPoint of plotPoints) {
const title: string = System.encryptDataWithUserKey(plotPoint.title, userKey);
const summary: string = System.encryptDataWithUserKey(plotPoint.summary ? plotPoint.summary : '', userKey);
const ifExist: boolean = BookRepo.plotPointExist(userId, bookId, plotPoint.plot_point_id,lang);
if (ifExist) {
const updated: boolean = BookRepo.updatePlotPoint(userId, bookId, plotPoint.plot_point_id, title, plotPoint.hashed_title, summary, plotPoint.last_update,lang);
if (!updated) {
return false;
}
} else {
if (!plotPoint.linked_incident_id) {
return false;
}
const created: boolean = BookRepo.insertSyncPlotPoint(plotPoint.plot_point_id, title, plotPoint.hashed_title, summary, plotPoint.linked_incident_id, plotPoint.author_id, bookId, plotPoint.last_update,lang);
if (!created) {
return false;
}
}
}
}
if (incidents && incidents.length > 0) {
for (const incident of incidents) {
const title: string = System.encryptDataWithUserKey(incident.title, userKey);
const summary: string = System.encryptDataWithUserKey(incident.summary ? incident.summary : '', userKey);
const isExist: boolean = BookRepo.incidentExist(userId, bookId, incident.incident_id,lang);
if (isExist) {
const updated: boolean = BookRepo.updateIncident(userId, bookId, incident.incident_id, title, incident.hashed_title, summary, incident.last_update,lang);
if (!updated) {
return false;
}
} else {
const created: boolean = BookRepo.insertSyncIncident(incident.incident_id, userId, bookId, title, incident.hashed_title, summary, incident.last_update,lang);
if (!created) {
return false;
}
}
}
}
if (chapterContents && chapterContents.length > 0) {
for (const chapterContent of chapterContents) {
const isExist: boolean = ChapterRepo.isChapterContentExist(userId, chapterContent.content_id, lang);
const content: string = System.encryptDataWithUserKey(chapterContent.content ? JSON.stringify(chapterContent.content) : '', userKey);
if (isExist) {
const updated: boolean = ChapterRepo.updateChapterContent(userId, chapterContent.chapter_id, chapterContent.version, content, chapterContent.words_count, chapterContent.last_update);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncChapterContent(chapterContent.content_id, chapterContent.chapter_id, userId, chapterContent.version, content, chapterContent.words_count, chapterContent.time_on_it, chapterContent.last_update, lang);
if (!insert) {
return false;
}
}
}
}
if (chapterInfos && chapterInfos.length > 0) {
for (const chapterInfo of chapterInfos) {
const isExist: boolean = ChapterRepo.isChapterInfoExist(userId, chapterInfo.chapter_id,lang);
const summary: string = System.encryptDataWithUserKey(chapterInfo.summary ? chapterInfo.summary : '', userKey);
const goal: string = System.encryptDataWithUserKey(chapterInfo.goal ? chapterInfo.goal : '', userKey);
if (isExist) {
const updated: boolean = ChapterRepo.updateChapterInfos(userId, chapterInfo.chapter_id, chapterInfo.act_id, bookId, chapterInfo.incident_id, chapterInfo.plot_point_id, summary, goal, chapterInfo.last_update,lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncChapterInfo(chapterInfo.chapter_info_id, chapterInfo.chapter_id, chapterInfo.act_id, chapterInfo.incident_id, chapterInfo.plot_point_id, bookId, chapterInfo.author_id, summary, goal, chapterInfo.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (characters && characters.length > 0) {
for (const character of characters) {
const isExist: boolean = CharacterRepo.isCharacterExist(userId, character.character_id,lang);
const firstName: string = System.encryptDataWithUserKey(character.first_name, userKey);
const lastName: string = System.encryptDataWithUserKey(character.last_name ? character.last_name : '', userKey);
const category: string = System.encryptDataWithUserKey(character.category, userKey);
const title: string = System.encryptDataWithUserKey(character.title ? character.title : '', userKey);
const role: string = System.encryptDataWithUserKey(character.role ? character.role : '', userKey);
const image: string = System.encryptDataWithUserKey(character.image ? character.image : '', userKey);
const biography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey);
const history: string = System.encryptDataWithUserKey(character.history ? character.history : '', userKey);
if (isExist) {
const updated: boolean = CharacterRepo.updateCharacter(userId, character.character_id, firstName, lastName, title, category, image, role, biography, history, character.last_update);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncCharacter(character.character_id, bookId, userId, firstName, lastName, category, title, image, role, biography, history, character.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (characterAttributes && characterAttributes.length > 0) {
for (const characterAttribute of characterAttributes) {
const isExist: boolean = CharacterRepo.isCharacterAttributeExist(userId, characterAttribute.attr_id,lang);
const attributeName: string = System.encryptDataWithUserKey(characterAttribute.attribute_name, userKey);
const attributeValue: string = System.encryptDataWithUserKey(characterAttribute.attribute_value, userKey);
if (isExist) {
const updated: boolean = CharacterRepo.updateCharacterAttribute(userId, characterAttribute.attr_id, attributeName, attributeValue, characterAttribute.last_update,lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncCharacterAttribute(characterAttribute.attr_id, characterAttribute.character_id, userId, attributeName, attributeValue, characterAttribute.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (locations && locations.length > 0) {
for (const location of locations) {
const isExist: boolean = LocationRepo.isLocationExist(userId, location.loc_id,lang);
const locName: string = System.encryptDataWithUserKey(location.loc_name, userKey);
if (isExist) {
const updated: boolean = LocationRepo.updateLocationSection(userId, location.loc_id, locName, location.loc_original_name, location.last_update,lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncLocation(location.loc_id, bookId, userId, locName, location.loc_original_name, location.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (locationElements && locationElements.length > 0) {
for (const locationElement of locationElements) {
const isExist: boolean = LocationRepo.isLocationElementExist(userId, locationElement.element_id,lang);
const elementName: string = System.encryptDataWithUserKey(locationElement.element_name, userKey);
const elementDescription: string = System.encryptDataWithUserKey(locationElement.element_description ? locationElement.element_description : '', userKey);
if (isExist) {
const updated: boolean = LocationRepo.updateLocationElement(userId, locationElement.element_id, elementName, locationElement.original_name, elementDescription, locationElement.last_update,lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncLocationElement(locationElement.element_id, locationElement.location, userId, elementName, locationElement.original_name, elementDescription, locationElement.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (locationSubElements && locationSubElements.length > 0) {
for (const locationSubElement of locationSubElements) {
const isExist: boolean = LocationRepo.isLocationSubElementExist(userId, locationSubElement.sub_element_id,lang);
const subElemName: string = System.encryptDataWithUserKey(locationSubElement.sub_elem_name, userKey);
const subElemDescription: string = System.encryptDataWithUserKey(locationSubElement.sub_elem_description ? locationSubElement.sub_elem_description : '', userKey);
if (isExist) {
const updated: boolean = LocationRepo.updateLocationSubElement(userId, locationSubElement.sub_element_id, subElemName, locationSubElement.original_name, subElemDescription, locationSubElement.last_update,lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncLocationSubElement(locationSubElement.sub_element_id, locationSubElement.element_id, userId, subElemName, locationSubElement.original_name, subElemDescription, locationSubElement.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (worlds && worlds.length > 0) {
for (const world of worlds) {
const isExist: boolean = BookRepo.worldExist(userId, bookId, world.world_id,lang);
const name: string = System.encryptDataWithUserKey(world.name, userKey);
const history: string = System.encryptDataWithUserKey(world.history ? world.history : '', userKey);
const politics: string = System.encryptDataWithUserKey(world.politics ? world.politics : '', userKey);
const economy: string = System.encryptDataWithUserKey(world.economy ? world.economy : '', userKey);
const religion: string = System.encryptDataWithUserKey(world.religion ? world.religion : '', userKey);
const languages: string = System.encryptDataWithUserKey(world.languages ? world.languages : '', userKey);
if (isExist) {
const updated: boolean = BookRepo.updateWorld(userId, world.world_id, name, world.hashed_name, history, politics, economy, religion, languages, world.last_update,lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncWorld(world.world_id, name, world.hashed_name, userId, bookId, history, politics, economy, religion, languages, world.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (worldElements && worldElements.length > 0) {
for (const worldElement of worldElements) {
const isExist: boolean = BookRepo.worldElementExist(userId, worldElement.world_id, worldElement.element_id,lang);
const name: string = System.encryptDataWithUserKey(worldElement.name, userKey);
const description: string = System.encryptDataWithUserKey(worldElement.description ? worldElement.description : '', userKey);
if (isExist) {
const updated: boolean = BookRepo.updateWorldElement(userId, worldElement.element_id, name, description, worldElement.last_update,lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncWorldElement(worldElement.element_id, worldElement.world_id, userId, worldElement.element_type, name, worldElement.original_name, description, worldElement.last_update,lang);
if (!insert) {
return false;
}
}
}
}
if (issues && issues.length > 0) {
for (const issue of issues) {
const isExist: boolean = BookRepo.issueExist(userId, bookId, issue.issue_id,lang);
const name: string = System.encryptDataWithUserKey(issue.name, userKey);
if (isExist) {
const updated: boolean = BookRepo.updateIssue(userId, bookId, issue.issue_id, name, issue.hashed_issue_name, issue.last_update, lang);
if (!updated) {
return false;
}
} else {
const insert: boolean = BookRepo.insertSyncIssue(issue.issue_id, userId, bookId, name, issue.hashed_issue_name, issue.last_update, lang);
if (!insert) {
return false;
}
}
}
}
return true;
}
}