Add models for guidelines, incidents, plot points, issues, acts, and world data

- Introduced new models: `GuideLine`, `Incident`, `PlotPoint`, `Issue`, `Act`, and `World` for managing book-related entities.
- Integrated encryption/decryption for sensitive properties in all models using user-specific keys.
- Added methods for CRUD operations and synchronization workflows with error handling and multilingual support.
- Improved maintainability with JSDoc comments and streamlined queries.
This commit is contained in:
natreex
2026-01-12 13:38:10 -05:00
parent d9bf089e32
commit cf6fb97bf0
19 changed files with 3643 additions and 2509 deletions

View File

@@ -3,24 +3,36 @@ import System from "../System.js";
import Book, {BookProps} from "./Book.js";
import {getUserEncryptionKey} from "../keyManager.js";
interface UserAccount{
firstName:string;
lastName:string;
username:string
authorName:string;
email:string;
/**
* Represents a user account with basic profile information.
*/
interface UserAccount {
firstName: string;
lastName: string;
username: string;
authorName: string;
email: string;
}
/**
* Represents the guide tour completion status for various features.
*/
export interface GuideTour {
[key: string]: boolean;
}
/**
* Summary information for a book associated with a user.
*/
interface BookSummary {
bookId: string;
title: string;
subTitle?: string;
}
/**
* Complete user information response including profile data and associated books.
*/
export interface UserInfoResponse {
id: string;
name: string;
@@ -31,13 +43,17 @@ export interface UserInfoResponse {
authorName: string;
groupId: number;
termsAccepted: boolean;
guideTour: any[];
guideTour: GuideTour[];
books: BookSummary[];
}
export default class User{
/**
* Represents a user entity with encrypted personal information storage.
* Handles user data retrieval, creation, and updates with AES-256-CBC encryption.
*/
export default class User {
private readonly id:string;
private readonly id: string;
private firstName: string;
private lastName: string;
private username: string;
@@ -47,7 +63,11 @@ export default class User{
private groupId: number;
private termsAccepted: boolean;
constructor(id:string){
/**
* Creates a new User instance with the specified identifier.
* @param id - The unique identifier for the user
*/
constructor(id: string) {
this.id = id;
this.firstName = '';
this.lastName = '';
@@ -58,25 +78,35 @@ export default class User{
this.groupId = 0;
this.termsAccepted = false;
}
/**
* Fetches and decrypts the user's information from the database.
* Populates all instance properties with the decrypted values.
* @returns A promise that resolves when user information has been loaded
*/
public async getUserInfos(): Promise<void> {
const data: UserInfosQueryResponse = UserRepo.fetchUserInfos(this.id);
const userKey:string = getUserEncryptionKey(this.id)
this.firstName = System.decryptDataWithUserKey(data.first_name, userKey);
this.lastName = System.decryptDataWithUserKey(data.last_name, userKey);
this.username = System.decryptDataWithUserKey(data.username, userKey);
this.email = System.decryptDataWithUserKey(data.email, userKey);
this.accountVerified = data.account_verified === 1;
this.authorName = data.author_name ? System.decryptDataWithUserKey(data.author_name, userKey) : '';
this.groupId = data.user_group ? data.user_group : 0;
this.termsAccepted = data.term_accepted === 1;
const userInfosData: UserInfosQueryResponse = UserRepo.fetchUserInfos(this.id);
const userEncryptionKey: string = getUserEncryptionKey(this.id);
this.firstName = System.decryptDataWithUserKey(userInfosData.first_name, userEncryptionKey);
this.lastName = System.decryptDataWithUserKey(userInfosData.last_name, userEncryptionKey);
this.username = System.decryptDataWithUserKey(userInfosData.username, userEncryptionKey);
this.email = System.decryptDataWithUserKey(userInfosData.email, userEncryptionKey);
this.accountVerified = userInfosData.account_verified === 1;
this.authorName = userInfosData.author_name ? System.decryptDataWithUserKey(userInfosData.author_name, userEncryptionKey) : '';
this.groupId = userInfosData.user_group ? userInfosData.user_group : 0;
this.termsAccepted = userInfosData.term_accepted === 1;
}
public static async returnUserInfos(userId: string):Promise<UserInfoResponse> {
/**
* Retrieves complete user information including associated books.
* @param userId - The unique identifier of the user to fetch
* @returns A promise resolving to the complete user information response
*/
public static async returnUserInfos(userId: string): Promise<UserInfoResponse> {
const user: User = new User(userId);
await user.getUserInfos();
const books: BookProps[] = await Book.getBooks(userId);
const guideTour: GuideTour[] = [];
const userBooks: BookProps[] = await Book.getBooks(userId);
const guideTourStatus: GuideTour[] = [];
return {
id: user.getId(),
name: user.getFirstName(),
@@ -87,95 +117,194 @@ export default class User{
authorName: user.getAuthorName(),
groupId: user.getGroupId(),
termsAccepted: user.isTermsAccepted(),
guideTour: guideTour,
books: books.map((book: BookProps):BookSummary => {
guideTour: guideTourStatus,
books: userBooks.map((book: BookProps): BookSummary => {
return {
bookId: book.id,
title: book.title,
subTitle: book.subTitle,
};
})
}
}
public static async addUser(userId:string,firstName: string, lastName: string, username: string, email: string, notEncryptPassword: string, lang: 'fr' | 'en' = 'fr'): Promise<string> {
const originEmail:string = System.hashElement(email);
const originUsername:string = System.hashElement(username);
const userKey: string = getUserEncryptionKey(userId);
const encryptFirstName: string = System.encryptDataWithUserKey(firstName, userKey);
const encryptLastName: string = System.encryptDataWithUserKey(lastName, userKey);
const encryptUsername: string = System.encryptDataWithUserKey(username, userKey);
const encryptEmail: string = System.encryptDataWithUserKey(email, userKey);
const originalEmail: string = System.hashElement(email);
const originalUsername: string = System.hashElement(username);
return UserRepo.insertUser(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail,lang);
}
public static async updateUserInfos(userKey: string, userId: string, firstName: string, lastName: string, username: string, email: string, authorName?: string, lang: 'fr' | 'en' = 'fr'): Promise<boolean> {
const encryptFirstName:string = System.encryptDataWithUserKey(firstName,userKey);
const encryptLastName:string = System.encryptDataWithUserKey(lastName,userKey);
const encryptUsername:string = System.encryptDataWithUserKey(username,userKey);
const encryptEmail:string = System.encryptDataWithUserKey(email,userKey);
const originalEmail:string = System.hashElement(email);
const originalUsername:string = System.hashElement(username);
let encryptAuthorName:string = '';
let originalAuthorName: string = '';
if (authorName){
encryptAuthorName = System.encryptDataWithUserKey(authorName,userKey);
originalAuthorName = System.hashElement(authorName);
}
return UserRepo.updateUserInfos(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail, originalAuthorName, encryptAuthorName, lang);
}
public static async getUserAccountInformation(userId: string): Promise<UserAccount> {
const data: UserAccountQuery = UserRepo.fetchAccountInformation(userId);
const userKey:string = getUserEncryptionKey(userId)
const userName: string = data.first_name ? System.decryptDataWithUserKey(data.first_name, userKey) : '';
const lastName: string = data.last_name ? System.decryptDataWithUserKey(data.last_name, userKey) : '';
const username: string = data.username ? System.decryptDataWithUserKey(data.username, userKey) : '';
const authorName: string = data.author_name ? System.decryptDataWithUserKey(data.author_name, userKey) : '';
const email: string = data.email ? System.decryptDataWithUserKey(data.email, userKey) : '';
return {
firstName: userName,
lastName: lastName,
username: username,
authorName: authorName,
email: email
};
}
/**
* Creates a new user in the database with encrypted personal information.
* @param userId - The unique identifier for the new user
* @param firstName - The user's first name (will be encrypted)
* @param lastName - The user's last name (will be encrypted)
* @param username - The user's username (will be encrypted and hashed)
* @param email - The user's email address (will be encrypted and hashed)
* @param notEncryptPassword - The user's password in plain text (unused in current implementation)
* @param lang - The preferred language for the user ('fr' or 'en'), defaults to 'fr'
* @returns A promise resolving to the created user's identifier
*/
public static async addUser(
userId: string,
firstName: string,
lastName: string,
username: string,
email: string,
notEncryptPassword: string,
lang: 'fr' | 'en' = 'fr'
): Promise<string> {
const userEncryptionKey: string = getUserEncryptionKey(userId);
const encryptedFirstName: string = System.encryptDataWithUserKey(firstName, userEncryptionKey);
const encryptedLastName: string = System.encryptDataWithUserKey(lastName, userEncryptionKey);
const encryptedUsername: string = System.encryptDataWithUserKey(username, userEncryptionKey);
const encryptedEmail: string = System.encryptDataWithUserKey(email, userEncryptionKey);
const hashedEmail: string = System.hashElement(email);
const hashedUsername: string = System.hashElement(username);
return UserRepo.insertUser(
userId,
encryptedFirstName,
encryptedLastName,
encryptedUsername,
hashedUsername,
encryptedEmail,
hashedEmail,
lang
);
}
/**
* Updates an existing user's profile information in the database.
* @param userKey - The encryption key for the user's data
* @param userId - The unique identifier of the user to update
* @param firstName - The updated first name (will be encrypted)
* @param lastName - The updated last name (will be encrypted)
* @param username - The updated username (will be encrypted and hashed)
* @param email - The updated email address (will be encrypted and hashed)
* @param authorName - The optional author/pen name (will be encrypted and hashed if provided)
* @param lang - The preferred language for the user ('fr' or 'en'), defaults to 'fr'
* @returns A promise resolving to true if the update was successful
*/
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 encryptedFirstName: string = System.encryptDataWithUserKey(firstName, userKey);
const encryptedLastName: string = System.encryptDataWithUserKey(lastName, userKey);
const encryptedUsername: string = System.encryptDataWithUserKey(username, userKey);
const encryptedEmail: string = System.encryptDataWithUserKey(email, userKey);
const hashedEmail: string = System.hashElement(email);
const hashedUsername: string = System.hashElement(username);
let encryptedAuthorName: string = '';
let hashedAuthorName: string = '';
if (authorName) {
encryptedAuthorName = System.encryptDataWithUserKey(authorName, userKey);
hashedAuthorName = System.hashElement(authorName);
}
return UserRepo.updateUserInfos(
userId,
encryptedFirstName,
encryptedLastName,
encryptedUsername,
hashedUsername,
encryptedEmail,
hashedEmail,
hashedAuthorName,
encryptedAuthorName,
lang
);
}
/**
* Retrieves and decrypts the user's account information from the database.
* @param userId - The unique identifier of the user
* @returns A promise resolving to the decrypted user account information
*/
public static async getUserAccountInformation(userId: string): Promise<UserAccount> {
const accountData: UserAccountQuery = UserRepo.fetchAccountInformation(userId);
const userEncryptionKey: string = getUserEncryptionKey(userId);
const decryptedFirstName: string = accountData.first_name ? System.decryptDataWithUserKey(accountData.first_name, userEncryptionKey) : '';
const decryptedLastName: string = accountData.last_name ? System.decryptDataWithUserKey(accountData.last_name, userEncryptionKey) : '';
const decryptedUsername: string = accountData.username ? System.decryptDataWithUserKey(accountData.username, userEncryptionKey) : '';
const decryptedAuthorName: string = accountData.author_name ? System.decryptDataWithUserKey(accountData.author_name, userEncryptionKey) : '';
const decryptedEmail: string = accountData.email ? System.decryptDataWithUserKey(accountData.email, userEncryptionKey) : '';
return {
firstName: decryptedFirstName,
lastName: decryptedLastName,
username: decryptedUsername,
authorName: decryptedAuthorName,
email: decryptedEmail
};
}
/**
* Gets the unique identifier of the user.
* @returns The user's unique identifier
*/
public getId(): string {
return this.id;
}
/**
* Gets the user's first name.
* @returns The user's first name
*/
public getFirstName(): string {
return this.firstName;
}
/**
* Gets the user's last name.
* @returns The user's last name
*/
public getLastName(): string {
return this.lastName;
}
/**
* Gets the user's username.
* @returns The user's username
*/
public getUsername(): string {
return this.username;
}
/**
* Gets the user's email address.
* @returns The user's email address
*/
public getEmail(): string {
return this.email;
}
/**
* Checks if the user's account has been verified.
* @returns True if the account is verified, false otherwise
*/
public isAccountVerified(): boolean {
return this.accountVerified;
}
/**
* Checks if the user has accepted the terms of service.
* @returns True if the terms have been accepted, false otherwise
*/
public isTermsAccepted(): boolean {
return this.termsAccepted;
}
/**
* Gets the user's group identifier.
* @returns The user's group identifier
*/
public getGroupId(): number {
return this.groupId;
}
/**
* Gets the user's author/pen name.
* @returns The user's author name
*/
public getAuthorName(): string {
return this.authorName;
}