- Deleted `CharacterComponent` and `CharacterDetail` files from the project. - Refactored related logic to improve code maintainability and reduce redundancy.
260 lines
11 KiB
TypeScript
260 lines
11 KiB
TypeScript
import { getUserEncryptionKey } from "../keyManager.js";
|
|
import System from "../System.js";
|
|
import SeriesRepo, { SeriesBookResult, SeriesListItem, SeriesResult } from "../repositories/series.repo.js";
|
|
import RemovedItem from "./RemovedItem.js";
|
|
|
|
export interface SeriesProps {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
coverImage: string | null;
|
|
}
|
|
|
|
export interface SeriesDetailProps {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
coverImage: string | null;
|
|
books: SeriesBookProps[];
|
|
}
|
|
|
|
export interface SeriesBookProps {
|
|
bookId: string;
|
|
title: string;
|
|
order: number;
|
|
coverImage: string | null;
|
|
}
|
|
|
|
export interface SeriesListItemProps {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
coverImage: string | null;
|
|
bookCount: number;
|
|
bookIds: string[];
|
|
}
|
|
|
|
export interface BooksOrderPost {
|
|
bookId: string;
|
|
order: number;
|
|
}
|
|
|
|
export default class Series {
|
|
/**
|
|
* Gets the list of all series for a user.
|
|
* @param userId - The unique identifier of the user
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns The list of series with decrypted names and descriptions
|
|
*/
|
|
public static getSeriesList(userId: string, lang: 'fr' | 'en' = 'fr'): SeriesListItemProps[] {
|
|
const userKey: string = getUserEncryptionKey(userId);
|
|
const seriesResults: SeriesListItem[] = SeriesRepo.fetchUserSeries(userId, lang);
|
|
|
|
return seriesResults.map((seriesItem: SeriesListItem): SeriesListItemProps => ({
|
|
id: seriesItem.series_id,
|
|
name: System.decryptDataWithUserKey(seriesItem.name, userKey),
|
|
description: seriesItem.description ? System.decryptDataWithUserKey(seriesItem.description, userKey) : '',
|
|
coverImage: seriesItem.cover_image,
|
|
bookCount: seriesItem.book_count,
|
|
bookIds: seriesItem.book_ids ? seriesItem.book_ids.split(',') : []
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Gets the detail of a series including its books.
|
|
* @param userId - The unique identifier of the user
|
|
* @param seriesId - The unique identifier of the series
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns The series detail with decrypted data
|
|
*/
|
|
public static getSeriesDetail(userId: string, seriesId: string, lang: 'fr' | 'en' = 'fr'): SeriesDetailProps {
|
|
const userKey: string = getUserEncryptionKey(userId);
|
|
|
|
const seriesResult: SeriesResult | null = SeriesRepo.fetchSeriesById(userId, seriesId, lang);
|
|
if (!seriesResult) {
|
|
throw new Error(lang === 'fr' ? 'Série non trouvée.' : 'Series not found.');
|
|
}
|
|
|
|
const booksResult: SeriesBookResult[] = SeriesRepo.fetchSeriesBooks(userId, seriesId, lang);
|
|
|
|
const books: SeriesBookProps[] = booksResult.map((book: SeriesBookResult) => ({
|
|
bookId: book.book_id,
|
|
title: System.decryptDataWithUserKey(book.title, userKey),
|
|
order: book.book_order,
|
|
coverImage: book.cover_image
|
|
}));
|
|
|
|
return {
|
|
id: seriesResult.series_id,
|
|
name: System.decryptDataWithUserKey(seriesResult.name, userKey),
|
|
description: seriesResult.description ? System.decryptDataWithUserKey(seriesResult.description, userKey) : '',
|
|
coverImage: seriesResult.cover_image,
|
|
books
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a new series.
|
|
* @param userId - The unique identifier of the user
|
|
* @param name - The name of the series
|
|
* @param description - The description of the series
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @param bookIds - Optional array of book IDs to add to the series
|
|
* @returns The created series ID
|
|
*/
|
|
public static createSeries(userId: string, name: string, description: string, lang: 'fr' | 'en' = 'fr', bookIds?: string[]): string {
|
|
const userKey: string = getUserEncryptionKey(userId);
|
|
const seriesId: string = System.createUniqueId();
|
|
|
|
const encryptedName: string = System.encryptDataWithUserKey(name, userKey);
|
|
const hashedName: string = System.hashElement(name);
|
|
const encryptedDescription: string | null = description ? System.encryptDataWithUserKey(description, userKey) : null;
|
|
|
|
SeriesRepo.insertSeries(seriesId, userId, encryptedName, hashedName, encryptedDescription, lang);
|
|
|
|
if (bookIds && bookIds.length > 0) {
|
|
for (let i: number = 0; i < bookIds.length; i++) {
|
|
SeriesRepo.addBookToSeries(seriesId, bookIds[i], i + 1, lang);
|
|
}
|
|
}
|
|
|
|
return seriesId;
|
|
}
|
|
|
|
/**
|
|
* Updates an existing series.
|
|
* @param userId - The unique identifier of the user
|
|
* @param seriesId - The unique identifier of the series
|
|
* @param name - The name of the series
|
|
* @param description - The description of the series
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns True if the update was successful
|
|
*/
|
|
public static updateSeries(userId: string, seriesId: string, name: string, description: string, lang: 'fr' | 'en' = 'fr'): boolean {
|
|
const exists: boolean = SeriesRepo.isSeriesExist(userId, seriesId, lang);
|
|
if (!exists) {
|
|
throw new Error(lang === 'fr' ? 'Série non trouvée.' : 'Series not found.');
|
|
}
|
|
|
|
const userKey: string = getUserEncryptionKey(userId);
|
|
|
|
const encryptedName: string = System.encryptDataWithUserKey(name, userKey);
|
|
const hashedName: string = System.hashElement(name);
|
|
const encryptedDescription: string | null = description ? System.encryptDataWithUserKey(description, userKey) : null;
|
|
|
|
return SeriesRepo.updateSeries(userId, seriesId, encryptedName, hashedName, encryptedDescription, lang);
|
|
}
|
|
|
|
/**
|
|
* Deletes a series.
|
|
* @param userId - The unique identifier of the user
|
|
* @param seriesId - The unique identifier of the series
|
|
* @param deletedAt - The timestamp of deletion
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns True if the deletion was successful
|
|
*/
|
|
public static deleteSeries(userId: string, seriesId: string, deletedAt: number = System.timeStampInSeconds(), lang: 'fr' | 'en' = 'fr'): boolean {
|
|
const exists: boolean = SeriesRepo.isSeriesExist(userId, seriesId, lang);
|
|
if (!exists) {
|
|
throw new Error(lang === 'fr' ? 'Série non trouvée.' : 'Series not found.');
|
|
}
|
|
|
|
const deleted: boolean = SeriesRepo.deleteSeries(userId, seriesId, lang);
|
|
if (deleted) {
|
|
RemovedItem.deleteTracker(userId, null, 'book_series', seriesId, deletedAt, lang);
|
|
}
|
|
return deleted;
|
|
}
|
|
|
|
/**
|
|
* Adds a book to a series.
|
|
* @param userId - The unique identifier of the user
|
|
* @param seriesId - The unique identifier of the series
|
|
* @param bookId - The unique identifier of the book
|
|
* @param order - The order of the book in the series
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns True if the addition was successful
|
|
*/
|
|
public static addBookToSeries(userId: string, seriesId: string, bookId: string, order: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
|
const exists: boolean = SeriesRepo.isSeriesExist(userId, seriesId, lang);
|
|
if (!exists) {
|
|
throw new Error(lang === 'fr' ? 'Série non trouvée.' : 'Series not found.');
|
|
}
|
|
|
|
return SeriesRepo.addBookToSeries(seriesId, bookId, order, lang);
|
|
}
|
|
|
|
/**
|
|
* Removes a book from a series.
|
|
* @param userId - The unique identifier of the user
|
|
* @param seriesId - The unique identifier of the series
|
|
* @param bookId - The unique identifier of the book
|
|
* @param deletedAt - The timestamp of deletion
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns True if the removal was successful
|
|
*/
|
|
public static removeBookFromSeries(userId: string, seriesId: string, bookId: string, deletedAt: number = System.timeStampInSeconds(), lang: 'fr' | 'en' = 'fr'): boolean {
|
|
const exists: boolean = SeriesRepo.isSeriesExist(userId, seriesId, lang);
|
|
if (!exists) {
|
|
throw new Error(lang === 'fr' ? 'Série non trouvée.' : 'Series not found.');
|
|
}
|
|
|
|
const deleted: boolean = SeriesRepo.removeBookFromSeries(seriesId, bookId, lang);
|
|
if (deleted) {
|
|
RemovedItem.deleteTracker(userId, null, 'series_books', `${seriesId}_${bookId}`, deletedAt, lang);
|
|
}
|
|
return deleted;
|
|
}
|
|
|
|
/**
|
|
* Updates the order of books in a series.
|
|
* @param userId - The unique identifier of the user
|
|
* @param seriesId - The unique identifier of the series
|
|
* @param booksOrder - An array of {bookId, order} objects
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns True if the update was successful
|
|
*/
|
|
public static updateBooksOrder(userId: string, seriesId: string, booksOrder: BooksOrderPost[], lang: 'fr' | 'en' = 'fr'): boolean {
|
|
const exists: boolean = SeriesRepo.isSeriesExist(userId, seriesId, lang);
|
|
if (!exists) {
|
|
throw new Error(lang === 'fr' ? 'Série non trouvée.' : 'Series not found.');
|
|
}
|
|
|
|
return SeriesRepo.updateBooksOrder(seriesId, booksOrder, lang);
|
|
}
|
|
|
|
/**
|
|
* Gets the series ID for a book if it belongs to one.
|
|
* @param bookId - The unique identifier of the book
|
|
* @returns The series ID or null
|
|
*/
|
|
public static getSeriesIdForBook(bookId: string): string | null {
|
|
return SeriesRepo.getSeriesIdForBook(bookId);
|
|
}
|
|
|
|
/**
|
|
* Gets only the books of a series (without series details).
|
|
* @param userId - The unique identifier of the user
|
|
* @param seriesId - The unique identifier of the series
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns The list of books in the series
|
|
*/
|
|
public static getSeriesBooks(userId: string, seriesId: string, lang: 'fr' | 'en' = 'fr'): SeriesBookProps[] {
|
|
const userKey: string = getUserEncryptionKey(userId);
|
|
|
|
const exists: boolean = SeriesRepo.isSeriesExist(userId, seriesId, lang);
|
|
if (!exists) {
|
|
throw new Error(lang === 'fr' ? 'Série non trouvée.' : 'Series not found.');
|
|
}
|
|
|
|
const booksResult: SeriesBookResult[] = SeriesRepo.fetchSeriesBooks(userId, seriesId, lang);
|
|
|
|
return booksResult.map((book: SeriesBookResult): SeriesBookProps => ({
|
|
bookId: book.book_id,
|
|
title: System.decryptDataWithUserKey(book.title, userKey),
|
|
order: book.book_order,
|
|
coverImage: book.cover_image
|
|
}));
|
|
}
|
|
}
|