- Introduced `lastUpdate` field in `book_tools` for tracking changes. - Refactored tool enablement logic in `CharacterComponent`, `WorldSetting`, and `LocationComponent` for consistency. - Updated database schema and migration scripts for `book_tools` table. - Enhanced synchronization workflows to support new `lastUpdate` logic. - Adjusted related models, repositories, and IPC handlers for streamlined management. - Improved type safety and robustness in tool-related methods with additional checks.
206 lines
18 KiB
TypeScript
206 lines
18 KiB
TypeScript
import {getUserEncryptionKey} from "../keyManager.js";
|
|
import System from "../System.js";
|
|
import {CompleteBook} from "./Book.js";
|
|
import BookRepo, {EritBooksTable, BookToolsTable} from "../repositories/book.repository.js";
|
|
import ChapterRepo, {
|
|
BookChapterInfosTable,
|
|
BookChaptersTable
|
|
} from "../repositories/chapter.repository.js";
|
|
import IncidentRepository, {BookIncidentsTable} from "../repositories/incident.repository.js";
|
|
import PlotPointRepository, {BookPlotPointsTable} from "../repositories/plotpoint.repository.js";
|
|
import ChapterContentRepository, {BookChapterContentTable} from "../repositories/chaptercontent.repository.js";
|
|
import CharacterRepo, {
|
|
BookCharactersAttributesTable,
|
|
BookCharactersTable
|
|
} from "../repositories/character.repository.js";
|
|
import LocationRepo, {
|
|
BookLocationTable,
|
|
LocationElementTable,
|
|
LocationSubElementTable
|
|
} from "../repositories/location.repository.js";
|
|
import WorldRepository, {
|
|
BookWorldElementsTable,
|
|
BookWorldTable
|
|
} from "../repositories/world.repository.js";
|
|
import ActRepository, {BookActSummariesTable} from "../repositories/act.repository.js";
|
|
import GuidelineRepo, {
|
|
BookAIGuideLineTable,
|
|
BookGuideLineTable
|
|
} from "../repositories/guideline.repository.js";
|
|
import IssueRepository, {BookIssuesTable} from "../repositories/issue.repository.js";
|
|
|
|
export default class Download {
|
|
/**
|
|
* Saves a complete book with all its associated data to the local database.
|
|
* This method encrypts all sensitive data using the user's encryption key before storing.
|
|
* It processes and inserts all book components including chapters, incidents, plot points,
|
|
* chapter contents, chapter infos, characters, character attributes, locations, location elements,
|
|
* location sub-elements, worlds, world elements, act summaries, AI guidelines, guidelines, and issues.
|
|
*
|
|
* @param userId - The unique identifier of the user who owns the book
|
|
* @param data - The complete book data structure containing all book components to save
|
|
* @param lang - The language code for localization ("fr" for French or "en" for English)
|
|
* @returns A promise that resolves to true if all data was saved successfully, false otherwise
|
|
*/
|
|
static async saveCompleteBook(userId: string, data: CompleteBook, lang: "fr" | "en"): Promise<boolean> {
|
|
const userEncryptionKey: string = getUserEncryptionKey(userId);
|
|
|
|
const bookData: EritBooksTable = data.eritBooks[0];
|
|
const encryptedBookTitle: string = System.encryptDataWithUserKey(bookData.title, userEncryptionKey);
|
|
const encryptedBookSubTitle: string | null = bookData.sub_title ? System.encryptDataWithUserKey(bookData.sub_title, userEncryptionKey) : null;
|
|
const encryptedBookSummary: string | null = bookData.summary ? System.encryptDataWithUserKey(bookData.summary, userEncryptionKey) : null;
|
|
const encryptedBookCoverImage: string | null = bookData.cover_image ? System.encryptDataWithUserKey(bookData.cover_image, userEncryptionKey) : null;
|
|
|
|
const bookInserted: boolean = BookRepo.insertSyncBook(
|
|
bookData.book_id,
|
|
userId,
|
|
bookData.type,
|
|
encryptedBookTitle,
|
|
bookData.hashed_title,
|
|
encryptedBookSubTitle,
|
|
bookData.hashed_sub_title,
|
|
encryptedBookSummary,
|
|
bookData.serie_id,
|
|
bookData.desired_release_date,
|
|
bookData.desired_word_count,
|
|
bookData.words_count,
|
|
encryptedBookCoverImage,
|
|
bookData.last_update,
|
|
lang
|
|
);
|
|
if (!bookInserted) return false;
|
|
|
|
const chaptersInserted: boolean = data.chapters.every((chapter: BookChaptersTable): boolean => {
|
|
const encryptedChapterTitle: string = System.encryptDataWithUserKey(chapter.title, userEncryptionKey);
|
|
return ChapterRepo.insertSyncChapter(chapter.chapter_id, chapter.book_id, userId, encryptedChapterTitle, 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, userEncryptionKey);
|
|
const encryptedIncidentSummary: string | null = incident.summary ? System.encryptDataWithUserKey(incident.summary, userEncryptionKey) : null;
|
|
return IncidentRepository.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 encryptedPlotPointTitle: string = System.encryptDataWithUserKey(plotPoint.title, userEncryptionKey);
|
|
const encryptedPlotPointSummary: string | null = plotPoint.summary ? System.encryptDataWithUserKey(plotPoint.summary, userEncryptionKey) : null;
|
|
return PlotPointRepository.insertSyncPlotPoint(plotPoint.plot_point_id, encryptedPlotPointTitle, plotPoint.hashed_title, encryptedPlotPointSummary, plotPoint.linked_incident_id, userId, plotPoint.book_id, plotPoint.last_update, lang);
|
|
});
|
|
if (!plotPointsInserted) return false;
|
|
|
|
const chapterContentsInserted: boolean = data.chapterContents.every((chapterContent: BookChapterContentTable): boolean => {
|
|
const encryptedChapterContent: string | null = chapterContent.content ? System.encryptDataWithUserKey(JSON.stringify(chapterContent.content), userEncryptionKey) : null;
|
|
return ChapterContentRepository.insertSyncChapterContent(chapterContent.content_id, chapterContent.chapter_id, userId, chapterContent.version, encryptedChapterContent, chapterContent.words_count, chapterContent.time_on_it, chapterContent.last_update, lang);
|
|
});
|
|
if (!chapterContentsInserted) return false;
|
|
|
|
const chapterInfosInserted: boolean = data.chapterInfos.every((chapterInfo: BookChapterInfosTable): boolean => {
|
|
const encryptedChapterSummary: string | null = chapterInfo.summary ? System.encryptDataWithUserKey(chapterInfo.summary, userEncryptionKey) : null;
|
|
const encryptedChapterGoal: string | null = chapterInfo.goal ? System.encryptDataWithUserKey(chapterInfo.goal, userEncryptionKey) : null;
|
|
return ChapterRepo.insertSyncChapterInfo(chapterInfo.chapter_info_id, chapterInfo.chapter_id, chapterInfo.act_id, chapterInfo.incident_id, chapterInfo.plot_point_id, chapterInfo.book_id, userId, encryptedChapterSummary, encryptedChapterGoal, chapterInfo.last_update, lang);
|
|
});
|
|
if (!chapterInfosInserted) return false;
|
|
|
|
const charactersInserted: boolean = data.characters.every((character: BookCharactersTable): boolean => {
|
|
const encryptedCharacterFirstName: string = System.encryptDataWithUserKey(character.first_name, userEncryptionKey);
|
|
const encryptedCharacterLastName: string | null = character.last_name ? System.encryptDataWithUserKey(character.last_name, userEncryptionKey) : null;
|
|
const encryptedCharacterCategory: string = System.encryptDataWithUserKey(character.category, userEncryptionKey);
|
|
const encryptedCharacterTitle: string | null = character.title ? System.encryptDataWithUserKey(character.title, userEncryptionKey) : null;
|
|
const encryptedCharacterImage: string | null = character.image ? System.encryptDataWithUserKey(character.image, userEncryptionKey) : null;
|
|
const encryptedCharacterRole: string | null = character.role ? System.encryptDataWithUserKey(character.role, userEncryptionKey) : null;
|
|
const encryptedCharacterBiography: string | null = character.biography ? System.encryptDataWithUserKey(character.biography, userEncryptionKey) : null;
|
|
const encryptedCharacterHistory: string | null = character.history ? System.encryptDataWithUserKey(character.history, userEncryptionKey) : null;
|
|
return CharacterRepo.insertSyncCharacter(character.character_id, character.book_id, userId, encryptedCharacterFirstName, encryptedCharacterLastName, encryptedCharacterCategory, encryptedCharacterTitle, encryptedCharacterImage, encryptedCharacterRole, encryptedCharacterBiography, encryptedCharacterHistory, character.last_update, lang);
|
|
});
|
|
if (!charactersInserted) return false;
|
|
|
|
const characterAttributesInserted: boolean = data.characterAttributes.every((characterAttribute: BookCharactersAttributesTable): boolean => {
|
|
const encryptedAttributeName: string = System.encryptDataWithUserKey(characterAttribute.attribute_name, userEncryptionKey);
|
|
const encryptedAttributeValue: string = System.encryptDataWithUserKey(characterAttribute.attribute_value, userEncryptionKey);
|
|
return CharacterRepo.insertSyncCharacterAttribute(characterAttribute.attr_id, characterAttribute.character_id, userId, encryptedAttributeName, encryptedAttributeValue, characterAttribute.last_update, lang);
|
|
});
|
|
if (!characterAttributesInserted) return false;
|
|
|
|
const locationsInserted: boolean = data.locations.every((location: BookLocationTable): boolean => {
|
|
const encryptedLocationName: string = System.encryptDataWithUserKey(location.loc_name, userEncryptionKey);
|
|
return LocationRepo.insertSyncLocation(location.loc_id, location.book_id, userId, encryptedLocationName, location.loc_original_name, location.last_update, lang);
|
|
});
|
|
if (!locationsInserted) return false;
|
|
|
|
const locationElementsInserted: boolean = data.locationElements.every((locationElement: LocationElementTable): boolean => {
|
|
const encryptedLocationElementName: string = System.encryptDataWithUserKey(locationElement.element_name, userEncryptionKey);
|
|
const encryptedLocationElementDescription: string | null = locationElement.element_description ? System.encryptDataWithUserKey(locationElement.element_description, userEncryptionKey) : null;
|
|
return LocationRepo.insertSyncLocationElement(locationElement.element_id, locationElement.location, userId, encryptedLocationElementName, locationElement.original_name, encryptedLocationElementDescription, locationElement.last_update, lang);
|
|
});
|
|
if (!locationElementsInserted) return false;
|
|
|
|
const locationSubElementsInserted: boolean = data.locationSubElements.every((locationSubElement: LocationSubElementTable): boolean => {
|
|
const encryptedSubElementName: string = System.encryptDataWithUserKey(locationSubElement.sub_elem_name, userEncryptionKey);
|
|
const encryptedSubElementDescription: string | null = locationSubElement.sub_elem_description ? System.encryptDataWithUserKey(locationSubElement.sub_elem_description, userEncryptionKey) : null;
|
|
return LocationRepo.insertSyncLocationSubElement(locationSubElement.sub_element_id, locationSubElement.element_id, userId, encryptedSubElementName, locationSubElement.original_name, encryptedSubElementDescription, locationSubElement.last_update, lang);
|
|
});
|
|
if (!locationSubElementsInserted) return false;
|
|
|
|
const worldsInserted: boolean = data.worlds.every((world: BookWorldTable): boolean => {
|
|
const encryptedWorldName: string = System.encryptDataWithUserKey(world.name, userEncryptionKey);
|
|
const encryptedWorldHistory: string | null = world.history ? System.encryptDataWithUserKey(world.history, userEncryptionKey) : null;
|
|
const encryptedWorldPolitics: string | null = world.politics ? System.encryptDataWithUserKey(world.politics, userEncryptionKey) : null;
|
|
const encryptedWorldEconomy: string | null = world.economy ? System.encryptDataWithUserKey(world.economy, userEncryptionKey) : null;
|
|
const encryptedWorldReligion: string | null = world.religion ? System.encryptDataWithUserKey(world.religion, userEncryptionKey) : null;
|
|
const encryptedWorldLanguages: string | null = world.languages ? System.encryptDataWithUserKey(world.languages, userEncryptionKey) : null;
|
|
return WorldRepository.insertSyncWorld(world.world_id, encryptedWorldName, world.hashed_name, userId, world.book_id, encryptedWorldHistory, encryptedWorldPolitics, encryptedWorldEconomy, encryptedWorldReligion, encryptedWorldLanguages, world.last_update, lang);
|
|
});
|
|
if (!worldsInserted) return false;
|
|
|
|
const worldElementsInserted: boolean = data.worldElements.every((worldElement: BookWorldElementsTable): boolean => {
|
|
const encryptedWorldElementName: string = System.encryptDataWithUserKey(worldElement.name, userEncryptionKey);
|
|
const encryptedWorldElementDescription: string | null = worldElement.description ? System.encryptDataWithUserKey(worldElement.description, userEncryptionKey) : null;
|
|
return WorldRepository.insertSyncWorldElement(worldElement.element_id, worldElement.world_id, userId, worldElement.element_type, encryptedWorldElementName, worldElement.original_name, encryptedWorldElementDescription, worldElement.last_update, lang);
|
|
});
|
|
if (!worldElementsInserted) return false;
|
|
|
|
const actSummariesInserted: boolean = data.actSummaries.every((actSummary: BookActSummariesTable): boolean => {
|
|
const encryptedActSummary: string | null = actSummary.summary ? System.encryptDataWithUserKey(actSummary.summary, userEncryptionKey) : null;
|
|
return ActRepository.insertSyncActSummary(actSummary.act_sum_id, actSummary.book_id, userId, actSummary.act_index, encryptedActSummary, actSummary.last_update, lang);
|
|
});
|
|
if (!actSummariesInserted) return false;
|
|
|
|
const aiGuidelinesInserted: boolean = data.aiGuideLine.every((aiGuideline: BookAIGuideLineTable): boolean => {
|
|
const encryptedAIGlobalResume: string | null = aiGuideline.global_resume ? System.encryptDataWithUserKey(aiGuideline.global_resume, userEncryptionKey) : null;
|
|
const encryptedAIThemes: string | null = aiGuideline.themes ? System.encryptDataWithUserKey(aiGuideline.themes, userEncryptionKey) : null;
|
|
const encryptedAITone: string | null = aiGuideline.tone ? System.encryptDataWithUserKey(aiGuideline.tone, userEncryptionKey) : null;
|
|
const encryptedAIAtmosphere: string | null = aiGuideline.atmosphere ? System.encryptDataWithUserKey(aiGuideline.atmosphere, userEncryptionKey) : null;
|
|
const encryptedAICurrentResume: string | null = aiGuideline.current_resume ? System.encryptDataWithUserKey(aiGuideline.current_resume, userEncryptionKey) : null;
|
|
return GuidelineRepo.insertSyncAIGuideLine(userId, aiGuideline.book_id, encryptedAIGlobalResume, encryptedAIThemes, aiGuideline.verbe_tense, aiGuideline.narrative_type, aiGuideline.langue, aiGuideline.dialogue_type, encryptedAITone, encryptedAIAtmosphere, encryptedAICurrentResume, aiGuideline.last_update, lang);
|
|
});
|
|
if (!aiGuidelinesInserted) return false;
|
|
|
|
const guidelinesInserted: boolean = data.guideLine.every((guideline: BookGuideLineTable): boolean => {
|
|
const encryptedGuidelineTone: string | null = guideline.tone ? System.encryptDataWithUserKey(guideline.tone, userEncryptionKey) : null;
|
|
const encryptedGuidelineAtmosphere: string | null = guideline.atmosphere ? System.encryptDataWithUserKey(guideline.atmosphere, userEncryptionKey) : null;
|
|
const encryptedGuidelineWritingStyle: string | null = guideline.writing_style ? System.encryptDataWithUserKey(guideline.writing_style, userEncryptionKey) : null;
|
|
const encryptedGuidelineThemes: string | null = guideline.themes ? System.encryptDataWithUserKey(guideline.themes, userEncryptionKey) : null;
|
|
const encryptedGuidelineSymbolism: string | null = guideline.symbolism ? System.encryptDataWithUserKey(guideline.symbolism, userEncryptionKey) : null;
|
|
const encryptedGuidelineMotifs: string | null = guideline.motifs ? System.encryptDataWithUserKey(guideline.motifs, userEncryptionKey) : null;
|
|
const encryptedGuidelineNarrativeVoice: string | null = guideline.narrative_voice ? System.encryptDataWithUserKey(guideline.narrative_voice, userEncryptionKey) : null;
|
|
const encryptedGuidelinePacing: string | null = guideline.pacing ? System.encryptDataWithUserKey(guideline.pacing, userEncryptionKey) : null;
|
|
const encryptedGuidelineIntendedAudience: string | null = guideline.intended_audience ? System.encryptDataWithUserKey(guideline.intended_audience, userEncryptionKey) : null;
|
|
const encryptedGuidelineKeyMessages: string | null = guideline.key_messages ? System.encryptDataWithUserKey(guideline.key_messages, userEncryptionKey) : null;
|
|
return GuidelineRepo.insertSyncGuideLine(userId, guideline.book_id, encryptedGuidelineTone, encryptedGuidelineAtmosphere, encryptedGuidelineWritingStyle, encryptedGuidelineThemes, encryptedGuidelineSymbolism, encryptedGuidelineMotifs, encryptedGuidelineNarrativeVoice, encryptedGuidelinePacing, encryptedGuidelineIntendedAudience, encryptedGuidelineKeyMessages, guideline.last_update, lang);
|
|
});
|
|
if (!guidelinesInserted) return false;
|
|
|
|
const issuesInserted: boolean = data.issues.every((issue: BookIssuesTable): boolean => {
|
|
const encryptedIssueName: string = System.encryptDataWithUserKey(issue.name, userEncryptionKey);
|
|
return IssueRepository.insertSyncIssue(issue.issue_id, userId, issue.book_id, encryptedIssueName, issue.hashed_issue_name, issue.last_update, lang);
|
|
});
|
|
if (!issuesInserted) return false;
|
|
|
|
return data.bookTools.every((bookTool: BookToolsTable): boolean => {
|
|
return BookRepo.insertSyncBookTools(bookTool.book_id, userId, bookTool.characters_enabled, bookTool.worlds_enabled, bookTool.locations_enabled, bookTool.last_update, lang);
|
|
});
|
|
}
|
|
}
|