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.
This commit is contained in:
48
electron/database/keyManager.ts
Normal file
48
electron/database/keyManager.ts
Normal file
@@ -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}`);
|
||||||
|
}
|
||||||
@@ -203,13 +203,13 @@ export default class Chapter {
|
|||||||
return ChapterRepo.updateChapter(userId, chapterId, encryptedTitle, hashedTitle, chapterOrder, lang);
|
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);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
for (const chapter of chapters) {
|
for (const chapter of chapters) {
|
||||||
const summary: string = chapter.summary ? System.encryptDataWithUserKey(chapter.summary, userKey) : '';
|
const summary: string = chapter.summary ? System.encryptDataWithUserKey(chapter.summary, userKey) : '';
|
||||||
const goal: string = chapter.goal ? System.encryptDataWithUserKey(chapter.goal, userKey) : '';
|
const goal: string = chapter.goal ? System.encryptDataWithUserKey(chapter.goal, userKey) : '';
|
||||||
const chapterId: string = chapter.chapterId;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import User from "./User";
|
import CharacterRepo, {
|
||||||
import System, {UserKey} from "./System";
|
AttributeResult,
|
||||||
import CharacterRepo, {AttributeResult, CharacterResult, CompleteCharacterResult} from "../repositories/character.repo";
|
CharacterResult,
|
||||||
|
CompleteCharacterResult
|
||||||
|
} from "@/electron/database/repositories/character.repository";
|
||||||
|
import System from "@/electron/database/System";
|
||||||
|
import {getUserEncryptionKey} from "../keyManager";
|
||||||
|
|
||||||
export type CharacterCategory = 'Main' | 'Secondary' | 'Recurring';
|
export type CharacterCategory = 'Main' | 'Secondary' | 'Recurring';
|
||||||
|
|
||||||
@@ -62,15 +66,13 @@ export interface CharacterAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class Character {
|
export default class Character {
|
||||||
public static async getCharacterList(userId: string, bookId: string): Promise<CharacterProps[]> {
|
public static getCharacterList(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): CharacterProps[] {
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const keys:UserKey[] = await System.getAllUserKeysAndVersions(userId);
|
const characters: CharacterResult[] = CharacterRepo.fetchCharacters(userId, bookId, lang);
|
||||||
const characters: CharacterResult[] = await CharacterRepo.fetchCharacters(userId, bookId);
|
|
||||||
if (!characters) return [];
|
if (!characters) return [];
|
||||||
if (characters.length === 0) return [];
|
if (characters.length === 0) return [];
|
||||||
const characterList: CharacterProps[] = [];
|
const characterList: CharacterProps[] = [];
|
||||||
for (const character of characters) {
|
for (const character of characters) {
|
||||||
const userKey:string = await user.getUserKey(character.char_meta,true,keys);
|
|
||||||
characterList.push({
|
characterList.push({
|
||||||
id: character.character_id,
|
id: character.character_id,
|
||||||
name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '',
|
name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '',
|
||||||
@@ -86,10 +88,8 @@ export default class Character{
|
|||||||
return characterList;
|
return characterList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async addNewCharacter(userId: string, character: CharacterPropsPost, bookId: string): Promise<string> {
|
public static addNewCharacter(userId: string, character: CharacterPropsPost, bookId: string, lang: 'fr' | 'en' = 'fr'): string {
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const meta:string = System.encryptDateKey(userId);
|
|
||||||
const userKey:string = await user.getUserKey(meta,true);
|
|
||||||
const characterId: string = System.createUniqueId();
|
const characterId: string = System.createUniqueId();
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey);
|
||||||
const encryptedLastName: string = System.encryptDataWithUserKey(character.lastName, 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 encryptedRole: string = System.encryptDataWithUserKey(character.role, userKey);
|
||||||
const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey);
|
const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey);
|
||||||
const encryptedHistory: string = System.encryptDataWithUserKey(character.history ? character.history : '', 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);
|
const attributes: string[] = Object.keys(character);
|
||||||
for (const key of attributes) {
|
for (const key of attributes) {
|
||||||
if (Array.isArray(character[key as keyof CharacterPropsPost])) {
|
if (Array.isArray(character[key as keyof CharacterPropsPost])) {
|
||||||
@@ -108,7 +108,7 @@ export default class Character{
|
|||||||
for (const item of array) {
|
for (const item of array) {
|
||||||
const type: string = key;
|
const type: string = key;
|
||||||
const name: string = item.name;
|
const name: string = item.name;
|
||||||
await this.addNewAttribute(characterId, userId, type, name);
|
this.addNewAttribute(characterId, userId, type, name, lang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,10 +116,8 @@ export default class Character{
|
|||||||
return characterId;
|
return characterId;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateCharacter(userId: string, character: CharacterPropsPost): Promise<boolean> {
|
static updateCharacter(userId: string, character: CharacterPropsPost, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const meta: string = System.encryptDateKey(userId);
|
|
||||||
const userKey: string = await user.getUserKey(meta, true);
|
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey);
|
||||||
const encryptedLastName: string = System.encryptDataWithUserKey(character.lastName, userKey);
|
const encryptedLastName: string = System.encryptDataWithUserKey(character.lastName, userKey);
|
||||||
const encryptedTitle: string = System.encryptDataWithUserKey(character.title, userKey);
|
const encryptedTitle: string = System.encryptDataWithUserKey(character.title, userKey);
|
||||||
@@ -128,33 +126,29 @@ export default class Character{
|
|||||||
const encryptedRole: string = System.encryptDataWithUserKey(character.role, userKey);
|
const encryptedRole: string = System.encryptDataWithUserKey(character.role, userKey);
|
||||||
const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey);
|
const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey);
|
||||||
const encryptedHistory: string = System.encryptDataWithUserKey(character.history ? character.history : '', 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<string> {
|
static addNewAttribute(characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const meta: string = System.encryptDateKey(userId);
|
|
||||||
const userKey: string = await user.getUserKey(meta, true);
|
|
||||||
const attributeId: string = System.createUniqueId();
|
const attributeId: string = System.createUniqueId();
|
||||||
const encryptedType: string = System.encryptDataWithUserKey(type, userKey);
|
const encryptedType: string = System.encryptDataWithUserKey(type, userKey);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(name, 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) {
|
static deleteAttribute(userId: string, attributeId: string, lang: 'fr' | 'en' = 'fr') {
|
||||||
return await CharacterRepo.deleteAttribute(userId, attributeId);
|
return CharacterRepo.deleteAttribute(userId, attributeId, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getAttributes(characterId: string, userId: string): Promise<CharacterAttribute[]> {
|
static getAttributes(characterId: string, userId: string, lang: 'fr' | 'en' = 'fr'): CharacterAttribute[] {
|
||||||
const user: User = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const keys: UserKey[] = await System.getAllUserKeysAndVersions(userId);
|
const attributes: AttributeResult[] = CharacterRepo.fetchAttributes(characterId, userId, lang);
|
||||||
const attributes: AttributeResult[] = await CharacterRepo.fetchAttributes(characterId, userId);
|
|
||||||
if (!attributes?.length) return [];
|
if (!attributes?.length) return [];
|
||||||
|
|
||||||
const groupedMap: Map<string, Attribute[]> = new Map<string, Attribute[]>();
|
const groupedMap: Map<string, Attribute[]> = new Map<string, Attribute[]>();
|
||||||
|
|
||||||
for (const attribute of attributes) {
|
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 type: string = System.decryptDataWithUserKey(attribute.attribute_name, userKey);
|
||||||
const value: string = attribute.attribute_value ? System.decryptDataWithUserKey(attribute.attribute_value, userKey) : '';
|
const value: string = attribute.attribute_value ? System.decryptDataWithUserKey(attribute.attribute_value, userKey) : '';
|
||||||
|
|
||||||
@@ -174,15 +168,14 @@ export default class Character{
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getCompleteCharacterList(userId: string, bookId: string, characters: string[]): Promise<CompleteCharacterProps[]> {
|
static getCompleteCharacterList(userId: string, bookId: string, characters: string[], lang: 'fr' | 'en' = 'fr'): CompleteCharacterProps[] {
|
||||||
const characterList: CompleteCharacterResult[] = await CharacterRepo.fetchCompleteCharacters(userId, bookId, characters);
|
const characterList: CompleteCharacterResult[] = CharacterRepo.fetchCompleteCharacters(userId, bookId, characters, lang);
|
||||||
|
|
||||||
if (!characterList || characterList.length === 0) {
|
if (!characterList || characterList.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const keys: UserKey[] = await System.getAllUserKeysAndVersions(userId);
|
|
||||||
const completeCharactersMap = new Map<string, CompleteCharacterProps>();
|
const completeCharactersMap = new Map<string, CompleteCharacterProps>();
|
||||||
for (const character of characterList) {
|
for (const character of characterList) {
|
||||||
if (!character.character_id) {
|
if (!character.character_id) {
|
||||||
@@ -190,10 +183,6 @@ export default class Character{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!completeCharactersMap.has(character.character_id)) {
|
if (!completeCharactersMap.has(character.character_id)) {
|
||||||
const userKey: string = await user.getUserKey(character.char_meta, true, keys);
|
|
||||||
if (!userKey) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const personnageObj: CompleteCharacterProps = {
|
const personnageObj: CompleteCharacterProps = {
|
||||||
id: '',
|
id: '',
|
||||||
name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '',
|
name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '',
|
||||||
@@ -221,13 +210,11 @@ export default class Character{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rowKey: string = await user.getUserKey(character.attr_meta, true, keys);
|
if (!personnage) {
|
||||||
|
|
||||||
if (!personnage || !rowKey) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const decryptedName: string = System.decryptDataWithUserKey(character.attribute_name, rowKey);
|
const decryptedName: string = System.decryptDataWithUserKey(character.attribute_name, userKey);
|
||||||
const decryptedValue: string = character.attribute_value ? System.decryptDataWithUserKey(character.attribute_value, rowKey) : '';
|
const decryptedValue: string = character.attribute_value ? System.decryptDataWithUserKey(character.attribute_value, userKey) : '';
|
||||||
|
|
||||||
if (Array.isArray(personnage[decryptedName])) {
|
if (Array.isArray(personnage[decryptedName])) {
|
||||||
personnage[decryptedName].push({
|
personnage[decryptedName].push({
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import User from "./User";
|
|
||||||
import System, {UserKey} from "./System";
|
|
||||||
import LocationRepo, {
|
import LocationRepo, {
|
||||||
LocationByTagResult,
|
LocationByTagResult,
|
||||||
LocationElementQueryResult,
|
LocationElementQueryResult,
|
||||||
LocationQueryResult
|
LocationQueryResult
|
||||||
} from "../repositories/location.repo";
|
} from "@/electron/database/repositories/location.repository";
|
||||||
|
import System from "@/electron/database/System";
|
||||||
|
import {getUserEncryptionKey} from "../keyManager";
|
||||||
|
|
||||||
export interface SubElement {
|
export interface SubElement {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -30,14 +30,13 @@ export default class Location {
|
|||||||
* Récupère toutes les locations pour un utilisateur et un livre donnés.
|
* Récupère toutes les locations pour un utilisateur et un livre donnés.
|
||||||
* @param {string} userId - L'ID de l'utilisateur.
|
* @param {string} userId - L'ID de l'utilisateur.
|
||||||
* @param {string} bookId - L'ID du livre.
|
* @param {string} bookId - L'ID du livre.
|
||||||
* @returns {Promise<LocationProps[]>} - 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.
|
* @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<LocationProps[]> {
|
static getAllLocations(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationProps[] {
|
||||||
const locations: LocationQueryResult[] = await LocationRepo.getLocation(userId, bookId);
|
const locations: LocationQueryResult[] = LocationRepo.getLocation(userId, bookId, lang);
|
||||||
if (!locations || locations.length === 0) return [];
|
if (!locations || locations.length === 0) return [];
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const userKeys: UserKey[] = await System.getAllUserKeysAndVersions(userId);
|
|
||||||
|
|
||||||
const locationArray: LocationProps[] = [];
|
const locationArray: LocationProps[] = [];
|
||||||
|
|
||||||
@@ -45,8 +44,7 @@ export default class Location {
|
|||||||
let location = locationArray.find(loc => loc.id === record.loc_id);
|
let location = locationArray.find(loc => loc.id === record.loc_id);
|
||||||
|
|
||||||
if (!location) {
|
if (!location) {
|
||||||
const key: string = await user.getUserKey(record.loc_meta, true, userKeys);
|
const decryptedName: string = System.decryptDataWithUserKey(record.loc_name, userKey);
|
||||||
const decryptedName: string = System.decryptDataWithUserKey(record.loc_name, key)/* ton code de décryptage ici avec record.loc_name */;
|
|
||||||
location = {
|
location = {
|
||||||
id: record.loc_id,
|
id: record.loc_id,
|
||||||
name: decryptedName,
|
name: decryptedName,
|
||||||
@@ -58,9 +56,8 @@ export default class Location {
|
|||||||
if (record.element_id) {
|
if (record.element_id) {
|
||||||
let element = location.elements.find(elem => elem.id === record.element_id);
|
let element = location.elements.find(elem => elem.id === record.element_id);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
const key: string = await user.getUserKey(record.element_meta, true, userKeys);
|
const decryptedName: string = System.decryptDataWithUserKey(record.element_name, userKey);
|
||||||
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, userKey) : '';
|
||||||
const decryptedDesc: string = record.element_description ? System.decryptDataWithUserKey(record.element_description, key) : ''/* ton code de décryptage ici avec record.element_description */;
|
|
||||||
|
|
||||||
element = {
|
element = {
|
||||||
id: record.element_id,
|
id: record.element_id,
|
||||||
@@ -75,9 +72,8 @@ export default class Location {
|
|||||||
const subElementExists = element.subElements.some(sub => sub.id === record.sub_element_id);
|
const subElementExists = element.subElements.some(sub => sub.id === record.sub_element_id);
|
||||||
|
|
||||||
if (!subElementExists) {
|
if (!subElementExists) {
|
||||||
const key: string = await user.getUserKey(record.sub_elem_meta, true, userKeys);
|
const decryptedName: string = System.decryptDataWithUserKey(record.sub_elem_name, userKey);
|
||||||
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, userKey) : '';
|
||||||
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 */;
|
|
||||||
|
|
||||||
|
|
||||||
element.subElements.push({
|
element.subElements.push({
|
||||||
@@ -92,81 +88,72 @@ export default class Location {
|
|||||||
return locationArray;
|
return locationArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addLocationSection(userId: string, locationName: string, bookId: string): Promise<string> {
|
static addLocationSection(userId: string, locationName: string, bookId: string, lang: 'fr' | 'en' = 'fr'): string {
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const meta: string = System.encryptDateKey(userId);
|
|
||||||
const userKey: string = await user.getUserKey(meta, true);
|
|
||||||
const originalName: string = System.hashElement(locationName);
|
const originalName: string = System.hashElement(locationName);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(locationName, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(locationName, userKey);
|
||||||
const locationId: string = System.createUniqueId();
|
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) {
|
static addLocationElement(userId: string, locationId: string, elementName: string, lang: 'fr' | 'en' = 'fr') {
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const meta: string = System.encryptDateKey(userId);
|
|
||||||
const userKey: string = await user.getUserKey(meta, true);
|
|
||||||
const originalName: string = System.hashElement(elementName);
|
const originalName: string = System.hashElement(elementName);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(elementName, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(elementName, userKey);
|
||||||
const elementId: string = System.createUniqueId();
|
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) {
|
static addLocationSubElement(userId: string, elementId: string, subElementName: string, lang: 'fr' | 'en' = 'fr') {
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const meta: string = System.encryptDateKey(userId);
|
|
||||||
const userKey: string = await user.getUserKey(meta, true);
|
|
||||||
const originalName: string = System.hashElement(subElementName);
|
const originalName: string = System.hashElement(subElementName);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(subElementName, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(subElementName, userKey);
|
||||||
const subElementId: string = System.createUniqueId();
|
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[]) {
|
static updateLocationSection(userId: string, locations: LocationProps[], lang: 'fr' | 'en' = 'fr') {
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const meta: string = System.encryptDateKey(userId);
|
|
||||||
const userKey: string = await user.getUserKey(meta, true);
|
|
||||||
|
|
||||||
for (const location of locations) {
|
for (const location of locations) {
|
||||||
const originalName: string = System.hashElement(location.name);
|
const originalName: string = System.hashElement(location.name);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(location.name, userKey);
|
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) {
|
for (const element of location.elements) {
|
||||||
const originalName: string = System.hashElement(element.name);
|
const originalName: string = System.hashElement(element.name);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(element.name, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(element.name, userKey);
|
||||||
const encryptDescription: string = element.description ? System.encryptDataWithUserKey(element.description, 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) {
|
for (const subElement of element.subElements) {
|
||||||
const originalName: string = System.hashElement(subElement.name);
|
const originalName: string = System.hashElement(subElement.name);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(subElement.name, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(subElement.name, userKey);
|
||||||
const encryptDescription: string = subElement.description ? System.encryptDataWithUserKey(subElement.description, 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 {
|
return {
|
||||||
valid: true,
|
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) {
|
static deleteLocationSection(userId: string, locationId: string, lang: 'fr' | 'en' = 'fr') {
|
||||||
return LocationRepo.deleteLocationSection(userId, locationId);
|
return LocationRepo.deleteLocationSection(userId, locationId, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteLocationElement(userId: string, elementId: string) {
|
static deleteLocationElement(userId: string, elementId: string, lang: 'fr' | 'en' = 'fr') {
|
||||||
return LocationRepo.deleteLocationElement(userId, elementId);
|
return LocationRepo.deleteLocationElement(userId, elementId, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteLocationSubElement(userId: string, subElementId: string) {
|
static deleteLocationSubElement(userId: string, subElementId: string, lang: 'fr' | 'en' = 'fr') {
|
||||||
return LocationRepo.deleteLocationSubElement(userId, subElementId);
|
return LocationRepo.deleteLocationSubElement(userId, subElementId, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getLocationTags(userId: string, bookId: string): Promise<SubElement[]> {
|
static getLocationTags(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): SubElement[] {
|
||||||
const data: LocationElementQueryResult[] = await LocationRepo.fetchLocationTags(userId, bookId);
|
const data: LocationElementQueryResult[] = LocationRepo.fetchLocationTags(userId, bookId, lang);
|
||||||
if (!data || data.length === 0) return [];
|
if (!data || data.length === 0) return [];
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const userKeys: UserKey[] = await System.getAllUserKeysAndVersions(userId);
|
|
||||||
|
|
||||||
const elementCounts = new Map<string, number>();
|
const elementCounts = new Map<string, number>();
|
||||||
data.forEach((record: LocationElementQueryResult): void => {
|
data.forEach((record: LocationElementQueryResult): void => {
|
||||||
@@ -182,21 +169,19 @@ export default class Location {
|
|||||||
if (elementCount > 1 && record.sub_element_id) {
|
if (elementCount > 1 && record.sub_element_id) {
|
||||||
if (processedIds.has(record.sub_element_id)) continue;
|
if (processedIds.has(record.sub_element_id)) continue;
|
||||||
|
|
||||||
const key: string = await user.getUserKey(record.sub_elem_meta, true, userKeys);
|
|
||||||
subElements.push({
|
subElements.push({
|
||||||
id: record.sub_element_id,
|
id: record.sub_element_id,
|
||||||
name: System.decryptDataWithUserKey(record.sub_elem_name, key),
|
name: System.decryptDataWithUserKey(record.sub_elem_name, userKey),
|
||||||
description: record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, key) : ''
|
description: record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, userKey) : ''
|
||||||
});
|
});
|
||||||
processedIds.add(record.sub_element_id);
|
processedIds.add(record.sub_element_id);
|
||||||
} else if (elementCount === 1 && !record.sub_element_id) {
|
} else if (elementCount === 1 && !record.sub_element_id) {
|
||||||
if (processedIds.has(record.element_id)) continue;
|
if (processedIds.has(record.element_id)) continue;
|
||||||
|
|
||||||
const key: string = await user.getUserKey(record.element_meta, true, userKeys);
|
|
||||||
subElements.push({
|
subElements.push({
|
||||||
id: record.element_id,
|
id: record.element_id,
|
||||||
name: System.decryptDataWithUserKey(record.element_name, key),
|
name: System.decryptDataWithUserKey(record.element_name, userKey),
|
||||||
description: record.element_description ? System.decryptDataWithUserKey(record.element_description, key) : ''
|
description: record.element_description ? System.decryptDataWithUserKey(record.element_description, userKey) : ''
|
||||||
});
|
});
|
||||||
processedIds.add(record.element_id);
|
processedIds.add(record.element_id);
|
||||||
}
|
}
|
||||||
@@ -204,20 +189,18 @@ export default class Location {
|
|||||||
return subElements;
|
return subElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getLocationsByTags(userId: string, locations: string[]): Promise<Element[]> {
|
static getLocationsByTags(userId: string, locations: string[], lang: 'fr' | 'en' = 'fr'): Element[] {
|
||||||
const locationsTags: LocationByTagResult[] = await LocationRepo.fetchLocationsByTags(userId, locations);
|
const locationsTags: LocationByTagResult[] = LocationRepo.fetchLocationsByTags(userId, locations, lang);
|
||||||
if (!locationsTags || locationsTags.length === 0) return [];
|
if (!locationsTags || locationsTags.length === 0) return [];
|
||||||
const user = new User(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const userKeys: UserKey[] = await System.getAllUserKeysAndVersions(userId);
|
|
||||||
const locationTags: Element[] = [];
|
const locationTags: Element[] = [];
|
||||||
for (const record of locationsTags) {
|
for (const record of locationsTags) {
|
||||||
let element: Element | undefined = locationTags.find((elem: Element): boolean => elem.name === record.element_name);
|
let element: Element | undefined = locationTags.find((elem: Element): boolean => elem.name === record.element_name);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
const key: string = await user.getUserKey(record.element_meta, true, userKeys);
|
const decryptedName: string = System.decryptDataWithUserKey(record.element_name, userKey);
|
||||||
const decryptedName: string = System.decryptDataWithUserKey(record.element_name, key);
|
const decryptedDesc: string = record.element_description ? System.decryptDataWithUserKey(record.element_description, userKey) : '';
|
||||||
const decryptedDesc: string = record.element_description ? System.decryptDataWithUserKey(record.element_description, key) : '';
|
|
||||||
element = {
|
element = {
|
||||||
id: record.element_id,
|
id: '',
|
||||||
name: decryptedName,
|
name: decryptedName,
|
||||||
description: decryptedDesc,
|
description: decryptedDesc,
|
||||||
subElements: []
|
subElements: []
|
||||||
@@ -227,9 +210,8 @@ export default class Location {
|
|||||||
if (record.sub_elem_name) {
|
if (record.sub_elem_name) {
|
||||||
const subElementExists: boolean = element.subElements.some(sub => sub.name === record.sub_elem_name);
|
const subElementExists: boolean = element.subElements.some(sub => sub.name === record.sub_elem_name);
|
||||||
if (!subElementExists) {
|
if (!subElementExists) {
|
||||||
const key: string = await user.getUserKey(record.sub_elem_meta, true, userKeys);
|
const decryptedName: string = System.decryptDataWithUserKey(record.sub_elem_name, userKey);
|
||||||
const decryptedName: string = System.decryptDataWithUserKey(record.sub_elem_name, key);
|
const decryptedDesc: string = record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, userKey) : '';
|
||||||
const decryptedDesc: string = record.sub_elem_description ? System.decryptDataWithUserKey(record.sub_elem_description, key) : '';
|
|
||||||
element.subElements.push({
|
element.subElements.push({
|
||||||
id: '',
|
id: '',
|
||||||
name: decryptedName,
|
name: decryptedName,
|
||||||
@@ -241,7 +223,7 @@ export default class Location {
|
|||||||
return locationTags;
|
return locationTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async locationsDescription(locations: Element[]): Promise<string> {
|
static locationsDescription(locations: Element[]): string {
|
||||||
return locations.map((location: Element): string => {
|
return locations.map((location: Element): string => {
|
||||||
const fields: string[] = [];
|
const fields: string[] = [];
|
||||||
if (location.name) fields.push(`Nom : ${location.name}`);
|
if (location.name) fields.push(`Nom : ${location.name}`);
|
||||||
|
|||||||
@@ -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 :';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +1,7 @@
|
|||||||
import UserRepo, {
|
import UserRepo, {UserAccountQuery, UserInfosQueryResponse} from "@/electron/database/repositories/user.repository";
|
||||||
BasicUserCredentials,
|
import System from "@/electron/database/System";
|
||||||
GuideTourResult,
|
import Book, {BookProps} from "@/electron/database/models/Book";
|
||||||
PasswordResponse,
|
import {getUserEncryptionKey} from "@/electron/database/keyManager";
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserAccount{
|
interface UserAccount{
|
||||||
firstName:string;
|
firstName:string;
|
||||||
@@ -46,29 +11,10 @@ interface UserAccount{
|
|||||||
email:string;
|
email:string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TOTPData{
|
|
||||||
totpCode:string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GuideTour {
|
export interface GuideTour {
|
||||||
[key: string]: boolean;
|
[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{
|
export default class User{
|
||||||
|
|
||||||
private readonly id:string;
|
private readonly id:string;
|
||||||
@@ -76,14 +22,9 @@ export default class User{
|
|||||||
private lastName: string;
|
private lastName: string;
|
||||||
private username: string;
|
private username: string;
|
||||||
private email: string;
|
private email: string;
|
||||||
private platform: string;
|
|
||||||
private accountVerified: boolean;
|
private accountVerified: boolean;
|
||||||
private authorName: string;
|
private authorName: string;
|
||||||
private writingLang: number;
|
|
||||||
private writingLevel: number;
|
|
||||||
private ritePoints: number;
|
|
||||||
private groupId: number;
|
private groupId: number;
|
||||||
private balancedCredits: number;
|
|
||||||
private termsAccepted: boolean;
|
private termsAccepted: boolean;
|
||||||
|
|
||||||
constructor(id:string){
|
constructor(id:string){
|
||||||
@@ -92,64 +33,30 @@ export default class User{
|
|||||||
this.lastName = '';
|
this.lastName = '';
|
||||||
this.username = '';
|
this.username = '';
|
||||||
this.email = '';
|
this.email = '';
|
||||||
this.platform = '';
|
|
||||||
this.accountVerified = false;
|
this.accountVerified = false;
|
||||||
this.authorName = '';
|
this.authorName = '';
|
||||||
this.writingLang = 0;
|
|
||||||
this.writingLevel = 1;
|
|
||||||
this.ritePoints = 0;
|
|
||||||
this.groupId = 0;
|
this.groupId = 0;
|
||||||
this.balancedCredits = 0;
|
|
||||||
this.termsAccepted = false;
|
this.termsAccepted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getUserInfos(): Promise<void> {
|
public async getUserInfos(): Promise<void> {
|
||||||
const data: UserInfosQueryResponse = await UserRepo.fetchUserInfos(this.id);
|
const data: UserInfosQueryResponse = UserRepo.fetchUserInfos(this.id);
|
||||||
const userKey: string = await this.getUserKey(data.user_meta);
|
const userKey:string = getUserEncryptionKey(this.id)
|
||||||
this.firstName = System.decryptDataWithUserKey(data.first_name, userKey);
|
this.firstName = System.decryptDataWithUserKey(data.first_name, userKey);
|
||||||
this.lastName = System.decryptDataWithUserKey(data.last_name, userKey);
|
this.lastName = System.decryptDataWithUserKey(data.last_name, userKey);
|
||||||
this.username = System.decryptDataWithUserKey(data.username, userKey);
|
this.username = System.decryptDataWithUserKey(data.username, userKey);
|
||||||
this.email = System.decryptDataWithUserKey(data.email, userKey);
|
this.email = System.decryptDataWithUserKey(data.email, userKey);
|
||||||
this.platform = data.plateform;
|
|
||||||
this.accountVerified = data.account_verified === 1;
|
this.accountVerified = data.account_verified === 1;
|
||||||
this.authorName = data.author_name ? System.decryptDataWithUserKey(data.author_name, userKey) : '';
|
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.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;
|
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);
|
const user: User = new User(userId);
|
||||||
await user.getUserInfos();
|
await user.getUserInfos();
|
||||||
const books: BookProps[] = await Book.getBooks(userId);
|
const books: BookProps[] = await Book.getBooks(userId);
|
||||||
const guideTourResult: GuideTourResult[] = await UserRepo.fetchGuideTour(userId, plateforme);
|
|
||||||
const guideTour: GuideTour[] = [];
|
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 {
|
return {
|
||||||
id: user.getId(),
|
id: user.getId(),
|
||||||
name: user.getFirstName(),
|
name: user.getFirstName(),
|
||||||
@@ -158,26 +65,9 @@ export default class User{
|
|||||||
email: user.getEmail(),
|
email: user.getEmail(),
|
||||||
accountVerified: user.isAccountVerified(),
|
accountVerified: user.isAccountVerified(),
|
||||||
authorName: user.getAuthorName(),
|
authorName: user.getAuthorName(),
|
||||||
writingLang: user.getWritingLang(),
|
|
||||||
writingLevel: user.getWritingLevel(),
|
|
||||||
ritePoints: user.getRitePoints(),
|
|
||||||
groupId: user.getGroupId(),
|
groupId: user.getGroupId(),
|
||||||
termsAccepted: user.isTermsAccepted(),
|
termsAccepted: user.isTermsAccepted(),
|
||||||
guideTour: guideTour,
|
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) => {
|
books: books.map((book: BookProps) => {
|
||||||
return {
|
return {
|
||||||
bookId: book.id,
|
bookId: book.id,
|
||||||
@@ -188,550 +78,38 @@ export default class User{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async isTwoFactorEnabled(userId: string): Promise<boolean> {
|
public static async addUser(userId:string,firstName: string, lastName: string, username: string, email: string, notEncryptPassword: string, lang: 'fr' | 'en' = 'fr'): Promise<string> {
|
||||||
return UserRepo.isTwoFactorEnabled(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async changePassword(userId: string, password: string, newPassword: string, email: string): Promise<boolean> {
|
|
||||||
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<boolean> {
|
|
||||||
return UserRepo.deleteAccount(userId, System.hashElement(email));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async resetPassword(newPassword: string, email: string, verifyCode: string): Promise<boolean> {
|
|
||||||
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<string> {
|
|
||||||
return await UserRepo.fetchVerifyCode(System.hashElement(email), verifyCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async subscribeToBeta(email: string): Promise<boolean> {
|
|
||||||
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<string> {
|
|
||||||
const originEmail:string = System.hashElement(email);
|
const originEmail:string = System.hashElement(email);
|
||||||
const originUsername:string = System.hashElement(username);
|
const originUsername:string = System.hashElement(username);
|
||||||
await this.checkUser(originUsername, originEmail);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
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 encryptFirstName: string = System.encryptDataWithUserKey(firstName, userKey);
|
||||||
const encryptLastName: string = System.encryptDataWithUserKey(lastName, userKey);
|
const encryptLastName: string = System.encryptDataWithUserKey(lastName, userKey);
|
||||||
const encryptUsername: string = System.encryptDataWithUserKey(username, userKey);
|
const encryptUsername: string = System.encryptDataWithUserKey(username, userKey);
|
||||||
const encryptEmail: string = System.encryptDataWithUserKey(email, userKey);
|
const encryptEmail: string = System.encryptDataWithUserKey(email, userKey);
|
||||||
const originalEmail: string = System.hashElement(email);
|
const originalEmail: string = System.hashElement(email);
|
||||||
const originalUsername: string = System.hashElement(username);
|
const originalUsername: string = System.hashElement(username);
|
||||||
const meta: string = System.encryptDateKey(userId);
|
return UserRepo.insertUser(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail,lang);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async updateUserInfos(userKey: string, userId: string, firstName: string, lastName: string, username: string, email: string, authorName?: string): Promise<boolean> {
|
public static async updateUserInfos(userKey: string, userId: string, firstName: string, lastName: string, username: string, email: string, authorName?: string, lang: 'fr' | 'en' = 'fr'): Promise<boolean> {
|
||||||
const encryptFirstName:string = System.encryptDataWithUserKey(firstName,userKey);
|
const encryptFirstName:string = System.encryptDataWithUserKey(firstName,userKey);
|
||||||
const encryptLastName:string = System.encryptDataWithUserKey(lastName,userKey);
|
const encryptLastName:string = System.encryptDataWithUserKey(lastName,userKey);
|
||||||
const encryptUsername:string = System.encryptDataWithUserKey(username,userKey);
|
const encryptUsername:string = System.encryptDataWithUserKey(username,userKey);
|
||||||
const encryptEmail:string = System.encryptDataWithUserKey(email,userKey);
|
const encryptEmail:string = System.encryptDataWithUserKey(email,userKey);
|
||||||
const originalEmail:string = System.hashElement(email);
|
const originalEmail:string = System.hashElement(email);
|
||||||
const originalUsername:string = System.hashElement(username);
|
const originalUsername:string = System.hashElement(username);
|
||||||
const userMeta:string = System.encryptDateKey(userId);
|
|
||||||
let encryptAuthorName:string = '';
|
let encryptAuthorName:string = '';
|
||||||
let originalAuthorName: string = '';
|
let originalAuthorName: string = '';
|
||||||
if (authorName){
|
if (authorName){
|
||||||
encryptAuthorName = System.encryptDataWithUserKey(authorName,userKey);
|
encryptAuthorName = System.encryptDataWithUserKey(authorName,userKey);
|
||||||
originalAuthorName = System.hashElement(authorName);
|
originalAuthorName = System.hashElement(authorName);
|
||||||
}
|
}
|
||||||
return UserRepo.updateUserInfos(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail, userMeta, originalAuthorName, encryptAuthorName);
|
return UserRepo.updateUserInfos(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail, originalAuthorName, encryptAuthorName, lang);
|
||||||
}
|
|
||||||
public async getUserKey(metaKey:string,isNeedToBeDecrypt:boolean=true,keys?:UserKey[]):Promise<string>{
|
|
||||||
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<EncryptedKey> = await System.getUserKey(this.id,meta);
|
|
||||||
if (userKey.data){
|
|
||||||
return userKey.data?.encrypted_key;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async checkUserName(username:string):Promise<boolean>{
|
|
||||||
return UserRepo.fetchUserName(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async checkEmail(email:string):Promise<boolean>{
|
|
||||||
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<void> {
|
|
||||||
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<string> {
|
|
||||||
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<boolean> {
|
|
||||||
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<boolean>{
|
|
||||||
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<string> {
|
|
||||||
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<boolean> {
|
|
||||||
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<void> {
|
|
||||||
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<boolean>{
|
|
||||||
return UserRepo.checkTOTPExist(userId,email);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async handleTOTP(userId: string, email: string, verifyCode: string): Promise<boolean> {
|
|
||||||
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<boolean> {
|
|
||||||
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<boolean> {
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getUserAccountInformation(userId: string): Promise<UserAccount> {
|
public static async getUserAccountInformation(userId: string): Promise<UserAccount> {
|
||||||
const user:User = new User(userId);
|
const data: UserAccountQuery = UserRepo.fetchAccountInformation(userId);
|
||||||
const data: UserAccountQuery = await UserRepo.fetchAccountInformation(userId);
|
const userKey:string = getUserEncryptionKey(userId)
|
||||||
const userKey: string = await user.getUserKey(data.user_meta);
|
|
||||||
const userName: string = data.first_name ? System.decryptDataWithUserKey(data.first_name, userKey) : '';
|
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 lastName: string = data.last_name ? System.decryptDataWithUserKey(data.last_name, userKey) : '';
|
||||||
const username: string = data.username ? System.decryptDataWithUserKey(data.username, 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<UserSubscriptionProps[]> {
|
|
||||||
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<void>{
|
|
||||||
const points:number = Math.ceil(time/30);
|
|
||||||
await UserRepo.updatePoints(points,this.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async setUserPreferences(userId: string, writingLang: number, writingLevel: number): Promise<boolean | string> {
|
|
||||||
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<boolean> {
|
|
||||||
const log: boolean = await UserRepo.logAcceptTerms(userId, version);
|
|
||||||
if (log) {
|
|
||||||
return await UserRepo.updateTermsAccepted(userId);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getId(): string {
|
public getId(): string {
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
@@ -812,18 +148,6 @@ export default class User{
|
|||||||
return this.accountVerified;
|
return this.accountVerified;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getWritingLang(): number {
|
|
||||||
return this.writingLang;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getWritingLevel(): number {
|
|
||||||
return this.writingLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getRitePoints(): number {
|
|
||||||
return this.ritePoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isTermsAccepted(): boolean {
|
public isTermsAccepted(): boolean {
|
||||||
return this.termsAccepted;
|
return this.termsAccepted;
|
||||||
}
|
}
|
||||||
@@ -832,54 +156,7 @@ export default class User{
|
|||||||
return this.groupId;
|
return this.groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBalancedCredits(): number {
|
|
||||||
return this.balancedCredits;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getAuthorName(): string {
|
public getAuthorName(): string {
|
||||||
return this.authorName;
|
return this.authorName;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateAIUserKey(userId: string, anthropicKey: string | null, openaiKey: string | null, geminiKey: string | null): Promise<boolean> {
|
|
||||||
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<UserAPIKey[]> {
|
|
||||||
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<number> {
|
|
||||||
const {last, first} = System.getMonthBounds();
|
|
||||||
const usage: UserAiUsageResult = await UserRepo.fetchUserAiUsage(userId, first, last);
|
|
||||||
return usage.total_price;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ export interface WorldElementValue {
|
|||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
type: number;
|
type: number;
|
||||||
meta: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GuideLineAIQuery extends Record<string, SQLiteValue> {
|
export interface GuideLineAIQuery extends Record<string, SQLiteValue> {
|
||||||
@@ -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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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]);
|
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) {
|
if (result !== null) {
|
||||||
throw new Error(lang === 'fr' ? `La problématique existe déjà.` : `This issue already exists.`);
|
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) {
|
if (insertResult.changes > 0) {
|
||||||
return issueId;
|
return issueId;
|
||||||
} else {
|
} 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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
if (result.changes > 0) {
|
||||||
return worldId;
|
return worldId;
|
||||||
} else {
|
} 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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -562,7 +561,7 @@ export default class BookRepo {
|
|||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
for (const element of elements) {
|
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) {
|
if (result.changes <= 0) {
|
||||||
return false;
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
if (result.changes > 0) {
|
||||||
return elementId;
|
return elementId;
|
||||||
} else {
|
} 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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
if (result.changes > 0) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} 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;
|
return result.changes > 0;
|
||||||
}
|
}
|
||||||
} catch (e: unknown) {
|
} 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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
if (result.changes > 0) {
|
||||||
return actSummaryId;
|
return actSummaryId;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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 {
|
public static updateChapter(userId: string, chapterId: string, encryptedTitle: string, hashTitle: string, chapterOrder: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
let sql: string = `UPDATE book_chapter_infos
|
let sql: string = `UPDATE book_chapter_infos
|
||||||
SET summary=?,
|
SET summary=?,
|
||||||
goal=?,
|
goal=?
|
||||||
meta_chapter_info=?
|
|
||||||
WHERE chapter_id = ?
|
WHERE chapter_id = ?
|
||||||
AND act_id = ?
|
AND act_id = ?
|
||||||
AND book_id = ?`;
|
AND book_id = ?`;
|
||||||
const params: any[] = [summary, goal, meta, chapterId, actId, bookId];
|
const params: any[] = [summary, goal, chapterId, actId, bookId];
|
||||||
if (incidentId) {
|
if (incidentId) {
|
||||||
sql += ` AND incident_id=?`;
|
sql += ` AND incident_id=?`;
|
||||||
params.push(incidentId);
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
if (result.changes > 0) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
const contentId:string = System.createUniqueId();
|
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;
|
return insertResult.changes > 0;
|
||||||
}
|
}
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ export default class CharacterRepo {
|
|||||||
return result;
|
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;
|
let result: RunResult;
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
console.error(`DB Error: ${e.message}`);
|
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;
|
let result: RunResult;
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
console.error(`DB Error: ${e.message}`);
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ export default class LocationRepo {
|
|||||||
return result;
|
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;
|
let result: RunResult;
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
console.error(`DB Error: ${e.message}`);
|
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;
|
let result: RunResult;
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
console.error(`DB Error: ${e.message}`);
|
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;
|
let result: RunResult;
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
console.error(`DB Error: ${e.message}`);
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
|
|||||||
@@ -33,17 +33,6 @@ interface UserResponse {
|
|||||||
account_verified: boolean;
|
account_verified: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserQueryResponse extends Record<string, SQLiteValue> {
|
|
||||||
user_id: string;
|
|
||||||
first_name: string;
|
|
||||||
last_name: string;
|
|
||||||
username: string;
|
|
||||||
email: string;
|
|
||||||
plateform: string;
|
|
||||||
accountVerified: boolean;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserAccountQuery extends Record<string, SQLiteValue> {
|
export interface UserAccountQuery extends Record<string, SQLiteValue> {
|
||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
@@ -53,77 +42,21 @@ export interface UserAccountQuery extends Record<string, SQLiteValue> {
|
|||||||
user_meta: string;
|
user_meta: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TOTPQuery extends Record<string, SQLiteValue> {
|
|
||||||
user_id: number;
|
|
||||||
email: string;
|
|
||||||
totp_code: string;
|
|
||||||
totp_meta: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PasswordResponse extends Record<string, SQLiteValue> {
|
|
||||||
user_id: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GuideTourResult extends Record<string, SQLiteValue> {
|
export interface GuideTourResult extends Record<string, SQLiteValue> {
|
||||||
step_tour: string;
|
step_tour: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserSubscription extends Record<string, SQLiteValue> {
|
|
||||||
user_id: string;
|
|
||||||
sub_type: string;
|
|
||||||
sub_tier: number;
|
|
||||||
start_date: string;
|
|
||||||
end_date: string;
|
|
||||||
status: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserAPIKeyResult extends Record<string, SQLiteValue> {
|
|
||||||
brand: AIBrand,
|
|
||||||
key: string | null,
|
|
||||||
actif: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserAiUsageResult extends Record<string, SQLiteValue> {
|
|
||||||
token_in: string;
|
|
||||||
token_out: string;
|
|
||||||
total_price: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BasicUserCredentials extends Record<string, SQLiteValue> {
|
|
||||||
user_id: string,
|
|
||||||
first_name: string,
|
|
||||||
last_name: string,
|
|
||||||
username: string,
|
|
||||||
email: string,
|
|
||||||
user_meta: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class UserRepo {
|
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;
|
let result: RunResult;
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const query = `INSERT INTO erit_users (user_id, first_name, last_name, username, email, origin_email,
|
const query = `INSERT INTO erit_users (user_id, first_name, last_name, username, email, origin_email,
|
||||||
origin_username, plateform, social_id, password, term_accepted,
|
origin_username, term_accepted,
|
||||||
account_verified, user_meta)
|
account_verified)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 1, ?)`;
|
VALUES (?, ?, ?, ?, ?, ?, ?, 0, 1)`;
|
||||||
const values: (string | null | number)[] = [uuId, firstName, lastName, username, email, originEmail, originUsername, provider, socialId, password, meta];
|
const values: (string | null | number)[] = [uuId, firstName, lastName, username, email, originEmail, originUsername];
|
||||||
result = db.run(query, values);
|
result = db.run(query, values);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -145,7 +78,7 @@ export default class UserRepo {
|
|||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
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) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
console.error(`DB Error: ${e.message}`);
|
console.error(`DB Error: ${e.message}`);
|
||||||
@@ -161,10 +94,10 @@ export default class UserRepo {
|
|||||||
return result as UserInfosQueryResponse;
|
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 {
|
try {
|
||||||
const db: Database = System.getDb();
|
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;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -196,20 +129,4 @@ export default class UserRepo {
|
|||||||
}
|
}
|
||||||
return result as UserAccountQuery;
|
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 [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,13 +46,14 @@ export interface GuideLine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface GuideLineAI {
|
export interface GuideLineAI {
|
||||||
narrativeType: number;
|
narrativeType: number|null;
|
||||||
dialogueType: number;
|
dialogueType: number|null;
|
||||||
globalResume: string;
|
globalResume: string|null;
|
||||||
atmosphere: string;
|
atmosphere: string|null;
|
||||||
verbeTense: number;
|
verbeTense: number|null;
|
||||||
langue: number;
|
langue: number|null;
|
||||||
themes: string;
|
currentResume: string|null;
|
||||||
|
themes: string|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlotPoint {
|
export interface PlotPoint {
|
||||||
|
|||||||
Reference in New Issue
Block a user