import UserRepo, {UserAccountQuery, UserInfosQueryResponse} from "../repositories/user.repository.js"; import System from "../System.js"; import Book, {BookProps} from "./Book.js"; import {getUserEncryptionKey} from "../keyManager.js"; /** * 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; lastName: string; username: string; email: string; accountVerified: boolean; authorName: string; groupId: number; termsAccepted: boolean; guideTour: GuideTour[]; } /** * 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 firstName: string; private lastName: string; private username: string; private email: string; private accountVerified: boolean; private authorName: string; private groupId: number; private termsAccepted: boolean; /** * 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 = ''; this.username = ''; this.email = ''; this.accountVerified = false; this.authorName = ''; 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 { 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; } /** * 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 { const user: User = new User(userId); await user.getUserInfos(); const guideTourStatus: GuideTour[] = []; return { id: user.getId(), name: user.getFirstName(), lastName: user.getLastName(), username: user.getUsername(), email: user.getEmail(), accountVerified: user.isAccountVerified(), authorName: user.getAuthorName(), groupId: user.getGroupId(), termsAccepted: user.isTermsAccepted(), guideTour: guideTourStatus, }; } /** * 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 { 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 { 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 { 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; } }