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