refactor: remove caching-related API endpoints and configurations, update preferences structure, and clean up unused services
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 7m22s

This commit is contained in:
Julien Froidefond
2026-01-03 18:55:12 +01:00
parent acd26ea427
commit 512e9a480f
49 changed files with 244 additions and 4073 deletions

View File

@@ -1,50 +1,27 @@
import { BaseApiService } from "./base-api.service";
import type { LibraryResponse } from "@/types/library";
import type { KomgaBook, KomgaSeries, TTLConfig } from "@/types/komga";
import type { KomgaBook, KomgaSeries } from "@/types/komga";
import { BookService } from "./book.service";
import type { ImageResponse } from "./image.service";
import { ImageService } from "./image.service";
import { PreferencesService } from "./preferences.service";
import { ConfigDBService } from "./config-db.service";
import { getServerCacheService } from "./server-cache.service";
import { ERROR_CODES } from "../../constants/errorCodes";
import { AppError } from "../../utils/errors";
import type { UserPreferences } from "@/types/preferences";
import type { ServerCacheService } from "./server-cache.service";
import logger from "@/lib/logger";
// Cache HTTP navigateur : 30 jours (immutable car les images ne changent pas)
const IMAGE_CACHE_MAX_AGE = 2592000;
export class SeriesService extends BaseApiService {
private static async getImageCacheMaxAge(): Promise<number> {
try {
const ttlConfig: TTLConfig | null = await ConfigDBService.getTTLConfig();
const maxAge = ttlConfig?.imageCacheMaxAge ?? 2592000;
return maxAge;
} catch (error) {
logger.error({ err: error }, "[ImageCache] Error fetching TTL config");
return 2592000; // 30 jours par défaut en cas d'erreur
}
}
static async getSeries(seriesId: string): Promise<KomgaSeries> {
try {
return this.fetchWithCache<KomgaSeries>(
`series-${seriesId}`,
async () => this.fetchFromApi<KomgaSeries>({ path: `series/${seriesId}` }),
"SERIES"
);
return this.fetchFromApi<KomgaSeries>({ path: `series/${seriesId}` });
} catch (error) {
throw new AppError(ERROR_CODES.SERIES.FETCH_ERROR, {}, error);
}
}
static async invalidateSeriesCache(seriesId: string): Promise<void> {
try {
const cacheService = await getServerCacheService();
await cacheService.delete(`series-${seriesId}`);
} catch (error) {
throw new AppError(ERROR_CODES.CACHE.DELETE_ERROR, {}, error);
}
}
static async getSeriesBooks(
seriesId: string,
page: number = 0,
@@ -96,28 +73,19 @@ export class SeriesService extends BaseApiService {
const searchBody = { condition };
// Clé de cache incluant tous les paramètres
const cacheKey = `series-${seriesId}-books-p${page}-s${size}-u${unreadOnly}`;
const params: Record<string, string | string[]> = {
page: String(page),
size: String(size),
sort: "number,asc",
};
const response = await this.fetchWithCache<LibraryResponse<KomgaBook>>(
cacheKey,
async () => {
const params: Record<string, string | string[]> = {
page: String(page),
size: String(size),
sort: "number,asc",
};
return this.fetchFromApi<LibraryResponse<KomgaBook>>(
{ path: "books/list", params },
headers,
{
method: "POST",
body: JSON.stringify(searchBody),
}
);
},
"BOOKS"
const response = await this.fetchFromApi<LibraryResponse<KomgaBook>>(
{ path: "books/list", params },
headers,
{
method: "POST",
body: JSON.stringify(searchBody),
}
);
// Filtrer uniquement les livres supprimés côté client (léger)
@@ -133,36 +101,17 @@ export class SeriesService extends BaseApiService {
}
}
static async invalidateSeriesBooksCache(seriesId: string): Promise<void> {
try {
const cacheService: ServerCacheService = await getServerCacheService();
// Invalider toutes les clés de cache pour cette série
// Format: series-{id}-books-p{page}-s{size}-u{unread}
await cacheService.deleteAll(`series-${seriesId}-books-`);
} catch (error) {
throw new AppError(ERROR_CODES.CACHE.DELETE_ERROR, {}, error);
}
}
static async getFirstBook(seriesId: string): Promise<string> {
try {
return this.fetchWithCache<string>(
`series-first-book-${seriesId}`,
async () => {
const data: LibraryResponse<KomgaBook> = await this.fetchFromApi<
LibraryResponse<KomgaBook>
>({
path: `series/${seriesId}/books`,
params: { page: "0", size: "1" },
});
if (!data.content || data.content.length === 0) {
throw new AppError(ERROR_CODES.SERIES.NO_BOOKS_FOUND);
}
const data: LibraryResponse<KomgaBook> = await this.fetchFromApi<LibraryResponse<KomgaBook>>({
path: `series/${seriesId}/books`,
params: { page: "0", size: "1" },
});
if (!data.content || data.content.length === 0) {
throw new AppError(ERROR_CODES.SERIES.NO_BOOKS_FOUND);
}
return data.content[0].id;
},
"SERIES"
);
return data.content[0].id;
} catch (error) {
logger.error({ err: error }, "Erreur lors de la récupération du premier livre");
throw new AppError(ERROR_CODES.SERIES.FETCH_ERROR, {}, error);
@@ -173,7 +122,6 @@ export class SeriesService extends BaseApiService {
try {
// Récupérer les préférences de l'utilisateur
const preferences: UserPreferences = await PreferencesService.getPreferences();
const maxAge = await this.getImageCacheMaxAge();
// Si l'utilisateur préfère les vignettes, utiliser la miniature
if (preferences.showThumbnails) {
@@ -181,7 +129,7 @@ export class SeriesService extends BaseApiService {
return new Response(response.buffer.buffer as ArrayBuffer, {
headers: {
"Content-Type": response.contentType || "image/jpeg",
"Cache-Control": `public, max-age=${maxAge}, immutable`,
"Cache-Control": `public, max-age=${IMAGE_CACHE_MAX_AGE}, immutable`,
},
});
}