From 3bc30d42ad46cf2594cbcd435e0387165066e7b2 Mon Sep 17 00:00:00 2001 From: natreex Date: Mon, 17 Nov 2025 21:32:42 -0500 Subject: [PATCH] Remove `Story` model handling verbal styles and linguistic properties - Delete `Story` model implementation including `getVerbesStyle` method and related properties. - Cleanup unused interfaces and redundant logic from the codebase. --- electron/database/keyManager.ts | 48 ++ electron/database/models/Chapter.ts | 4 +- electron/database/models/Character.ts | 133 ++- electron/database/models/Location.ts | 154 ++-- electron/database/models/Story.ts | 712 ---------------- electron/database/models/User.ts | 763 +----------------- .../database/repositories/book.repository.ts | 41 +- .../repositories/chapter.repository.ts | 15 +- .../repositories/character.repository.ts | 12 +- .../repositories/location.repository.ts | 24 +- .../database/repositories/user.repository.ts | 99 +-- lib/models/Book.ts | 15 +- 12 files changed, 259 insertions(+), 1761 deletions(-) create mode 100644 electron/database/keyManager.ts delete mode 100755 electron/database/models/Story.ts diff --git a/electron/database/keyManager.ts b/electron/database/keyManager.ts new file mode 100644 index 0000000..676c7c6 --- /dev/null +++ b/electron/database/keyManager.ts @@ -0,0 +1,48 @@ +import Store from 'electron-store'; + +/** + * Key Manager - Manages user encryption keys stored in electron-store + */ + +const store = new Store({ + encryptionKey: 'eritors-scribe-secure-key' +}); + +/** + * Get user encryption key from secure store + * @param userId - User ID + * @returns User's encryption key or null if not found + */ +export function getUserEncryptionKey(userId: string): string { + const key: string | undefined = store.get(`encryptionKey-${userId}`) as string | undefined; + if (key === undefined) { + throw new Error(`Unknown encryptionKey`); + } + return key; +} + +/** + * Set user encryption key in secure store + * @param userId - User ID + * @param encryptionKey - Encryption key to store + */ +export function setUserEncryptionKey(userId: string, encryptionKey: string): void { + store.set(`encryptionKey-${userId}`, encryptionKey); +} + +/** + * Check if user has an encryption key + * @param userId - User ID + * @returns True if key exists + */ +export function hasUserEncryptionKey(userId: string): boolean { + return store.has(`encryptionKey-${userId}`); +} + +/** + * Delete user encryption key + * @param userId - User ID + */ +export function deleteUserEncryptionKey(userId: string): void { + store.delete(`encryptionKey-${userId}`); +} diff --git a/electron/database/models/Chapter.ts b/electron/database/models/Chapter.ts index c3bce67..1e143a8 100644 --- a/electron/database/models/Chapter.ts +++ b/electron/database/models/Chapter.ts @@ -203,13 +203,13 @@ export default class Chapter { return ChapterRepo.updateChapter(userId, chapterId, encryptedTitle, hashedTitle, chapterOrder, lang); } - static updateChapterInfos(chapters: ActChapter[], userId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, meta: string, lang: 'fr' | 'en' = 'fr') { + static updateChapterInfos(chapters: ActChapter[], userId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, lang: 'fr' | 'en' = 'fr') { const userKey: string = getUserEncryptionKey(userId); for (const chapter of chapters) { const summary: string = chapter.summary ? System.encryptDataWithUserKey(chapter.summary, userKey) : ''; const goal: string = chapter.goal ? System.encryptDataWithUserKey(chapter.goal, userKey) : ''; const chapterId: string = chapter.chapterId; - ChapterRepo.updateChapterInfos(userId, chapterId, actId, bookId, incidentId, plotId, summary, goal, meta, lang); + ChapterRepo.updateChapterInfos(userId, chapterId, actId, bookId, incidentId, plotId, summary, goal, lang); } } diff --git a/electron/database/models/Character.ts b/electron/database/models/Character.ts index 42245ed..a79fcaf 100644 --- a/electron/database/models/Character.ts +++ b/electron/database/models/Character.ts @@ -1,6 +1,10 @@ -import User from "./User"; -import System, {UserKey} from "./System"; -import CharacterRepo, {AttributeResult, CharacterResult, CompleteCharacterResult} from "../repositories/character.repo"; +import CharacterRepo, { + AttributeResult, + CharacterResult, + CompleteCharacterResult +} from "@/electron/database/repositories/character.repository"; +import System from "@/electron/database/System"; +import {getUserEncryptionKey} from "../keyManager"; export type CharacterCategory = 'Main' | 'Secondary' | 'Recurring'; @@ -61,19 +65,17 @@ export interface CharacterAttribute { values: Attribute[]; } -export default class Character{ - public static async getCharacterList(userId: string, bookId: string): Promise { - const user = new User(userId); - const keys:UserKey[] = await System.getAllUserKeysAndVersions(userId); - const characters: CharacterResult[] = await CharacterRepo.fetchCharacters(userId, bookId); +export default class Character { + public static getCharacterList(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): CharacterProps[] { + const userKey: string = getUserEncryptionKey(userId); + const characters: CharacterResult[] = CharacterRepo.fetchCharacters(userId, bookId, lang); if (!characters) return []; if (characters.length === 0) return []; - const characterList:CharacterProps[] = []; + const characterList: CharacterProps[] = []; for (const character of characters) { - const userKey:string = await user.getUserKey(character.char_meta,true,keys); characterList.push({ - id:character.character_id, - name:character.first_name ? System.decryptDataWithUserKey(character.first_name,userKey) : '', + id: character.character_id, + name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '', lastName: character.last_name ? System.decryptDataWithUserKey(character.last_name, userKey) : '', title: character.title ? System.decryptDataWithUserKey(character.title, userKey) : '', category: character.category ? System.decryptDataWithUserKey(character.category, userKey) : '', @@ -85,11 +87,9 @@ export default class Character{ } return characterList; } - - public static async addNewCharacter(userId: string, character: CharacterPropsPost, bookId: string): Promise { - const user = new User(userId); - const meta:string = System.encryptDateKey(userId); - const userKey:string = await user.getUserKey(meta,true); + + public static addNewCharacter(userId: string, character: CharacterPropsPost, bookId: string, lang: 'fr' | 'en' = 'fr'): string { + const userKey: string = getUserEncryptionKey(userId); const characterId: string = System.createUniqueId(); const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey); const encryptedLastName: string = System.encryptDataWithUserKey(character.lastName, userKey); @@ -99,7 +99,7 @@ export default class Character{ const encryptedRole: string = System.encryptDataWithUserKey(character.role, userKey); const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey); const encryptedHistory: string = System.encryptDataWithUserKey(character.history ? character.history : '', userKey); - await CharacterRepo.addNewCharacter(userId, characterId, encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, bookId, meta); + CharacterRepo.addNewCharacter(userId, characterId, encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, bookId, lang); const attributes: string[] = Object.keys(character); for (const key of attributes) { if (Array.isArray(character[key as keyof CharacterPropsPost])) { @@ -108,18 +108,16 @@ export default class Character{ for (const item of array) { const type: string = key; const name: string = item.name; - await this.addNewAttribute(characterId, userId, type, name); + this.addNewAttribute(characterId, userId, type, name, lang); } } } } return characterId; } - - static async updateCharacter(userId: string, character: CharacterPropsPost): Promise { - const user = new User(userId); - const meta: string = System.encryptDateKey(userId); - const userKey: string = await user.getUserKey(meta, true); + + static updateCharacter(userId: string, character: CharacterPropsPost, lang: 'fr' | 'en' = 'fr'): boolean { + const userKey: string = getUserEncryptionKey(userId); const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey); const encryptedLastName: string = System.encryptDataWithUserKey(character.lastName, userKey); const encryptedTitle: string = System.encryptDataWithUserKey(character.title, userKey); @@ -128,72 +126,63 @@ export default class Character{ const encryptedRole: string = System.encryptDataWithUserKey(character.role, userKey); const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey); const encryptedHistory: string = System.encryptDataWithUserKey(character.history ? character.history : '', userKey); - return await CharacterRepo.updateCharacter(userId, character.id, encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, meta); + return CharacterRepo.updateCharacter(userId, character.id, encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, lang); } - - static async addNewAttribute(characterId: string, userId: string, type: string, name: string): Promise { - const user = new User(userId); - const meta: string = System.encryptDateKey(userId); - const userKey: string = await user.getUserKey(meta, true); + + static addNewAttribute(characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string { + const userKey: string = getUserEncryptionKey(userId); const attributeId: string = System.createUniqueId(); const encryptedType: string = System.encryptDataWithUserKey(type, userKey); const encryptedName: string = System.encryptDataWithUserKey(name, userKey); - return await CharacterRepo.insertAttribute(attributeId, characterId, userId, encryptedType, encryptedName, meta); + return CharacterRepo.insertAttribute(attributeId, characterId, userId, encryptedType, encryptedName, lang); } - static async deleteAttribute(userId: string, attributeId: string) { - return await CharacterRepo.deleteAttribute(userId, attributeId); + static deleteAttribute(userId: string, attributeId: string, lang: 'fr' | 'en' = 'fr') { + return CharacterRepo.deleteAttribute(userId, attributeId, lang); } - - static async getAttributes(characterId: string, userId: string): Promise { - const user: User = new User(userId); - const keys: UserKey[] = await System.getAllUserKeysAndVersions(userId); - const attributes: AttributeResult[] = await CharacterRepo.fetchAttributes(characterId, userId); + + static getAttributes(characterId: string, userId: string, lang: 'fr' | 'en' = 'fr'): CharacterAttribute[] { + const userKey: string = getUserEncryptionKey(userId); + const attributes: AttributeResult[] = CharacterRepo.fetchAttributes(characterId, userId, lang); if (!attributes?.length) return []; - + const groupedMap: Map = new Map(); for (const attribute of attributes) { - const userKey: string = await user.getUserKey(attribute.attr_meta, true, keys); const type: string = System.decryptDataWithUserKey(attribute.attribute_name, userKey); const value: string = attribute.attribute_value ? System.decryptDataWithUserKey(attribute.attribute_value, userKey) : ''; - + if (!groupedMap.has(type)) { groupedMap.set(type, []); } - + groupedMap.get(type)!.push({ id: attribute.attr_id, name: value }); } - + return Array.from<[string, Attribute[]], CharacterAttribute>( groupedMap, ([type, values]: [string, Attribute[]]): CharacterAttribute => ({type, values}) ); } - - static async getCompleteCharacterList(userId: string, bookId: string, characters: string[]): Promise { - const characterList: CompleteCharacterResult[] = await CharacterRepo.fetchCompleteCharacters(userId, bookId, characters); - + + static getCompleteCharacterList(userId: string, bookId: string, characters: string[], lang: 'fr' | 'en' = 'fr'): CompleteCharacterProps[] { + const characterList: CompleteCharacterResult[] = CharacterRepo.fetchCompleteCharacters(userId, bookId, characters, lang); + if (!characterList || characterList.length === 0) { return []; } - - const user = new User(userId); - const keys: UserKey[] = await System.getAllUserKeysAndVersions(userId); + + const userKey: string = getUserEncryptionKey(userId); const completeCharactersMap = new Map(); for (const character of characterList) { if (!character.character_id) { continue; } - + if (!completeCharactersMap.has(character.character_id)) { - const userKey: string = await user.getUserKey(character.char_meta, true, keys); - if (!userKey) { - continue; - } const personnageObj: CompleteCharacterProps = { id: '', name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '', @@ -214,21 +203,19 @@ export default class Character{ }; completeCharactersMap.set(character.character_id, personnageObj); } - + const personnage: CompleteCharacterProps | undefined = completeCharactersMap.get(character.character_id); - + if (!character.attr_meta) { continue; } - - const rowKey: string = await user.getUserKey(character.attr_meta, true, keys); - - if (!personnage || !rowKey) { + + if (!personnage) { continue; } - const decryptedName: string = System.decryptDataWithUserKey(character.attribute_name, rowKey); - const decryptedValue: string = character.attribute_value ? System.decryptDataWithUserKey(character.attribute_value, rowKey) : ''; - + const decryptedName: string = System.decryptDataWithUserKey(character.attribute_name, userKey); + const decryptedValue: string = character.attribute_value ? System.decryptDataWithUserKey(character.attribute_value, userKey) : ''; + if (Array.isArray(personnage[decryptedName])) { personnage[decryptedName].push({ id: '', @@ -238,14 +225,14 @@ export default class Character{ } return Array.from(completeCharactersMap.values()); } - + static characterVCard(characters: CompleteCharacterProps[]): string { const charactersMap = new Map(); let charactersDescription: string = ''; - + characters.forEach((character: CompleteCharacterProps): void => { const characterKey: string = character.name || character.id || 'unknown'; - + if (!charactersMap.has(characterKey)) { charactersMap.set(characterKey, { name: character.name, @@ -257,9 +244,9 @@ export default class Character{ history: character.history }); } - + const characterData: CompleteCharacterProps = charactersMap.get(characterKey)!; - + Object.keys(character).forEach((fieldName: string): void => { if (Array.isArray(character[fieldName])) { if (!characterData[fieldName]) characterData[fieldName] = []; @@ -267,18 +254,18 @@ export default class Character{ } }); }); - + charactersDescription = Array.from(charactersMap.values()).map((character: CompleteCharacterProps): string => { const descriptionFields: string[] = []; const fullName: string = [character.name, character.lastName].filter(Boolean).join(' '); if (fullName) descriptionFields.push(`Nom : ${fullName}`); - + (['category', 'title', 'role', 'biography', 'history'] as const).forEach((propertyKey) => { if (character[propertyKey]) { descriptionFields.push(`${propertyKey.charAt(0).toUpperCase() + propertyKey.slice(1)} : ${character[propertyKey]}`); } }); - + Object.keys(character).forEach((propertyKey: string): void => { const propertyValue: string | Attribute[] | undefined = character[propertyKey]; if (Array.isArray(propertyValue) && propertyValue.length > 0) { @@ -287,7 +274,7 @@ export default class Character{ descriptionFields.push(`${capitalizedPropertyKey} : ${formattedValues}`); } }); - + return descriptionFields.join('\n'); }).join('\n\n'); return charactersDescription; diff --git a/electron/database/models/Location.ts b/electron/database/models/Location.ts index b0552a2..1814f1c 100644 --- a/electron/database/models/Location.ts +++ b/electron/database/models/Location.ts @@ -1,10 +1,10 @@ -import User from "./User"; -import System, {UserKey} from "./System"; import LocationRepo, { LocationByTagResult, LocationElementQueryResult, LocationQueryResult -} from "../repositories/location.repo"; +} from "@/electron/database/repositories/location.repository"; +import System from "@/electron/database/System"; +import {getUserEncryptionKey} from "../keyManager"; export interface SubElement { id: string; @@ -30,23 +30,21 @@ export default class Location { * Récupère toutes les locations pour un utilisateur et un livre donnés. * @param {string} userId - L'ID de l'utilisateur. * @param {string} bookId - L'ID du livre. - * @returns {Promise} - Une promesse qui résout un tableau de propriétés de location. + * @returns {LocationProps[]} - Un tableau de propriétés de location. * @throws {Error} - Lance une erreur si une exception se produit lors de la récupération des locations. */ - static async getAllLocations(userId: string, bookId: string): Promise { - const locations: LocationQueryResult[] = await LocationRepo.getLocation(userId, bookId); + static getAllLocations(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationProps[] { + const locations: LocationQueryResult[] = LocationRepo.getLocation(userId, bookId, lang); if (!locations || locations.length === 0) return []; - const user = new User(userId); - const userKeys: UserKey[] = await System.getAllUserKeysAndVersions(userId); - + const userKey: string = getUserEncryptionKey(userId); + const locationArray: LocationProps[] = []; - + for (const record of locations) { let location = locationArray.find(loc => loc.id === record.loc_id); - + if (!location) { - const key: string = await user.getUserKey(record.loc_meta, true, userKeys); - const decryptedName: string = System.decryptDataWithUserKey(record.loc_name, key)/* ton code de décryptage ici avec record.loc_name */; + const decryptedName: string = System.decryptDataWithUserKey(record.loc_name, userKey); location = { id: record.loc_id, name: decryptedName, @@ -54,14 +52,13 @@ export default class Location { }; locationArray.push(location); } - + if (record.element_id) { let element = location.elements.find(elem => elem.id === record.element_id); if (!element) { - const key: string = await user.getUserKey(record.element_meta, true, userKeys); - const decryptedName: string = System.decryptDataWithUserKey(record.element_name, key)/* ton code de décryptage ici avec record.element_name */; - const decryptedDesc: string = record.element_description ? System.decryptDataWithUserKey(record.element_description, key) : ''/* ton code de décryptage ici avec record.element_description */; - + const decryptedName: string = System.decryptDataWithUserKey(record.element_name, userKey); + const decryptedDesc: string = record.element_description ? System.decryptDataWithUserKey(record.element_description, userKey) : ''; + element = { id: record.element_id, name: decryptedName, @@ -70,16 +67,15 @@ export default class Location { }; location.elements.push(element); } - + if (record.sub_element_id) { const subElementExists = element.subElements.some(sub => sub.id === record.sub_element_id); - + if (!subElementExists) { - const key: string = await user.getUserKey(record.sub_elem_meta, true, userKeys); - const decryptedName: string = System.decryptDataWithUserKey(record.sub_elem_name, key)/* ton code de décryptage ici avec record.sub_elem_name */; - const decryptedDesc: string = record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, key) : ''/* ton code de décryptage ici avec record.sub_elem_description */; - - + const decryptedName: string = System.decryptDataWithUserKey(record.sub_elem_name, userKey); + const decryptedDesc: string = record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, userKey) : ''; + + element.subElements.push({ id: record.sub_element_id, name: decryptedName, @@ -91,133 +87,120 @@ export default class Location { } return locationArray; } - - static async addLocationSection(userId: string, locationName: string, bookId: string): Promise { - const user = new User(userId); - const meta: string = System.encryptDateKey(userId); - const userKey: string = await user.getUserKey(meta, true); + + static addLocationSection(userId: string, locationName: string, bookId: string, lang: 'fr' | 'en' = 'fr'): string { + const userKey: string = getUserEncryptionKey(userId); const originalName: string = System.hashElement(locationName); const encryptedName: string = System.encryptDataWithUserKey(locationName, userKey); const locationId: string = System.createUniqueId(); - return await LocationRepo.insertLocation(userId, locationId, bookId, encryptedName, originalName, meta); + return LocationRepo.insertLocation(userId, locationId, bookId, encryptedName, originalName, lang); } - static async addLocationElement(userId: string, locationId: string, elementName: string) { - const user = new User(userId); - const meta: string = System.encryptDateKey(userId); - const userKey: string = await user.getUserKey(meta, true); + static addLocationElement(userId: string, locationId: string, elementName: string, lang: 'fr' | 'en' = 'fr') { + const userKey: string = getUserEncryptionKey(userId); const originalName: string = System.hashElement(elementName); const encryptedName: string = System.encryptDataWithUserKey(elementName, userKey); const elementId: string = System.createUniqueId(); - return LocationRepo.insertLocationElement(userId, elementId, locationId, encryptedName, originalName, meta) + return LocationRepo.insertLocationElement(userId, elementId, locationId, encryptedName, originalName, lang) } - static async addLocationSubElement(userId: string, elementId: string, subElementName: string) { - const user = new User(userId); - const meta: string = System.encryptDateKey(userId); - const userKey: string = await user.getUserKey(meta, true); + static addLocationSubElement(userId: string, elementId: string, subElementName: string, lang: 'fr' | 'en' = 'fr') { + const userKey: string = getUserEncryptionKey(userId); const originalName: string = System.hashElement(subElementName); const encryptedName: string = System.encryptDataWithUserKey(subElementName, userKey); const subElementId: string = System.createUniqueId(); - return LocationRepo.insertLocationSubElement(userId, subElementId, elementId, encryptedName, originalName, meta) + return LocationRepo.insertLocationSubElement(userId, subElementId, elementId, encryptedName, originalName, lang) } - static async updateLocationSection(userId: string, locations: LocationProps[]) { - const user = new User(userId); - const meta: string = System.encryptDateKey(userId); - const userKey: string = await user.getUserKey(meta, true); + static updateLocationSection(userId: string, locations: LocationProps[], lang: 'fr' | 'en' = 'fr') { + const userKey: string = getUserEncryptionKey(userId); for (const location of locations) { const originalName: string = System.hashElement(location.name); const encryptedName: string = System.encryptDataWithUserKey(location.name, userKey); - await LocationRepo.updateLocationSection(userId, location.id, encryptedName, originalName, meta) + LocationRepo.updateLocationSection(userId, location.id, encryptedName, originalName, lang) for (const element of location.elements) { const originalName: string = System.hashElement(element.name); const encryptedName: string = System.encryptDataWithUserKey(element.name, userKey); const encryptDescription: string = element.description ? System.encryptDataWithUserKey(element.description, userKey) : ''; - await LocationRepo.updateLocationElement(userId, element.id, encryptedName, originalName, encryptDescription, meta) + LocationRepo.updateLocationElement(userId, element.id, encryptedName, originalName, encryptDescription, lang) for (const subElement of element.subElements) { const originalName: string = System.hashElement(subElement.name); const encryptedName: string = System.encryptDataWithUserKey(subElement.name, userKey); const encryptDescription: string = subElement.description ? System.encryptDataWithUserKey(subElement.description, userKey) : ''; - await LocationRepo.updateLocationSubElement(userId, subElement.id, encryptedName, originalName, encryptDescription, meta) + LocationRepo.updateLocationSubElement(userId, subElement.id, encryptedName, originalName, encryptDescription, lang) } } } return { valid: true, - message: 'Les sections ont été mis à jour.' + message: lang === 'fr' ? 'Les sections ont été mis à jour.' : 'Sections have been updated.' } } - static async deleteLocationSection(userId: string, locationId: string) { - return LocationRepo.deleteLocationSection(userId, locationId); + static deleteLocationSection(userId: string, locationId: string, lang: 'fr' | 'en' = 'fr') { + return LocationRepo.deleteLocationSection(userId, locationId, lang); } - static async deleteLocationElement(userId: string, elementId: string) { - return LocationRepo.deleteLocationElement(userId, elementId); + static deleteLocationElement(userId: string, elementId: string, lang: 'fr' | 'en' = 'fr') { + return LocationRepo.deleteLocationElement(userId, elementId, lang); } - static async deleteLocationSubElement(userId: string, subElementId: string) { - return LocationRepo.deleteLocationSubElement(userId, subElementId); + static deleteLocationSubElement(userId: string, subElementId: string, lang: 'fr' | 'en' = 'fr') { + return LocationRepo.deleteLocationSubElement(userId, subElementId, lang); } - - static async getLocationTags(userId: string, bookId: string): Promise { - const data: LocationElementQueryResult[] = await LocationRepo.fetchLocationTags(userId, bookId); + + static getLocationTags(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): SubElement[] { + const data: LocationElementQueryResult[] = LocationRepo.fetchLocationTags(userId, bookId, lang); if (!data || data.length === 0) return []; - const user = new User(userId); - const userKeys: UserKey[] = await System.getAllUserKeysAndVersions(userId); - + const userKey: string = getUserEncryptionKey(userId); + const elementCounts = new Map(); data.forEach((record: LocationElementQueryResult): void => { elementCounts.set(record.element_id, (elementCounts.get(record.element_id) || 0) + 1); }); - + const subElements: SubElement[] = []; const processedIds = new Set(); - + for (const record of data) { const elementCount: number = elementCounts.get(record.element_id) || 0; - + if (elementCount > 1 && record.sub_element_id) { if (processedIds.has(record.sub_element_id)) continue; - - const key: string = await user.getUserKey(record.sub_elem_meta, true, userKeys); + subElements.push({ id: record.sub_element_id, - name: System.decryptDataWithUserKey(record.sub_elem_name, key), - description: record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, key) : '' + name: System.decryptDataWithUserKey(record.sub_elem_name, userKey), + description: record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, userKey) : '' }); processedIds.add(record.sub_element_id); } else if (elementCount === 1 && !record.sub_element_id) { if (processedIds.has(record.element_id)) continue; - - const key: string = await user.getUserKey(record.element_meta, true, userKeys); + subElements.push({ id: record.element_id, - name: System.decryptDataWithUserKey(record.element_name, key), - description: record.element_description ? System.decryptDataWithUserKey(record.element_description, key) : '' + name: System.decryptDataWithUserKey(record.element_name, userKey), + description: record.element_description ? System.decryptDataWithUserKey(record.element_description, userKey) : '' }); processedIds.add(record.element_id); } } return subElements; } - - static async getLocationsByTags(userId: string, locations: string[]): Promise { - const locationsTags: LocationByTagResult[] = await LocationRepo.fetchLocationsByTags(userId, locations); + + static getLocationsByTags(userId: string, locations: string[], lang: 'fr' | 'en' = 'fr'): Element[] { + const locationsTags: LocationByTagResult[] = LocationRepo.fetchLocationsByTags(userId, locations, lang); if (!locationsTags || locationsTags.length === 0) return []; - const user = new User(userId); - const userKeys: UserKey[] = await System.getAllUserKeysAndVersions(userId); + const userKey: string = getUserEncryptionKey(userId); const locationTags: Element[] = []; for (const record of locationsTags) { let element: Element | undefined = locationTags.find((elem: Element): boolean => elem.name === record.element_name); if (!element) { - const key: string = await user.getUserKey(record.element_meta, true, userKeys); - const decryptedName: string = System.decryptDataWithUserKey(record.element_name, key); - const decryptedDesc: string = record.element_description ? System.decryptDataWithUserKey(record.element_description, key) : ''; + const decryptedName: string = System.decryptDataWithUserKey(record.element_name, userKey); + const decryptedDesc: string = record.element_description ? System.decryptDataWithUserKey(record.element_description, userKey) : ''; element = { - id: record.element_id, + id: '', name: decryptedName, description: decryptedDesc, subElements: [] @@ -227,9 +210,8 @@ export default class Location { if (record.sub_elem_name) { const subElementExists: boolean = element.subElements.some(sub => sub.name === record.sub_elem_name); if (!subElementExists) { - const key: string = await user.getUserKey(record.sub_elem_meta, true, userKeys); - const decryptedName: string = System.decryptDataWithUserKey(record.sub_elem_name, key); - const decryptedDesc: string = record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, key) : ''; + const decryptedName: string = System.decryptDataWithUserKey(record.sub_elem_name, userKey); + const decryptedDesc: string = record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, userKey) : ''; element.subElements.push({ id: '', name: decryptedName, @@ -240,8 +222,8 @@ export default class Location { } return locationTags; } - - static async locationsDescription(locations: Element[]): Promise { + + static locationsDescription(locations: Element[]): string { return locations.map((location: Element): string => { const fields: string[] = []; if (location.name) fields.push(`Nom : ${location.name}`); diff --git a/electron/database/models/Story.ts b/electron/database/models/Story.ts deleted file mode 100755 index 22d93d8..0000000 --- a/electron/database/models/Story.ts +++ /dev/null @@ -1,712 +0,0 @@ -export interface VerbalTimeProps{ - actions: string; - descriptions: string; - dialogues: string; - thoughts: string; - summary: string; -} - -export interface DialogueProps{ - description: string; - example: string; -} - -export default class Story { - constructor() {} - - static getVerbesStyle(verbalTimeValue: number, level: number, lang: "fr" | "en"): VerbalTimeProps { - switch (verbalTimeValue) { - case 1: // Passé Simple / Simple Past - return { - actions: level === 1 - ? (lang === "fr" ? 'Passé composé' : 'Past perfect') - : level === 2 - ? (lang === "fr" ? 'Passé simple' : 'Simple past') - : (lang === "fr" ? 'Passé simple + passé antérieur' : 'Simple past + past perfect'), - descriptions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + plus-que-parfait' : 'Imperfect + pluperfect'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + passé composé' : 'Present + present perfect'), - thoughts: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect') - : (lang === "fr" ? 'Plus-que-parfait + subjonctif passé' : 'Pluperfect + past subjunctive'), - summary: lang === "fr" ? '→ Narrations épurées, style classique' : '→ Clean narratives, classical style', - }; - - case 2: // Passé Immédiat / Immediate Past - return { - actions: level === 1 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : level === 2 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : (lang === "fr" ? 'Passé composé + présent' : 'Present perfect + present'), - descriptions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + participe passé' : 'Imperfect + past participle'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + passé composé' : 'Present + present perfect'), - thoughts: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + passé composé' : 'Present + present perfect'), - summary: lang === "fr" ? '→ Témoignages, récits autobiographiques' : '→ Testimonies, autobiographical narratives', - }; - - case 3: // Passé Profond / Deep Past - return { - actions: level === 1 - ? (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect') - : level === 2 - ? (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect') - : (lang === "fr" ? 'Passé antérieur' : 'Past anterior'), - descriptions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect') - : (lang === "fr" ? 'Plus-que-parfait + conditionnel passé' : 'Pluperfect + past conditional'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect'), - thoughts: level === 1 - ? (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect') - : level === 2 - ? (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect') - : (lang === "fr" ? 'Plus-que-parfait + subjonctif passé' : 'Pluperfect + past subjunctive'), - summary: lang === "fr" ? '→ Flashbacks littéraires, tragédies' : '→ Literary flashbacks, tragedies', - }; - - case 4: // Présent Brut / Raw Present - return { - actions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + participe présent' : 'Present + present participle'), - descriptions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + adjectifs' : 'Present + adjectives'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + impératif' : 'Present + imperative'), - thoughts: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + infinitif' : 'Present + infinitive'), - summary: lang === "fr" ? '→ Urgence, immersion totale' : '→ Urgency, total immersion', - }; - - case 5: // Présent Réflexif / Reflective Present - return { - actions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + gérondif' : 'Present + gerund'), - descriptions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent + adjectifs' : 'Present + adjectives') - : (lang === "fr" ? 'Présent + métaphores' : 'Present + metaphors'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + style indirect libre' : 'Present + free indirect style'), - thoughts: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + subjonctif présent' : 'Present + present subjunctive'), - summary: lang === "fr" ? '→ Méditations philosophiques' : '→ Philosophical meditations', - }; - - case 6: // Futur Projeté / Projected Future - return { - actions: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur antérieur' : 'Future perfect'), - descriptions: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur antérieur' : 'Future perfect'), - dialogues: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur simple + conditionnel' : 'Simple future + conditional'), - thoughts: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur antérieur' : 'Future perfect'), - summary: lang === "fr" ? '→ Prophéties, plans stratégiques' : '→ Prophecies, strategic plans', - }; - - case 7: // Futur Catastrophe / Catastrophic Future - return { - actions: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur antérieur' : 'Future perfect'), - descriptions: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur antérieur + conditionnel' : 'Future perfect + conditional'), - dialogues: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur simple + impératif' : 'Simple future + imperative'), - thoughts: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Conditionnel présent' : 'Present conditional'), - summary: lang === "fr" ? '→ Dystopies, récits post-apocalyptiques' : '→ Dystopias, post-apocalyptic narratives', - }; - - case 8: // Imparfait Onirique / Dreamlike Imperfect - return { - actions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + conditionnel présent' : 'Imperfect + present conditional'), - descriptions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + conditionnel présent' : 'Imperfect + present conditional'), - dialogues: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + conditionnel présent' : 'Imperfect + present conditional'), - thoughts: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + subjonctif présent' : 'Imperfect + present subjunctive'), - summary: lang === "fr" ? '→ Rêves, souvenirs déformés' : '→ Dreams, distorted memories', - }; - - case 9: // Conditionnel Hypothétique / Hypothetical Conditional - return { - actions: level === 1 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : level === 2 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : (lang === "fr" ? 'Conditionnel passé' : 'Past conditional'), - descriptions: level === 1 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : level === 2 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : (lang === "fr" ? 'Conditionnel passé' : 'Past conditional'), - dialogues: level === 1 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : level === 2 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : (lang === "fr" ? 'Conditionnel présent + subjonctif' : 'Present conditional + subjunctive'), - thoughts: level === 1 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : level === 2 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : (lang === "fr" ? 'Conditionnel passé' : 'Past conditional'), - summary: lang === "fr" ? '→ Uchronies, réalités alternatives' : '→ Alternate histories, alternative realities', - }; - - case 10: // Subjonctif Angoissé / Anxious Subjunctive - return { - actions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + subjonctif présent' : 'Present + present subjunctive'), - descriptions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + subjonctif présent' : 'Present + present subjunctive'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent + interjections' : 'Present + interjections') - : (lang === "fr" ? 'Présent + subjonctif présent' : 'Present + present subjunctive'), - thoughts: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Subjonctif présent' : 'Present subjunctive'), - summary: lang === "fr" ? '→ Drames psychologiques, dilemmes' : '→ Psychological dramas, dilemmas', - }; - - case 11: // Mélancolie Composée / Compound Melancholy - return { - actions: level === 1 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : level === 2 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : (lang === "fr" ? 'Passé composé + plus-que-parfait' : 'Present perfect + pluperfect'), - descriptions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + conditionnel passé' : 'Imperfect + past conditional'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect'), - thoughts: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect') - : (lang === "fr" ? 'Plus-que-parfait + conditionnel passé' : 'Pluperfect + past conditional'), - summary: lang === "fr" ? '→ Regrets, introspection nostalgique' : '→ Regrets, nostalgic introspection', - }; - - case 12: // Urgence Narrative / Narrative Urgency - return { - actions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + participe présent' : 'Present + present participle'), - descriptions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + adjectifs courts' : 'Present + short adjectives'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent + impératif' : 'Present + imperative') - : (lang === "fr" ? 'Impératif + présent' : 'Imperative + present'), - thoughts: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + infinitif' : 'Present + infinitive'), - summary: lang === "fr" ? '→ Crise en cours, compte à rebours' : '→ Ongoing crisis, countdown', - }; - - case 13: // Présent Émotionnel / Emotional Present - return { - actions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + participe présent' : 'Present + present participle'), - descriptions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent + adjectifs' : 'Present + adjectives') - : (lang === "fr" ? 'Présent + adjectifs expressifs' : 'Present + expressive adjectives'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent + interjections' : 'Present + interjections') - : (lang === "fr" ? 'Présent + style expressif' : 'Present + expressive style'), - thoughts: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + exclamations' : 'Present + exclamations'), - summary: lang === "fr" ? '→ Émotions intenses, introspections vives' : '→ Intense emotions, vivid introspections', - }; - - case 14: // Présent Introspectif / Introspective Present - return { - actions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + gérondif' : 'Present + gerund'), - descriptions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent + adjectifs' : 'Present + adjectives') - : (lang === "fr" ? 'Présent + métaphores' : 'Present + metaphors'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Style indirect libre' : 'Free indirect style'), - thoughts: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + subjonctif présent' : 'Present + present subjunctive'), - summary: lang === "fr" ? '→ Réflexions profondes, analyse des émotions' : '→ Deep reflections, emotional analysis', - }; - - case 15: // Présent Historique / Historical Present - return { - actions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + passé simple' : 'Present + simple past'), - descriptions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + imparfait' : 'Present + imperfect'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + passé composé' : 'Present + present perfect'), - thoughts: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + plus-que-parfait' : 'Present + pluperfect'), - summary: lang === "fr" ? '→ Histoires historiques avec intensité immédiate' : '→ Historical stories with immediate intensity', - }; - - case 16: // Passé Réflexif / Reflective Past - return { - actions: level === 1 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : level === 2 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : (lang === "fr" ? 'Passé composé + plus-que-parfait' : 'Present perfect + pluperfect'), - descriptions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + conditionnel passé' : 'Imperfect + past conditional'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : (lang === "fr" ? 'Style indirect libre' : 'Free indirect style'), - thoughts: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + subjonctif passé' : 'Imperfect + past subjunctive'), - summary: lang === "fr" ? '→ Récits introspectifs, auto-analyse' : '→ Introspective narratives, self-analysis', - }; - - case 17: // Futur Prophétique / Prophetic Future - return { - actions: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur antérieur' : 'Future perfect'), - descriptions: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur antérieur' : 'Future perfect'), - dialogues: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur simple + présent gnomique' : 'Simple future + gnomic present'), - thoughts: level === 1 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : level === 2 - ? (lang === "fr" ? 'Futur simple' : 'Simple future') - : (lang === "fr" ? 'Futur antérieur' : 'Future perfect'), - summary: lang === "fr" ? '→ Prophéties, visions apocalyptiques' : '→ Prophecies, apocalyptic visions', - }; - - case 18: // Conditionnel Visionnaire / Visionary Conditional - return { - actions: level === 1 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : level === 2 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : (lang === "fr" ? 'Conditionnel passé' : 'Past conditional'), - descriptions: level === 1 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : level === 2 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : (lang === "fr" ? 'Conditionnel passé' : 'Past conditional'), - dialogues: level === 1 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : level === 2 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : (lang === "fr" ? 'Conditionnel présent + subjonctif' : 'Present conditional + subjunctive'), - thoughts: level === 1 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : level === 2 - ? (lang === "fr" ? 'Conditionnel présent' : 'Present conditional') - : (lang === "fr" ? 'Conditionnel passé' : 'Past conditional'), - summary: lang === "fr" ? '→ Mondes parallèles, uchronies' : '→ Parallel worlds, alternate histories', - }; - - case 19: // Imparfait Poétique / Poetic Imperfect - return { - actions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + participe présent' : 'Imperfect + present participle'), - descriptions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait + adjectifs' : 'Imperfect + adjectives') - : (lang === "fr" ? 'Imparfait + métaphores' : 'Imperfect + metaphors'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Style poétique libre' : 'Free poetic style'), - thoughts: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + subjonctif présent' : 'Imperfect + present subjunctive'), - summary: lang === "fr" ? '→ Lyrisme, poésie narrative' : '→ Lyricism, narrative poetry', - }; - - case 20: // Second Person Narrative / Narration à la deuxième personne - return { - actions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + imparfait' : 'Present + imperfect'), - descriptions: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + conditionnel présent' : 'Present + present conditional'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + style direct' : 'Present + direct style'), - thoughts: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + conditionnel présent' : 'Present + present conditional'), - summary: lang === "fr" ? '→ Immersion totale, récits interactifs' : '→ Total immersion, interactive narratives', - }; - - default: - return { - actions: level === 1 - ? (lang === "fr" ? 'Passé composé' : 'Present perfect') - : level === 2 - ? (lang === "fr" ? 'Passé simple' : 'Simple past') - : (lang === "fr" ? 'Passé simple + passé antérieur' : 'Simple past + past perfect'), - descriptions: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : (lang === "fr" ? 'Imparfait + plus-que-parfait' : 'Imperfect + pluperfect'), - dialogues: level === 1 - ? (lang === "fr" ? 'Présent' : 'Present') - : level === 2 - ? (lang === "fr" ? 'Présent' : 'Present') - : (lang === "fr" ? 'Présent + passé composé' : 'Present + present perfect'), - thoughts: level === 1 - ? (lang === "fr" ? 'Imparfait' : 'Imperfect') - : level === 2 - ? (lang === "fr" ? 'Plus-que-parfait' : 'Pluperfect') - : (lang === "fr" ? 'Plus-que-parfait + subjonctif passé' : 'Pluperfect + past subjunctive'), - summary: lang === "fr" ? '→ Narrations épurées, style classique' : '→ Clean narratives, classical style', - }; - } - } - - static getDialogueType(value: number, level: number, lang: "fr" | "en"): string { - if (lang === "fr") { - // Version française existante - if (level === 1 /* Débutant - Secondaire 5 */) { - switch (value) { - case 1: - return 'Dialogue direct - Paroles exactes (ex: "Je t\'aime !")'; - case 2: - return 'Dialogue indirect - Résumé par le narrateur (ex: Il dit qu\'il m\'aime)'; - default: - return 'Dialogue direct'; - } - } else if (level === 2 /* Intermédiaire - Collégial */) { - switch (value) { - case 1: - return 'Dialogue direct'; - case 2: - return 'Dialogue indirect'; - case 3: - return 'Dialogue mixte (ex: "Je t\'aime" dit-il, puis explique ses sentiments)'; - default: - return 'Dialogue direct'; - } - } else if (level === 3 /* Avancé - Universitaire/Littéraire */) { - switch (value) { - case 1: - return 'Dialogue direct'; - case 2: - return 'Dialogue indirect'; - case 3: - return 'Dialogue mixte'; - case 4: - return 'Monologue intérieur (ex: *Je ne peux pas le perdre...*)'; - default: - return 'Dialogue direct'; - } - } - } else if (lang === "en") { - // Version anglaise canadienne - if (level === 1 /* Beginner - Grade 12 level */) { - switch (value) { - case 1: - return 'Direct dialogue - Exact words (e.g., "I love you!")'; - case 2: - return 'Indirect dialogue - Summarized by narrator (e.g., He said that he loved me)'; - default: - return 'Direct dialogue'; - } - } else if (level === 2 /* Intermediate - College level */) { - switch (value) { - case 1: - return 'Direct dialogue'; - case 2: - return 'Indirect dialogue'; - case 3: - return 'Mixed dialogue (e.g., "I love you," he said, then explained his feelings)'; - default: - return 'Direct dialogue'; - } - } else if (level === 3 /* Advanced - University/Literary level */) { - switch (value) { - case 1: - return 'Direct dialogue'; - case 2: - return 'Indirect dialogue'; - case 3: - return 'Mixed dialogue'; - case 4: - return 'Interior monologue (e.g., *I cannot lose him...*)'; - default: - return 'Direct dialogue'; - } - } - } - - return lang === "fr" ? 'Dialogue direct' : 'Direct dialogue'; - } - static getLanguage(value: number): string { - switch (value) { - case 1: // Français Canada - return 'Français Canada'; - case 2: // Français France - return 'Français France'; - case 3: // Français Québécois - return 'Français Québécois'; - case 4: // Anglais - return 'English Canada'; - default: - return 'Français Canada'; - } - } - - static getFormat(value: number): string { - switch (value) { - case 1: // Français Canada (espace fine « texte ») - case 3: // Français Québécois - return '«dialogue» - avec guillemet - [Espace fine insécable]'; - case 2: // Français France - return '« dialogue » - avec guillemet - [Espace insécable standard]'; - case 4: // Anglais - return `"dialogue" - or with a dash - [No space]`; - default: - return 'Format : « dialogue » - avec guillemet - [Espace fine insécable]'; - } - } - - static getNarrativePerson(value: number, level: number, lang: "fr" | "en"): string { - if (level === 1) { - switch (value) { - case 1: - return lang === "fr" ? 'Première personne (Je acteur)' : 'First Person (I as actor)'; - case 3: - return lang === "fr" ? 'Troisième omnisciente - Narration globale' : 'Third Person Omniscient - Global narration'; - default: - return 'Première personne'; - } - } else if (level === 2) { - switch (value) { - case 1: - return lang === "fr" ? 'Première personne (Je acteur)' : 'First Person (I as actor)'; - case 2: - return lang === "fr" ? 'Première personne (Je témoin) - Observateur' : 'First Person (I as witness) - Observer'; - case 3: - return lang === "fr" ? 'Troisième omnisciente' : 'Third Person Omniscient'; - case 4: - return lang === "fr" ? 'Troisième limitée - Focus sur un personnage' : 'Third Person Limited - Focus on a character'; - default: - return 'Première personne'; - } - } else if (level === 3) { - switch (value) { - case 1: - return lang === "fr" ? 'Première personne (Je acteur)' : 'First Person (I as actor)'; - case 2: - return lang === "fr" ? 'Première personne (Je témoin) - Observateur' : 'First Person (I as witness) - Observer'; - case 3: - return lang === "fr" ? 'Troisième omnisciente' : 'Third Person Omniscient'; - case 4: - return lang === "fr" ? 'Troisième limitée - Focus sur un personnage' : 'Third Person Limited - Focus on a character'; - case 5: - return lang === "fr" ? 'Deuxième personne (Tu) - Immersion forte' : 'Second Person (You) - Strong immersion'; - case 6: - return lang === "fr" ? 'Nous collectif - Voix chorale' : 'We Collective - Choral voice'; - default: - return lang === "fr" ? 'Troisième omnisciente' : 'Third Person Omniscient'; - } - } - return 'Première personne'; - } - - static getStoryState(value: number): string { - switch (value) { - case 0: - return 'Continue à partir de la mise en contexte avec cela :'; - case 1: - return 'Débutons le chapitre avec :'; - case 2: - return 'Continue à partir de la mise en contexte et ferme le chapitre avec :'; - case 3: - return `Commençons le l'histoire avec :`; - case 4: - return 'Terminons l\'histoire avec :'; - default: - return 'Continue à partir de la mise en contexte avec cela :'; - } - } -} diff --git a/electron/database/models/User.ts b/electron/database/models/User.ts index 8563a3a..f3e12c2 100644 --- a/electron/database/models/User.ts +++ b/electron/database/models/User.ts @@ -1,42 +1,7 @@ -import UserRepo, { - BasicUserCredentials, - GuideTourResult, - PasswordResponse, - TOTPQuery, - UserAccountQuery, - UserAiUsageResult, - UserAPIKeyResult, - UserInfosQueryResponse, - UserQueryResponse, - UserSubscription -} from "../repositories/user.repo"; -import {FetchQueryResponse} from "../../config/SharedInterface"; -import System, {EncryptedKey, EncryptedUserKey, UserKey} from "./System"; -import {FastifyInstance} from "fastify"; -import EmailTemplate from "./EmailTemplate"; -import path from "path"; -import fs from "fs"; -import {Secret, TOTP} from "otpauth"; -import Book, {BookProps} from "./Book"; -import {JWT} from "@fastify/jwt"; -import {AIBrand} from "./AI"; -import {ConflictError, NotFoundError} from "../error"; -import {getLanguage} from "../context"; -import Subscription, { SubscriptionInfo } from "./Subscription"; - -export type UserLanguage = 'fr' | 'en'; - -export interface LoginData{ - valid:boolean; - token?:string; - id?:string; - message?:string; -} - -export interface UserAPIKey { - brand: AIBrand, - key: string; -} +import UserRepo, {UserAccountQuery, UserInfosQueryResponse} from "@/electron/database/repositories/user.repository"; +import System from "@/electron/database/System"; +import Book, {BookProps} from "@/electron/database/models/Book"; +import {getUserEncryptionKey} from "@/electron/database/keyManager"; interface UserAccount{ firstName:string; @@ -46,29 +11,10 @@ interface UserAccount{ email:string; } -export interface TOTPData{ - totpCode:string; -} - export interface GuideTour { [key: string]: boolean; } -export interface UserSubscriptionProps { - userId: string; - subType: string; - subTier: number; - startDate: string; - endDate: string; - status: number; -} - -interface UserSubscriptionInfo { - subType: string; - subTier: number; - status: boolean; -} - export default class User{ private readonly id:string; @@ -76,14 +22,9 @@ export default class User{ private lastName: string; private username: string; private email: string; - private platform: string; private accountVerified: boolean; private authorName: string; - private writingLang: number; - private writingLevel: number; - private ritePoints: number; private groupId: number; - private balancedCredits: number; private termsAccepted: boolean; constructor(id:string){ @@ -92,64 +33,30 @@ export default class User{ this.lastName = ''; this.username = ''; this.email = ''; - this.platform = ''; this.accountVerified = false; this.authorName = ''; - this.writingLang = 0; - this.writingLevel = 1; - this.ritePoints = 0; this.groupId = 0; - this.balancedCredits = 0; this.termsAccepted = false; } public async getUserInfos(): Promise { - const data: UserInfosQueryResponse = await UserRepo.fetchUserInfos(this.id); - const userKey: string = await this.getUserKey(data.user_meta); + const data: UserInfosQueryResponse = UserRepo.fetchUserInfos(this.id); + const userKey:string = getUserEncryptionKey(this.id) this.firstName = System.decryptDataWithUserKey(data.first_name, userKey); this.lastName = System.decryptDataWithUserKey(data.last_name, userKey); this.username = System.decryptDataWithUserKey(data.username, userKey); this.email = System.decryptDataWithUserKey(data.email, userKey); - this.platform = data.plateform; this.accountVerified = data.account_verified === 1; this.authorName = data.author_name ? System.decryptDataWithUserKey(data.author_name, userKey) : ''; - this.writingLang = data.writing_lang ? data.writing_lang : 1; - this.writingLevel = data.writing_level ? data.writing_level : 1; this.groupId = data.user_group ? data.user_group : 0; - this.ritePoints = data.rite_points ? data.rite_points : 0; - this.balancedCredits = data.credits_balance ? data.credits_balance : 0; this.termsAccepted = data.term_accepted === 1; } - public static async returnUserInfos(userId: string, plateforme: string) { + public static async returnUserInfos(userId: string) { const user: User = new User(userId); await user.getUserInfos(); const books: BookProps[] = await Book.getBooks(userId); - const guideTourResult: GuideTourResult[] = await UserRepo.fetchGuideTour(userId, plateforme); const guideTour: GuideTour[] = []; - const requestSubscription: UserSubscriptionProps[] = await User.getSubscription(userId); - const userSubscriptions:SubscriptionInfo = await Subscription.getSubscriptionInfo(userId); - const requestKeys: UserAPIKey[] = await User.getAPIKey(userId); - let moneySpentThisMonth: number = 0; - if (requestKeys.length > 0) { - moneySpentThisMonth = await User.moneySpentThisMonth(userId) - } - const subscriptions: UserSubscriptionInfo[] = []; - for (const userSubscriptionProp of requestSubscription) { - subscriptions.push({ - subType: userSubscriptionProp.subType, - subTier: userSubscriptionProp.subTier, - status: userSubscriptionProp.status === 1 - }); - } - - if (guideTourResult) { - guideTourResult.forEach((tour: GuideTourResult): void => { - guideTour.push({ - [tour.step_tour]: true - }); - }); - } return { id: user.getId(), name: user.getFirstName(), @@ -158,26 +65,9 @@ export default class User{ email: user.getEmail(), accountVerified: user.isAccountVerified(), authorName: user.getAuthorName(), - writingLang: user.getWritingLang(), - writingLevel: user.getWritingLevel(), - ritePoints: user.getRitePoints(), groupId: user.getGroupId(), termsAccepted: user.isTermsAccepted(), guideTour: guideTour, - subscription: subscriptions, - aiUsage: moneySpentThisMonth, - creditsBalance: userSubscriptions.totalCredits, - apiKeys: { - gemini: !!requestKeys.find((key: UserAPIKey): boolean => { - return key.brand === 'gemini' - }), - openai: !!requestKeys.find((key: UserAPIKey): boolean => { - return key.brand === 'openai' - }), - anthropic: !!requestKeys.find((key: UserAPIKey): boolean => { - return key.brand === 'anthropic' - }), - }, books: books.map((book: BookProps) => { return { bookId: book.id, @@ -188,550 +78,38 @@ export default class User{ } } - public static async isTwoFactorEnabled(userId: string): Promise { - return UserRepo.isTwoFactorEnabled(userId); - } - - public static async changePassword(userId: string, password: string, newPassword: string, email: string): Promise { - if (await User.validUser(email, password)) { - const newPasswordHash: string = await System.hashPassword(newPassword); - return await UserRepo.updatePassword(userId, System.hashElement(email), newPasswordHash); - } else { - throw new Error('Your password is incorrect'); - } - } - - public static async deleteAccount(userId: string, email: string): Promise { - return UserRepo.deleteAccount(userId, System.hashElement(email)); - } - - public static async resetPassword(newPassword: string, email: string, verifyCode: string): Promise { - const encryptPassword: string = await System.hashPassword(newPassword); - const user: PasswordResponse = await UserRepo.verifyUserAuth(System.hashElement(email), verifyCode) - const userId: string = user.user_id; - return await UserRepo.updatePassword(userId, System.hashElement(email), encryptPassword); - } - - public static async verifyCode(verifyCode: string, email: string): Promise { - return await UserRepo.fetchVerifyCode(System.hashElement(email), verifyCode); - } - - static async subscribeToBeta(email: string): Promise { - let checkEmail: string[] = email.split('@'); - checkEmail = checkEmail[1].split('.'); - if (checkEmail[0] === 'gmail' || checkEmail[0] === 'hotmail' || checkEmail[0] === 'outlook') { - if (await UserRepo.checkBetaSubscriber(email)) { - throw new Error(`L'adresse courriel est déjà enregistré.`); - } - const insertEmail: boolean = await UserRepo.insertBetaSubscriber(email); - if (insertEmail) { - System.sendEmailTo(email, 'Inscription à la bêta.', '', EmailTemplate('newsletter', {})); - return insertEmail; - } else { - throw new Error('Une erreur est survenue lors de l\'inscription à la bêta.'); - } - } else { - throw new Error('Votre adresse email n\'est pas valide.'); - } - } - - public static async addUser(firstName: string, lastName: string, username: string, email: string, notEncryptPassword: string, userId: string | null = null, provider: string = 'credential', socialId: string | null = null, tempAdd: boolean = false): Promise { + public static async addUser(userId:string,firstName: string, lastName: string, username: string, email: string, notEncryptPassword: string, lang: 'fr' | 'en' = 'fr'): Promise { const originEmail:string = System.hashElement(email); const originUsername:string = System.hashElement(username); - await this.checkUser(originUsername, originEmail); - const lang: "fr" | "en" = getLanguage() - if (tempAdd) { - const password:string = notEncryptPassword ? await System.hashPassword(notEncryptPassword) : ''; - const newUserId: string = System.createUniqueId(); - await UserRepo.insertTempUser(newUserId, firstName, lastName, username, originUsername, email, originEmail, password); - await User.sendVerifyCode(username, email); - return newUserId; - } else { - if (!userId) { - throw new Error(lang === 'fr' ? 'L\'identifiant utilisateur est requis.' : 'User ID is required.'); - } - const encryptData: EncryptedUserKey = await System.generateAndEncryptUserKey(userId); - const userKey: string = encryptData.userKey; - const encryptFirstName: string = System.encryptDataWithUserKey(firstName, userKey); - const encryptLastName: string = System.encryptDataWithUserKey(lastName, userKey); - const encryptUsername: string = System.encryptDataWithUserKey(username, userKey); - const encryptEmail: string = System.encryptDataWithUserKey(email, userKey); - const originalEmail: string = System.hashElement(email); - const originalUsername: string = System.hashElement(username); - const meta: string = System.encryptDateKey(userId); - const insertedUserId: string = await UserRepo.insertUser(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail, notEncryptPassword, meta, socialId, provider); - await System.storeUserKey(userId, encryptData.encryptedKey); - await UserRepo.deleteTempUser(userId); - return insertedUserId; - } + const userKey: string = getUserEncryptionKey(userId); + const encryptFirstName: string = System.encryptDataWithUserKey(firstName, userKey); + const encryptLastName: string = System.encryptDataWithUserKey(lastName, userKey); + const encryptUsername: string = System.encryptDataWithUserKey(username, userKey); + const encryptEmail: string = System.encryptDataWithUserKey(email, userKey); + const originalEmail: string = System.hashElement(email); + const originalUsername: string = System.hashElement(username); + return UserRepo.insertUser(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail,lang); } - public static async updateUserInfos(userKey: string, userId: string, firstName: string, lastName: string, username: string, email: string, authorName?: string): Promise { + public static async updateUserInfos(userKey: string, userId: string, firstName: string, lastName: string, username: string, email: string, authorName?: string, lang: 'fr' | 'en' = 'fr'): Promise { const encryptFirstName:string = System.encryptDataWithUserKey(firstName,userKey); const encryptLastName:string = System.encryptDataWithUserKey(lastName,userKey); const encryptUsername:string = System.encryptDataWithUserKey(username,userKey); const encryptEmail:string = System.encryptDataWithUserKey(email,userKey); const originalEmail:string = System.hashElement(email); const originalUsername:string = System.hashElement(username); - const userMeta:string = System.encryptDateKey(userId); let encryptAuthorName:string = ''; let originalAuthorName: string = ''; if (authorName){ encryptAuthorName = System.encryptDataWithUserKey(authorName,userKey); originalAuthorName = System.hashElement(authorName); } - return UserRepo.updateUserInfos(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail, userMeta, originalAuthorName, encryptAuthorName); - } - public async getUserKey(metaKey:string,isNeedToBeDecrypt:boolean=true,keys?:UserKey[]):Promise{ - let meta:string = ''; - if (isNeedToBeDecrypt){ - meta = System.decryptDateKey(this.id,metaKey); - } else { - meta = metaKey; - } - if (keys){ - return System.getClosestPreviousKey(keys,meta); - } else { - const userKey:FetchQueryResponse = await System.getUserKey(this.id,meta); - if (userKey.data){ - return userKey.data?.encrypted_key; - } else { - return ''; - } - } - } - - public static async checkUserName(username:string):Promise{ - return UserRepo.fetchUserName(username); - } - - public static async checkEmail(email:string):Promise{ - return UserRepo.fetchEmail(email); - } - - static getLevelDetail(level: number, lang: "fr" | "en"): string { - if (lang === "fr") { - switch (level) { - case 1: // Débutant français - return `**Niveau : Débutant** -**Objectif** : Texte écrit par un élève de secondaire qui apprend encore à écrire en français. -**Lexique** : -- **Vocabulaire limité** : Répétition des mêmes mots simples, manque de synonymes. -- **Exemples typiques** : "il y a", "c'est", "très", "beaucoup", "faire", "aller", "dire". -- **Caractéristiques** : Anglicismes possibles, répétitions non voulues. -**Structure** : -- **Phrases courtes par nécessité** : 8-15 mots (le français tolère plus de mots). -- **Constructions simples** : Sujet-Verbe-Complément, quelques "qui/que" basiques. -- **Coordinations basiques** : "et", "mais", "alors", "après". -- **Erreurs typiques** : "Il y a des arbres qui sont grands" au lieu de "Les arbres sont grands". -**Style littéraire** : -- **Descriptions factuelle** : "La forêt était sombre et silencieuse." -- **Pas de figures de style** : Tentatives maladroites si présentes. -- **Français scolaire** : Formulations apprises par cœur. -**Exemples comparatifs** : -✅ "Marc a couru dans la forêt. Il avait très peur des bruits qu'il entendait." -❌ "Marc s'élança dans les profondeurs sylvestres, son cœur battant la chamade..." -`; - - case 2: // Intermédiaire français - return `**Niveau : Intermédiaire** -**Objectif** : Texte écrit par un étudiant de cégep/université qui expérimente le style français. -**Lexique** : -- **Vocabulaire enrichi** : Recherche de synonymes, mots plus soutenus. -- **Exemples** : "s'élancer", "contempler", "mystérieux", "murmurer", "lueur". -- **Tentatives stylistiques** : Parfois réussies, parfois précieuses. -**Structure** : -- **Phrases moyennes à longues** : 12-25 mots, avec subordinations. -- **Constructions élaborées** : Relatives multiples, quelques inversions. -- **Style français émergent** : Tentatives de périodes, de nuances. -- **Connecteurs plus variés** : "cependant", "néanmoins", "tandis que". -**Style littéraire** : -- **Premières métaphores** : Simples mais françaises ("tel un fantôme"). -- **Descriptions étoffées** : Adjectifs et compléments circonstanciels. -- **Registre soutenu recherché** : Parfois forcé. -**Exemples comparatifs** : -✅ "Marc s'élança dans la forêt obscure, tandis que chaque bruissement mystérieux faisait naître en lui une crainte sourde." -❌ "Marc se précipita avec véhémence dans les méandres ténébreux de la sylve ancestrale..." -`; - - case 3: // Avancé français - return `**Niveau : Avancé** -**Objectif** : Texte écrit par un écrivain professionnel maîtrisant la rhétorique française. -**Lexique** : -- **Richesse lexicale maîtrisée** : Précision du mot juste, nuances subtiles. -- **Registres variés** : Du familier au soutenu selon l'effet recherché. -- **Synonymie élégante** : Évitement des répétitions par art, non par contrainte. -**Structure** : -- **Périodes françaises** : Phrases longues et rythmées (20-40 mots possibles). -- **Architecture complexe** : Subordinations enchâssées, incises, inversions maîtrisées. -- **Rythme classique** : Alternance entre brièveté saisissante et amplitude oratoire. -- **Ponctuation expressive** : Points-virgules, deux-points, parenthèses. -**Style littéraire** : -- **Figures maîtrisées** : Métaphores filées, antithèses, chiasmes. -- **Tradition française** : Élégance, clarté, harmonie des sonorités. -- **Sous-entendus et allusions** : Subtilité dans l'évocation. -**Exemples comparatifs** : -✅ "Marc courut." (Simplicité voulue) -✅ "Dans l'entrelacement des ombres que la lune, filtrant à travers la ramure, dessinait sur le sol moussu, Marc discernait les échos de sa propre terreur ; terreur ancestrale, née de ce dialogue éternel entre l'homme et la nuit." -`; - default: - return ``; - } - } else { // English Canadian - switch (level) { - case 1: // Beginner English - return `**Level: Beginner** -**Target**: Text written by a high school student still learning English composition. -**Vocabulary**: -- **Limited word choice**: Repetition of basic words, simple alternatives. -- **Typical examples**: "said", "went", "got", "thing", "stuff", "really", "very". -- **Characteristics**: Overuse of "and then", simple connectors. -**Structure**: -- **Short, choppy sentences**: 5-10 words (English naturally shorter). -- **Basic constructions**: Subject-Verb-Object, simple coordination. -- **Limited variety**: Mostly declarative sentences. -- **Common errors**: Run-on sentences with "and", fragments. -**Literary style**: -- **Plain description**: "The forest was dark. It was scary." -- **No literary devices**: Attempts at metaphors usually failed. -- **Direct emotion**: "He was afraid" (English directness). -**Comparative examples**: -✅ "Mark ran into the forest. He was scared. The trees looked big and dark." -❌ "Mark ventured forth into the sylvan depths, his heart pounding with trepidation..." -`; - - case 2: // Intermediate English - return `**Level: Intermediate** -**Target**: Text written by a college student developing English writing skills. -**Vocabulary**: -- **Expanded vocabulary**: Some sophisticated words, better word choice. -- **Examples**: "ventured", "glimpsed", "mysterious", "whispered", "shadowy". -- **Characteristics**: Occasional overreach, some pretentious word choices. -**Structure**: -- **Varied sentence length**: 8-18 words, some complex sentences. -- **Better flow**: Proper use of conjunctions, some subordination. -- **Paragraph development**: Topic sentences, basic transitions. -- **English rhythm**: Shorter than French but with variation. -**Literary style**: -- **Simple imagery**: Basic metaphors and similes that work. -- **Show don't tell**: Attempts at indirect description. -- **English conciseness**: Getting to the point while adding style. -**Comparative examples**: -✅ "Mark plunged into the dark forest. Every sound made him jump, like whispers from invisible watchers." -❌ "Mark precipitously advanced into the tenebrous woodland whilst phantasmagorical emanations..." -`; - - case 3: // Advanced English - return `**Level: Advanced** -**Target**: Text written by a professional English-speaking author. -**Vocabulary**: -- **Precise word choice**: Economy of language, powerful verbs. -- **Understated elegance**: Sophisticated but not showy. -- **Anglo-Saxon vs. Latin**: Strategic use of both registers. -**Structure**: -- **Masterful variety**: From punchy fragments to flowing periods. -- **English rhythm**: Natural speech patterns, emphasis through structure. -- **Controlled complexity**: Never convoluted, always clear. -- **Strategic brevity**: Power in conciseness. -**Literary style**: -- **Subtle imagery**: Metaphors that illuminate, don't decorate. -- **Understated power**: English preference for implication over elaboration. -- **Voice and tone**: Distinctive style through restraint and precision. -- **Anglo tradition**: Clarity, wit, emotional resonance through simplicity. -**Comparative examples**: -✅ "Mark ran. The forest swallowed him." (English power in brevity) -✅ "The shadows moved between the trees like living things, and Mark felt something ancient watching from the darkness—something that had been waiting." (Controlled complexity) -`; - default: - return ``; - } - } - } - - static getAudienceDetail(level: number, lang: "fr" | "en"): string { - if (lang === "fr") { - switch (level) { - case 1: // Débutant - return `**Niveau : Débutant** - **Objectif** : Texte accessible à un collégien (18-22 ans). - **Lexique** : - - **Priorité absolue** : Mots du quotidien, fréquents dans la langue parlée. - - **Exemples autorisés** : "marcher", "voir", "dire", "beau", "peur", "lumière". - - **Mots à éviter** : "luminescence", "éthéré", "volutes", "chuchotements", "centenaires". - **Structure** : - - **Longueur moyenne maximale par phrase** : 10 mots. - - **Interdit** : Phrases composées, subordonnées, ou parenthèses. - - **Style des descriptions** : Directes et visuelles (ex: "La forêt était sombre" → pas de "La forêt respirait la nuit"). - **Style littéraire** : - - **Interdits** : Métaphores, similes, ou figures de style. - - **Priorité** : Clarté et simplicité absolue. - **Exemples comparatifs** : - ❌ "La brume enveloppait les arbres avec une grâce éthérée" → ✅ "La brume couvrait les arbres." - ❌ "Elle courait, les jambes tremblantes, vers la lumière" → ✅ "Elle courait vers une lumière." - `; - - case 2: // Intermédiaire - return ` - **Niveau : Intermédiaire** - **Objectif** : Texte pour un étudiant universitaire (22-30 ans). - **Lexique** : - - **Priorité** : Vocabulaire varié mais compréhensible (ex: "murmurer", "luminescent", "mystère", "sentinelles"). - - **Mots à éviter** : Termes littéraires complexes (ex: "volutes", "centenaires", "éthéré"). - **Structure** : - - **Longueur moyenne maximale par phrase** : 18 mots. - - **Autorisé** : Phrases composées simples (ex: "Elle courait, mais la forêt était dense"). - - **Descriptions** : Métaphores basiques (ex: "La forêt respirait la nuit"). - **Style littéraire** : - - **Autorisé** : Similes simples (ex: "comme un fantôme"). - - **Interdit** : Figures de style complexes (antithèses, anaphores). - **Exemples comparatifs** : - ❌ "Les arbres, semblables à des sentinelles de pierre, veillaient" → ✅ "Les arbres ressemblaient à des gardiens." - ❌ "La lueur dansante évoquait un espoir éphémère" → ✅ "La lumière clignotait comme un espoir." - `; - - case 3: // Avancé - return ` - **Niveau : Avancé** - **Objectif** : Texte littéraire d'un professionnel littéraire, ayant un diplôme universitaire. - **Lexique** : - - **Priorité** : Vocabulaire riche et littéraire (ex: "volutes", "centenaires", "éthéré", "luminescence"). - - **Mots à éviter** : Termes simples jugés "banals" (ex: "marcher" → préférer "arpenter", "errer"). - **Structure** : - - **Longueur maximale par phrase** : aucune limite. - - **Autorisé** : Phrases complexes et enchaînées (ex: "Parmi les ombres dansantes, elle discerna une lueur vacillante, symbole de l'espoir qui s'évanouissait"). - - **Descriptions** : Métaphores poétiques et figures de style avancées. - **Style littéraire** : - - **Priorité** : Atmosphère immersive et langage élégant. - - **Autorisé** : Antithèses, anaphores, et métaphores multi-niveaux. - **Exemples comparatifs** : - ✅ "Les volutes de brume, tels des spectres en quête d'écho, enveloppaient les troncs centenaires dans un ballet spectral." - ✅ "Luminescence vacillante, espoir éphémère : les deux dansaient une dernière valse avant l'obscurité." - `; - default: - return ``; - } - } else { // English Canadian - switch (level) { - case 1: // Beginner (Grade 11/Secondary 5 level) - return ` - **Level: Beginner** - **Target**: Text accessible to a Grade 11 student (Secondary 5 equivalent). - **Vocabulary**: - - **Absolute priority**: Common everyday words, frequent in spoken language. - - **Allowed examples**: "walk", "see", "say", "nice", "scared", "light". - - **Words to avoid**: "luminescence", "ethereal", "tendrils", "whispers", "ancient". - **Structure**: - - **Maximum average sentence length**: 10 words. - - **Forbidden**: Compound sentences, subordinate clauses, or parentheses. - - **Description style**: Direct and visual (e.g., "The forest was dark" → not "The forest breathed with darkness"). - **Literary style**: - - **Forbidden**: Metaphors, similes, or figures of speech. - - **Priority**: Absolute clarity and simplicity. - **Comparative examples**: - ❌ "The mist enveloped the trees with ethereal grace" → ✅ "The mist covered the trees." - ❌ "She ran, her legs trembling, toward the light" → ✅ "She ran toward a light." - `; - - case 2: // Intermediate (CEGEP/College level) - return ` - **Level: Intermediate** - **Target**: Text for a CEGEP/College student. - **Vocabulary**: - - **Priority**: Varied but understandable vocabulary (e.g., "murmur", "luminous", "mystery", "sentinels"). - - **Words to avoid**: Complex literary terms (e.g., "tendrils", "ancient", "ethereal"). - **Structure**: - - **Maximum average sentence length**: 18 words. - - **Allowed**: Simple compound sentences (e.g., "She ran, but the forest was thick"). - - **Descriptions**: Basic metaphors (e.g., "The forest breathed in the night"). - **Literary style**: - - **Allowed**: Simple similes (e.g., "like a ghost"). - - **Forbidden**: Complex figures of speech (antithesis, anaphora). - **Comparative examples**: - ❌ "The trees, like stone sentinels, stood watch" → ✅ "The trees looked like guardians." - ❌ "The dancing light evoked fleeting hope" → ✅ "The light flickered like hope." - `; - - case 3: // Advanced (University/Professional writer level) - return ` - **Level: Advanced** - **Target**: Literary text from a professional writer or bestselling author with university education. - **Vocabulary**: - - **Priority**: Rich and literary vocabulary (e.g., "tendrils", "ancient", "ethereal", "luminescence"). - - **Words to avoid**: Simple terms deemed "mundane" (e.g., "walk" → prefer "traverse", "wander"). - **Structure**: - - **Maximum sentence length**: No limit. - - **Allowed**: Complex and chained sentences (e.g., "Among the dancing shadows, she discerned a flickering light, a symbol of hope that was fading away"). - - **Descriptions**: Poetic metaphors and advanced figures of speech. - **Literary style**: - - **Priority**: Immersive atmosphere and elegant language. - - **Allowed**: Antithesis, anaphora, and multi-layered metaphors. - **Comparative examples**: - ✅ "Tendrils of mist, like spectres seeking echo, enveloped the ancient trunks in a ghostly ballet." - ✅ "Flickering luminescence, fleeting hope: both danced a final waltz before darkness fell." - `; - default: - return ``; - } - } - } - - public static async checkUser(username: string, email: string): Promise { - const validUsername:boolean = await User.checkUserName(username); - const validEmail:boolean = await User.checkEmail(email); - const lang: "fr" | "en" = getLanguage() - if (validUsername){ - throw new ConflictError(lang === 'fr' ? "Le nom d'utilisateur est déjà utilisé." : 'Username is already taken.'); - } - if (validEmail){ - throw new ConflictError(lang === 'fr' ? "L'adresse courriel est déjà utilisée." : 'Email address is already in use.'); - } - } - - public static async login(email: string, password: string, jwtInstance: FastifyInstance): Promise { - const lang: "fr" | "en" = getLanguage() - if (await User.validUser(email, password)) { - const user: BasicUserCredentials = await UserRepo.fetchUserByCredentials(System.hashElement(email)); - - return jwtInstance.jwt.sign({userId: user.user_id}) - } else { - throw new NotFoundError(lang === 'fr' ? "Nom d'utilisateur ou mot de passe invalide." : "Invalid username or password."); - } - } - - public static async setUserAccountInformation(userId: string, authorName: string, username: string): Promise { - const user:User = new User(userId); - await user.getUserInfos(); - const meta:string = System.encryptDateKey(userId); - const userKey:string = await user.getUserKey(meta); - return User.updateUserInfos(userKey, userId, user.firstName, user.lastName, username, user.getEmail(), authorName); - } - - public static async validUser(email:string,password:string):Promise{ - const validUser: PasswordResponse | null = await UserRepo.verifyUserAuth(System.hashElement(email)); - if (!validUser) { - return false; - } - const stockedPassword: string = validUser.password; - return System.verifyPassword(stockedPassword, password); - } - - public static async socialLogin(firstName: string, lastName: string, email: string | null, socialId: string, provider: string, jwtInstance: JWT): Promise { - const validUserId: string | null = await UserRepo.fetchBySocialId(socialId); - let userId: string = ''; - let newEmail: string = ''; - if (email === null) { - newEmail = socialId + '@' + provider + '.com'; - } else { - newEmail = email; - } - if (!validUserId) { - const emailValidationUserId: string | null = await UserRepo.fetchByEmail(System.hashElement(newEmail)); - if (!emailValidationUserId) { - const randomNum: number = Math.floor(Math.random() * 10000); - const fourDigitNum: string = randomNum.toString().padStart(4, '0'); - const username: string = newEmail.split('@')[0] + fourDigitNum; - userId = System.createUniqueId(); - await this.addUser(firstName, lastName, username, newEmail, "", userId, provider, socialId); - } else { - userId = emailValidationUserId; - await UserRepo.updateSocialId(userId, socialId, provider); - } - } else { - userId = validUserId; - } - - return jwtInstance.sign({userId: userId}) - } - - public static async confirmAccount(verifyCode: string, email: string): Promise { - await UserRepo.fetchVerifyCode(System.hashElement(email), verifyCode); - const account: UserQueryResponse = await UserRepo.fetchTempAccount(email, verifyCode); - const userId: string = account.user_id; - const firstName: string = account.first_name; - const lastName: string = account.last_name; - const username: string = account.username; - const emailAddr: string = account.email; - const password: string = account.password; - const insertUser: string = await this.addUser(firstName, lastName, username, emailAddr, password, userId); - const projectDirectory: string = path.join(process.cwd(), 'uploads', userId.toString(), 'cover'); - - if (!insertUser) { - throw new Error('Une erreur est survenue lors de la confirmation du compte.'); - } - await fs.promises.mkdir(projectDirectory, {recursive: true}) - .catch(error => console.error(`Erreur lors de la création du dossier: ${error}`)); - return true; - } - - public static async sendVerifyCode(username: string, email: string): Promise { - const verifyCode: string = System.createVerifyCode(); - const validUpdate: boolean = await UserRepo.updateVerifyCode(username, username === "" ? System.hashElement(email) : email, verifyCode); - if (validUpdate) { - const options = {code: verifyCode} - System.sendEmailTo(email, 'Votre code de vérification', '', EmailTemplate('verify-code', options)); - } else { - throw new Error('Une erreur est survenue lors de l\'envoi du code de vérification'); - } - } - public static validateTOTPCode(code: string, token: string): boolean { - try { - const totp = new TOTP({ - secret: Secret.fromBase32(code) - }); - - const isValid = totp.validate({ - token, - window: 1 - }); - return isValid !== null; - } catch (error) { - return false; - } - } - - public static async checkTOTPExist(userId:string,email:string):Promise{ - return UserRepo.checkTOTPExist(userId,email); - } - - public static async handleTOTP(userId: string, email: string, verifyCode: string): Promise { - const user = new User(userId); - const meta:string = System.encryptDateKey(userId); - const userKey:string = await user.getUserKey(meta); - const encryptedCode:string = System.encryptDataWithUserKey(verifyCode,userKey); - const encryptedEmail:string = System.hashElement(email); - return UserRepo.insertTOTP(encryptedCode,encryptedEmail,userId,meta,true); - } - - public static async activateTOTP(userId: string, email: string, token: string): Promise { - const data: TOTPQuery = await UserRepo.fetchTempTOTP(userId, System.hashElement(email)); - const user = new User(userId); - const meta: string = data.totp_meta; - const userKey: string = await user.getUserKey(meta); - const code: string = System.decryptDataWithUserKey(data.totp_code, userKey); - if (User.validateTOTPCode(code, token)) { - return UserRepo.insertTOTP(data.totp_code, data.email, userId, meta, false); - } else { - throw new Error('Your token is incorrect'); - } - } - - public static async validateTOTP(userId: string, email: string, token: string): Promise { - const data: TOTPQuery = await UserRepo.fetchTOTP(userId, System.hashElement(email)); - const user = new User(userId); - const meta: string = data.totp_meta; - const userKey: string = await user.getUserKey(meta); - const code: string = System.decryptDataWithUserKey(data.totp_code, userKey); - if (User.validateTOTPCode(code, token)) { - return true; - } else { - throw new Error('Your token is incorrect'); - } + return UserRepo.updateUserInfos(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail, originalAuthorName, encryptAuthorName, lang); } public static async getUserAccountInformation(userId: string): Promise { - const user:User = new User(userId); - const data: UserAccountQuery = await UserRepo.fetchAccountInformation(userId); - const userKey: string = await user.getUserKey(data.user_meta); + const data: UserAccountQuery = UserRepo.fetchAccountInformation(userId); + const userKey:string = getUserEncryptionKey(userId) const userName: string = data.first_name ? System.decryptDataWithUserKey(data.first_name, userKey) : ''; const lastName: string = data.last_name ? System.decryptDataWithUserKey(data.last_name, userKey) : ''; const username: string = data.username ? System.decryptDataWithUserKey(data.username, userKey) : ''; @@ -746,48 +124,6 @@ export default class User{ }; } - public static async getSubscription(userId: string): Promise { - const result: UserSubscription[] = await UserRepo.fetchSubscription(userId); - const userSubscription: UserSubscriptionProps[] = []; - for (const sub of result) { - userSubscription.push({ - userId: sub.user_id, - subType: sub.sub_type, - subTier: sub.sub_tier, - startDate: sub.start_date, - endDate: sub.end_date, - status: sub.status - }) - } - return userSubscription; - } - - public async convertTimeToPoint(time:number):Promise{ - const points:number = Math.ceil(time/30); - await UserRepo.updatePoints(points,this.id); - } - - static async setUserPreferences(userId: string, writingLang: number, writingLevel: number): Promise { - if (writingLang === 0 || writingLevel === 0) { - throw new Error('Veuillez choisir une langue et un niveau de rédaction valides.'); - } - if (await UserRepo.isUserPreferencesExist(userId)) { - return UserRepo.updateUserPreference(userId, writingLang, writingLevel) - } else { - const preferenceId: string = System.createUniqueId(); - return await UserRepo.insertUserPreference(preferenceId, userId, writingLang, writingLevel); - } - } - - static async acceptTerms(userId: string, version: string): Promise { - const log: boolean = await UserRepo.logAcceptTerms(userId, version); - if (log) { - return await UserRepo.updateTermsAccepted(userId); - } else { - return false; - } - } - public getId(): string { return this.id; } @@ -812,18 +148,6 @@ export default class User{ return this.accountVerified; } - public getWritingLang(): number { - return this.writingLang; - } - - public getWritingLevel(): number { - return this.writingLevel; - } - - public getRitePoints(): number { - return this.ritePoints; - } - public isTermsAccepted(): boolean { return this.termsAccepted; } @@ -832,54 +156,7 @@ export default class User{ return this.groupId; } - public getBalancedCredits(): number { - return this.balancedCredits; - } - public getAuthorName(): string { return this.authorName; } - - static async updateAIUserKey(userId: string, anthropicKey: string | null, openaiKey: string | null, geminiKey: string | null): Promise { - const user = new User(userId); - const meta: string = System.encryptDateKey(userId); - const userKey: string = await user.getUserKey(meta); - const groupedKey: { brand: 'anthropic' | 'openai' | 'gemini', key: string }[] = []; - const anthropicKeyEncrypted: string = anthropicKey ? System.encryptDataWithUserKey(anthropicKey, userKey) : ''; - groupedKey.push({brand: 'anthropic', key: anthropicKeyEncrypted}); - const openaiKeyEncrypted: string = openaiKey ? System.encryptDataWithUserKey(openaiKey, userKey) : ''; - groupedKey.push({brand: 'openai', key: openaiKeyEncrypted}); - const geminiKeyEncrypted: string = geminiKey ? System.encryptDataWithUserKey(geminiKey, userKey) : ''; - groupedKey.push({brand: 'gemini', key: geminiKeyEncrypted}); - for (const key of groupedKey) { - const updateAI: boolean = await UserRepo.updateAIUserKey(userId, key.brand, key.key); - if (!updateAI) { - return false; - } - } - return true; - } - - static async getAPIKey(userId: string): Promise { - const user: User = new User(userId); - const meta: string = System.encryptDateKey(userId); - const userKey: string = await user.getUserKey(meta); - const apiKeys: UserAPIKeyResult[] = await UserRepo.fetchAPIKey(userId); - const decryptKeys: UserAPIKey[] = []; - for (const apiKey of apiKeys) { - if (apiKey.key && apiKey.key.length > 0) { - decryptKeys.push({ - brand: apiKey.brand, - key: System.decryptDataWithUserKey(apiKey.key, userKey) - }) - } - } - return decryptKeys; - } - - private static async moneySpentThisMonth(userId: string): Promise { - const {last, first} = System.getMonthBounds(); - const usage: UserAiUsageResult = await UserRepo.fetchUserAiUsage(userId, first, last); - return usage.total_price; - } } diff --git a/electron/database/repositories/book.repository.ts b/electron/database/repositories/book.repository.ts index c262efd..805e2e3 100644 --- a/electron/database/repositories/book.repository.ts +++ b/electron/database/repositories/book.repository.ts @@ -97,7 +97,6 @@ export interface WorldElementValue { name: string; description: string; type: number; - meta: string; } export interface GuideLineAIQuery extends Record { @@ -329,10 +328,10 @@ export default class BookRepo { } } - public static updateActSummary(userId: string, bookId: string, actId: number, summary: string, metaActs: string, lang: 'fr' | 'en'): boolean { + public static updateActSummary(userId: string, bookId: string, actId: number, summary: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_act_summaries SET summary=?, meta_acts=? WHERE user_id=? AND book_id=? AND act_sum_id=?', [summary, metaActs, userId, bookId, actId]); + const result: RunResult = db.run('UPDATE book_act_summaries SET summary=? WHERE user_id=? AND book_id=? AND act_sum_id=?', [summary, userId, bookId, actId]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { @@ -421,14 +420,14 @@ export default class BookRepo { } } - public static insertNewIssue(issueId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, metaIssue: string, lang: 'fr' | 'en'): string { + public static insertNewIssue(issueId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string { try { const db: Database = System.getDb(); const result = db.get('SELECT issue_id FROM book_issues WHERE hashed_issue_name=? AND book_id=? AND author_id=?', [hashedName, bookId, userId]); if (result !== null) { throw new Error(lang === 'fr' ? `La problématique existe déjà.` : `This issue already exists.`); } - const insertResult: RunResult = db.run('INSERT INTO book_issues (issue_id,author_id, book_id, name, hashed_issue_name, meta_issue) VALUES (?,?,?,?,?,?)', [issueId, userId, bookId, encryptedName, hashedName, metaIssue]); + const insertResult: RunResult = db.run('INSERT INTO book_issues (issue_id,author_id, book_id, name, hashed_issue_name) VALUES (?,?,?,?,?)', [issueId, userId, bookId, encryptedName, hashedName]); if (insertResult.changes > 0) { return issueId; } else { @@ -461,10 +460,10 @@ export default class BookRepo { } } - public static updateIncident(userId: string, bookId: string, incidentId: string, encryptedIncidentName: string, incidentHashedName: string, incidentSummary: string, meta: string, lang: 'fr' | 'en'): boolean { + public static updateIncident(userId: string, bookId: string, incidentId: string, encryptedIncidentName: string, incidentHashedName: string, incidentSummary: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_incidents SET title=?, hashed_title=?, summary=?, meta_incident=? WHERE author_id=? AND book_id=? AND incident_id=?', [encryptedIncidentName, incidentHashedName, incidentSummary, meta, userId, bookId, incidentId]); + const result: RunResult = db.run('UPDATE book_incidents SET title=?, hashed_title=?, summary=? WHERE author_id=? AND book_id=? AND incident_id=?', [encryptedIncidentName, incidentHashedName, incidentSummary, userId, bookId, incidentId]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { @@ -477,10 +476,10 @@ export default class BookRepo { } } - public static updatePlotPoint(userId: string, bookId: string, plotPointId: string, encryptedPlotPointName: string, plotPointHashedName: string, plotPointSummary: string, meta: string, lang: 'fr' | 'en'): boolean { + public static updatePlotPoint(userId: string, bookId: string, plotPointId: string, encryptedPlotPointName: string, plotPointHashedName: string, plotPointSummary: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_plot_points SET title=?, hashed_title=?, summary=?, meta_plot=? WHERE author_id=? AND book_id=? AND plot_point_id=?', [encryptedPlotPointName, plotPointHashedName, plotPointSummary, meta, userId, bookId, plotPointId]); + const result: RunResult = db.run('UPDATE book_plot_points SET title=?, hashed_title=?, summary=? WHERE author_id=? AND book_id=? AND plot_point_id=?', [encryptedPlotPointName, plotPointHashedName, plotPointSummary, userId, bookId, plotPointId]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { @@ -508,10 +507,10 @@ export default class BookRepo { } } - public static insertNewWorld(worldId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, meta: string, lang: 'fr' | 'en'): string { + public static insertNewWorld(worldId: string, userId: string, bookId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string { try { const db: Database = System.getDb(); - const result: RunResult = db.run('INSERT INTO book_world (world_id,author_id, book_id, name, hashed_name, meta_world) VALUES (?,?,?,?,?,?)', [worldId, userId, bookId, encryptedName, hashedName, meta]); + const result: RunResult = db.run('INSERT INTO book_world (world_id,author_id, book_id, name, hashed_name) VALUES (?,?,?,?,?)', [worldId, userId, bookId, encryptedName, hashedName]); if (result.changes > 0) { return worldId; } else { @@ -542,10 +541,10 @@ export default class BookRepo { } } - public static updateWorld(userId: string, worldId: string, encryptName: string, hashedName: string, encryptHistory: string, encryptPolitics: string, encryptEconomy: string, encryptReligion: string, encryptLanguages: string, meta: string, lang: 'fr' | 'en'): boolean { + public static updateWorld(userId: string, worldId: string, encryptName: string, hashedName: string, encryptHistory: string, encryptPolitics: string, encryptEconomy: string, encryptReligion: string, encryptLanguages: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_world SET name=?, hashed_name=?, history=?, politics=?, economy=?, religion=?, languages=?, meta_world=? WHERE author_id=? AND world_id=?', [encryptName, hashedName, encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, meta, userId, worldId]); + const result: RunResult = db.run('UPDATE book_world SET name=?, hashed_name=?, history=?, politics=?, economy=?, religion=?, languages=? WHERE author_id=? AND world_id=?', [encryptName, hashedName, encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, userId, worldId]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { @@ -562,7 +561,7 @@ export default class BookRepo { try { const db: Database = System.getDb(); for (const element of elements) { - const result: RunResult = db.run('UPDATE book_world_elements SET name=?, description=?, element_type=?, meta_element=? WHERE user_id=? AND element_id=?', [element.name, element.description, element.type, element.meta, userId, element.id]); + const result: RunResult = db.run('UPDATE book_world_elements SET name=?, description=?, element_type=? WHERE user_id=? AND element_id=?', [element.name, element.description, element.type, userId, element.id]); if (result.changes <= 0) { return false; } @@ -595,10 +594,10 @@ export default class BookRepo { } } - public static insertNewElement(userId: string, elementId: string, elementType: number, worldId: string, encryptedName: string, hashedName: string, meta: string, lang: 'fr' | 'en'): string { + public static insertNewElement(userId: string, elementId: string, elementType: number, worldId: string, encryptedName: string, hashedName: string, lang: 'fr' | 'en'): string { try { const db: Database = System.getDb(); - const result: RunResult = db.run('INSERT INTO book_world_elements (element_id,world_id,user_id, name, original_name, element_type, meta_element) VALUES (?,?,?,?,?,?,?)', [elementId, worldId, userId, encryptedName, hashedName, elementType, meta]); + const result: RunResult = db.run('INSERT INTO book_world_elements (element_id,world_id,user_id, name, original_name, element_type) VALUES (?,?,?,?,?,?)', [elementId, worldId, userId, encryptedName, hashedName, elementType]); if (result.changes > 0) { return elementId; } else { @@ -647,14 +646,14 @@ export default class BookRepo { } } - static insertAIGuideLine(userId: string, bookId: string, narrativeType: number, dialogueType: number, encryptedPlotSummary: string, encryptedToneAtmosphere: string, verbTense: number, language: number, encryptedThemes: string, metaGuideLine: string, lang: 'fr' | 'en'): boolean { + static insertAIGuideLine(userId: string, bookId: string, narrativeType: number, dialogueType: number, encryptedPlotSummary: string, encryptedToneAtmosphere: string, verbTense: number, language: number, encryptedThemes: string, lang: 'fr' | 'en'): boolean { try { const db: Database = System.getDb(); - let result: RunResult = db.run('UPDATE book_ai_guide_line SET narrative_type=?, dialogue_type=?, global_resume=?, atmosphere=?, verbe_tense=?, langue=?, themes=?, meta=? WHERE user_id=? AND book_id=?', [narrativeType ? narrativeType : null, dialogueType ? dialogueType : null, encryptedPlotSummary, encryptedToneAtmosphere, verbTense ? verbTense : null, language ? language : null, encryptedThemes, metaGuideLine, userId, bookId]); + let result: RunResult = db.run('UPDATE book_ai_guide_line SET narrative_type=?, dialogue_type=?, global_resume=?, atmosphere=?, verbe_tense=?, langue=?, themes=? WHERE user_id=? AND book_id=?', [narrativeType ? narrativeType : null, dialogueType ? dialogueType : null, encryptedPlotSummary, encryptedToneAtmosphere, verbTense ? verbTense : null, language ? language : null, encryptedThemes, userId, bookId]); if (result.changes > 0) { return true; } else { - result = db.run('INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume, meta) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [userId, bookId, encryptedPlotSummary, encryptedThemes, verbTense ? verbTense : null, narrativeType ? narrativeType : null, language ? language : null, dialogueType ? dialogueType : null, encryptedToneAtmosphere, encryptedToneAtmosphere, encryptedPlotSummary, metaGuideLine]); + result = db.run('INSERT INTO book_ai_guide_line (user_id, book_id, global_resume, themes, verbe_tense, narrative_type, langue, dialogue_type, tone, atmosphere, current_resume) VALUES (?,?,?,?,?,?,?,?,?,?,?)', [userId, bookId, encryptedPlotSummary, encryptedThemes, verbTense ? verbTense : null, narrativeType ? narrativeType : null, language ? language : null, dialogueType ? dialogueType : null, encryptedToneAtmosphere, encryptedToneAtmosphere, encryptedPlotSummary]); return result.changes > 0; } } catch (e: unknown) { @@ -687,10 +686,10 @@ export default class BookRepo { } } - static insertActSummary(actSummaryId: string, userId: string, bookId: string, actId: number, actSummary: string, meta: string, lang: 'fr' | 'en'): string { + static insertActSummary(actSummaryId: string, userId: string, bookId: string, actId: number, actSummary: string, lang: 'fr' | 'en'): string { try { const db: Database = System.getDb(); - const result: RunResult = db.run('INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary, meta_acts) VALUES (?,?,?,?,?,?)', [actSummaryId, bookId, userId, actId, actSummary, meta]); + const result: RunResult = db.run('INSERT INTO book_act_summaries (act_sum_id, book_id, user_id, act_index, summary) VALUES (?,?,?,?,?)', [actSummaryId, bookId, userId, actId, actSummary]); if (result.changes > 0) { return actSummaryId; } else { diff --git a/electron/database/repositories/chapter.repository.ts b/electron/database/repositories/chapter.repository.ts index 3799c76..5b6597f 100644 --- a/electron/database/repositories/chapter.repository.ts +++ b/electron/database/repositories/chapter.repository.ts @@ -227,7 +227,7 @@ export default class ChapterRepo{ public static updateChapter(userId: string, chapterId: string, encryptedTitle: string, hashTitle: string, chapterOrder: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=?, meta_chapter=? WHERE author_id=? AND chapter_id=?', [encryptedTitle, hashTitle, chapterOrder, userId, chapterId]); + const result: RunResult = db.run('UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=? WHERE author_id=? AND chapter_id=?', [encryptedTitle, hashTitle, chapterOrder, userId, chapterId]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { @@ -240,17 +240,16 @@ export default class ChapterRepo{ } } - public static updateChapterInfos(userId: string, chapterId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, summary: string, goal: string | null, meta: string, lang: 'fr' | 'en' = 'fr'): boolean { + public static updateChapterInfos(userId: string, chapterId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, summary: string, goal: string | null, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); let sql: string = `UPDATE book_chapter_infos SET summary=?, - goal=?, - meta_chapter_info=? + goal=? WHERE chapter_id = ? AND act_id = ? AND book_id = ?`; - const params: any[] = [summary, goal, meta, chapterId, actId, bookId]; + const params: any[] = [summary, goal, chapterId, actId, bookId]; if (incidentId) { sql += ` AND incident_id=?`; params.push(incidentId); @@ -278,15 +277,15 @@ export default class ChapterRepo{ } } - public static updateChapterContent(userId: string, chapterId: string, version: number, encryptContent: string, wordsCount: number, meta: string, lang: 'fr' | 'en' = 'fr'): boolean { + public static updateChapterContent(userId: string, chapterId: string, version: number, encryptContent: string, wordsCount: number, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_chapter_content SET content=?, meta_chapter_content=?, words_count=? WHERE chapter_id=? AND author_id=? AND version=?', [encryptContent, meta, wordsCount, chapterId, userId, version]); + const result: RunResult = db.run('UPDATE book_chapter_content SET content=?, words_count=? WHERE chapter_id=? AND author_id=? AND version=?', [encryptContent, wordsCount, chapterId, userId, version]); if (result.changes > 0) { return true; } else { const contentId:string = System.createUniqueId(); - const insertResult: RunResult = db.run('INSERT INTO book_chapter_content (content_id,chapter_id, author_id, version, content, words_count, meta_chapter_content) VALUES (?,?,?,?,?,?,?)', [contentId, chapterId, userId, version, encryptContent, wordsCount, meta]); + const insertResult: RunResult = db.run('INSERT INTO book_chapter_content (content_id,chapter_id, author_id, version, content, words_count) VALUES (?,?,?,?,?,?)', [contentId, chapterId, userId, version, encryptContent, wordsCount]); return insertResult.changes > 0; } } catch (e: unknown) { diff --git a/electron/database/repositories/character.repository.ts b/electron/database/repositories/character.repository.ts index df65d5d..ab35f98 100644 --- a/electron/database/repositories/character.repository.ts +++ b/electron/database/repositories/character.repository.ts @@ -54,11 +54,11 @@ export default class CharacterRepo { return result; } - public static addNewCharacter(userId: string, characterId: string, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, bookId: string, meta: string, lang: 'fr' | 'en' = 'fr'): string { + public static addNewCharacter(userId: string, characterId: string, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, bookId: string, lang: 'fr' | 'en' = 'fr'): string { let result: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO `book_characters` (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, char_meta) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, meta]); + result = db.run('INSERT INTO `book_characters` (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history) VALUES (?,?,?,?,?,?,?,?,?,?,?)', [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory]); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); @@ -75,11 +75,11 @@ export default class CharacterRepo { } } - static insertAttribute(attributeId: string, characterId: string, userId: string, type: string, name: string, meta: string, lang: 'fr' | 'en' = 'fr'): string { + static insertAttribute(attributeId: string, characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string { let result: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value,attr_meta) VALUES (?,?,?,?,?,?)', [attributeId, characterId, userId, type, name, meta]); + result = db.run('INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value) VALUES (?,?,?,?,?)', [attributeId, characterId, userId, type, name]); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); @@ -96,10 +96,10 @@ export default class CharacterRepo { } } - static updateCharacter(userId: string, id: number | null, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, meta: string, lang: 'fr' | 'en' = 'fr'): boolean { + static updateCharacter(userId: string, id: number | null, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=?, char_meta=? WHERE `character_id`=? AND `user_id`=?', [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, meta, id, userId]); + const result: RunResult = db.run('UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=? WHERE `character_id`=? AND `user_id`=?', [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, id, userId]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { diff --git a/electron/database/repositories/location.repository.ts b/electron/database/repositories/location.repository.ts index c9d238a..15ff35c 100644 --- a/electron/database/repositories/location.repository.ts +++ b/electron/database/repositories/location.repository.ts @@ -54,11 +54,11 @@ export default class LocationRepo { return result; } - static insertLocation(userId: string, locationId: string, bookId: string, encryptedName: string, originalName: string, meta: string, lang: 'fr' | 'en' = 'fr'): string { + static insertLocation(userId: string, locationId: string, bookId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string { let result: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, loc_meta) VALUES (?, ?, ?, ?, ?, ?)', [locationId, bookId, userId, encryptedName, originalName, meta]); + result = db.run('INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name) VALUES (?, ?, ?, ?, ?)', [locationId, bookId, userId, encryptedName, originalName]); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); @@ -75,11 +75,11 @@ export default class LocationRepo { } } - static insertLocationElement(userId: string, elementId: string, locationId: string, encryptedName: string, originalName: string, meta: string, lang: 'fr' | 'en' = 'fr'): string { + static insertLocationElement(userId: string, elementId: string, locationId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string { let result: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, element_meta) VALUES (?,?,?,?,?,?,?)', [elementId, locationId, userId, encryptedName, originalName, '', meta]); + result = db.run('INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description) VALUES (?,?,?,?,?,?)', [elementId, locationId, userId, encryptedName, originalName, '']); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); @@ -96,11 +96,11 @@ export default class LocationRepo { } } - static insertLocationSubElement(userId: string, subElementId: string, elementId: string, encryptedName: string, originalName: string, meta: string, lang: 'fr' | 'en' = 'fr'): string { + static insertLocationSubElement(userId: string, subElementId: string, elementId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string { let result: RunResult; try { const db: Database = System.getDb(); - result = db.run('INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, sub_elem_meta) VALUES (?,?,?,?,?,?,?)', [subElementId, elementId, userId, encryptedName, originalName, '', meta]); + result = db.run('INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description) VALUES (?,?,?,?,?,?)', [subElementId, elementId, userId, encryptedName, originalName, '']); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); @@ -117,10 +117,10 @@ export default class LocationRepo { } } - static updateLocationSubElement(userId: string, id: string, encryptedName: string, originalName: string, encryptDescription: string, meta: string, lang: 'fr' | 'en' = 'fr'): boolean { + static updateLocationSubElement(userId: string, id: string, encryptedName: string, originalName: string, encryptDescription: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE location_sub_element SET sub_elem_name=?, original_name=?, sub_elem_description=?, sub_elem_meta=? WHERE sub_element_id=? AND user_id=?', [encryptedName, originalName, encryptDescription, meta, id, userId]); + const result: RunResult = db.run('UPDATE location_sub_element SET sub_elem_name=?, original_name=?, sub_elem_description=? WHERE sub_element_id=? AND user_id=?', [encryptedName, originalName, encryptDescription, id, userId]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { @@ -133,10 +133,10 @@ export default class LocationRepo { } } - static updateLocationElement(userId: string, id: string, encryptedName: string, originalName: string, encryptedDescription: string, meta: string, lang: 'fr' | 'en' = 'fr'): boolean { + static updateLocationElement(userId: string, id: string, encryptedName: string, originalName: string, encryptedDescription: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE location_element SET element_name=?, original_name=?, element_description=?, element_meta=? WHERE element_id=? AND user_id=?', [encryptedName, originalName, encryptedDescription, meta, id, userId]); + const result: RunResult = db.run('UPDATE location_element SET element_name=?, original_name=?, element_description=? WHERE element_id=? AND user_id=?', [encryptedName, originalName, encryptedDescription, id, userId]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { @@ -149,10 +149,10 @@ export default class LocationRepo { } } - static updateLocationSection(userId: string, id: string, encryptedName: string, originalName: string, meta: string, lang: 'fr' | 'en' = 'fr'): boolean { + static updateLocationSection(userId: string, id: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE book_location SET loc_name=?, loc_original_name=?, loc_meta=? WHERE loc_id=? AND user_id=?', [encryptedName, originalName, meta, id, userId]); + const result: RunResult = db.run('UPDATE book_location SET loc_name=?, loc_original_name=? WHERE loc_id=? AND user_id=?', [encryptedName, originalName, id, userId]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { diff --git a/electron/database/repositories/user.repository.ts b/electron/database/repositories/user.repository.ts index bce4d8c..fbdca85 100644 --- a/electron/database/repositories/user.repository.ts +++ b/electron/database/repositories/user.repository.ts @@ -33,17 +33,6 @@ interface UserResponse { account_verified: boolean; } -export interface UserQueryResponse extends Record { - user_id: string; - first_name: string; - last_name: string; - username: string; - email: string; - plateform: string; - accountVerified: boolean; - password: string; -} - export interface UserAccountQuery extends Record { first_name: string; last_name: string; @@ -53,77 +42,21 @@ export interface UserAccountQuery extends Record { user_meta: string; } -export interface TOTPQuery extends Record { - user_id: number; - email: string; - totp_code: string; - totp_meta: string; -} - -export interface PasswordResponse extends Record { - user_id: string; - password: string; -} - export interface GuideTourResult extends Record { step_tour: string; } -export interface UserSubscription extends Record { - user_id: string; - sub_type: string; - sub_tier: number; - start_date: string; - end_date: string; - status: number; -} - -export interface UserAPIKeyResult extends Record { - brand: AIBrand, - key: string | null, - actif: number -} - -export interface UserAiUsageResult extends Record { - token_in: string; - token_out: string; - total_price: number; -} - -export interface BasicUserCredentials extends Record { - user_id: string, - first_name: string, - last_name: string, - username: string, - email: string, - user_meta: string -} - export default class UserRepo { - public static updatePoints(points: number, userId: string, lang: 'fr' | 'en' = 'fr'): void { - try { - const db: Database = System.getDb(); - db.run('UPDATE `erit_users` SET `erite_points`=erite_points+? WHERE `user_id`=?', [points, userId]); - } catch (e: unknown) { - if (e instanceof Error) { - console.error(`DB Error: ${e.message}`); - throw new Error(lang === 'fr' ? `Impossible de mettre à jour les points.` : `Unable to update points.`); - } else { - console.error("An unknown error occurred."); - throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred."); - } - } - } - public static insertUser(uuId: string, firstName: string, lastName: string, username: string, originUsername: string, email: string, originEmail: string, password: string, meta: string, socialId: string | null, provider: string, lang: 'fr' | 'en' = 'fr'): string { + public static insertUser(uuId: string, firstName: string, lastName: string, username: string, originUsername: string, email: string, originEmail: string, lang: 'fr' | 'en' = 'fr'): string { let result: RunResult; try { const db: Database = System.getDb(); const query = `INSERT INTO erit_users (user_id, first_name, last_name, username, email, origin_email, - origin_username, plateform, social_id, password, term_accepted, - account_verified, user_meta) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 1, ?)`; - const values: (string | null | number)[] = [uuId, firstName, lastName, username, email, originEmail, originUsername, provider, socialId, password, meta]; + origin_username, term_accepted, + account_verified) + VALUES (?, ?, ?, ?, ?, ?, ?, 0, 1)`; + const values: (string | null | number)[] = [uuId, firstName, lastName, username, email, originEmail, originUsername]; result = db.run(query, values); } catch (e: unknown) { if (e instanceof Error) { @@ -145,7 +78,7 @@ export default class UserRepo { let result; try { const db: Database = System.getDb(); - result = db.get('SELECT `first_name`, `last_name`, `username`, `email`, `plateform`, `term_accepted`, `account_verified`, user_meta, author_name, writing_lang, writing_level, erite_points AS rite_points, user_group, credits_balance FROM `erit_users` AS users LEFT JOIN user_preferences AS preference ON users.user_id=preference.user_id WHERE users.user_id=?', [userId]); + result = db.get('SELECT `first_name`, `last_name`, `username`, `email`, `plateform`, `term_accepted`, `account_verified`, user_meta, author_name, erite_points AS rite_points, user_group, credits_balance FROM `erit_users` AS users LEFT JOIN user_preferences AS preference ON users.user_id=preference.user_id WHERE users.user_id=?', [userId]); } catch (e: unknown) { if (e instanceof Error) { console.error(`DB Error: ${e.message}`); @@ -161,10 +94,10 @@ export default class UserRepo { return result as UserInfosQueryResponse; } - public static updateUserInfos(userId: string, firstName: string, lastName: string, username: string, originUsername: string, email: string, originEmail: string, userMeta: string, originalAuthorName: string, authorName: string, lang: 'fr' | 'en' = 'fr'): boolean { + public static updateUserInfos(userId: string, firstName: string, lastName: string, username: string, originUsername: string, email: string, originEmail: string, originalAuthorName: string, authorName: string, lang: 'fr' | 'en' = 'fr'): boolean { try { const db: Database = System.getDb(); - const result: RunResult = db.run('UPDATE `erit_users` SET `first_name`=?, `last_name`=?, `username`=?, email=?,`origin_username`=?, user_meta=?, origin_author_name=? ,author_name=? WHERE user_id=? AND `origin_email`=?', [firstName, lastName, username, email, originUsername, userMeta, originalAuthorName, authorName, userId, originEmail]); + const result: RunResult = db.run('UPDATE `erit_users` SET `first_name`=?, `last_name`=?, `username`=?, email=?,`origin_username`=?, origin_author_name=? ,author_name=? WHERE user_id=? AND `origin_email`=?', [firstName, lastName, username, email, originUsername, originalAuthorName, authorName, userId, originEmail]); return result.changes > 0; } catch (e: unknown) { if (e instanceof Error) { @@ -196,20 +129,4 @@ export default class UserRepo { } return result as UserAccountQuery; } - - static fetchGuideTour(userId: string, plateforme: string): GuideTourResult[] { - try { - const db: Database = System.getDb(); - const result = db.all('SELECT `step_tour` FROM `logs_guide_tour` WHERE user_id=? AND plateforme=?', [userId, plateforme]) as GuideTourResult[]; - return result.length > 0 ? result : []; - } catch (e: unknown) { - if (e instanceof Error) { - console.error(e.message); - return []; - } else { - console.error(`Une erreur inconnue est survenue lors de la récupération du guide tour.`); - return []; - } - } - } } diff --git a/lib/models/Book.ts b/lib/models/Book.ts index ccb543c..8441675 100644 --- a/lib/models/Book.ts +++ b/lib/models/Book.ts @@ -46,13 +46,14 @@ export interface GuideLine { } export interface GuideLineAI { - narrativeType: number; - dialogueType: number; - globalResume: string; - atmosphere: string; - verbeTense: number; - langue: number; - themes: string; + 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 PlotPoint {