refacto(services): fetchFromApi simpler + bug libraries fetch

This commit is contained in:
Julien Froidefond
2025-02-23 10:42:04 +01:00
parent 9c1889cf7d
commit fd2632c394
6 changed files with 90 additions and 84 deletions

View File

@@ -9,6 +9,11 @@ interface KomgaRequestInit extends RequestInit {
isImage?: boolean;
}
interface KomgaUrlBuilder {
path: string;
params?: Record<string, string>;
}
export abstract class BaseApiService {
protected static async getKomgaConfig(): Promise<AuthConfig> {
try {
@@ -78,10 +83,24 @@ export abstract class BaseApiService {
}
protected static async fetchFromApi<T>(
url: string,
headers: Headers,
urlBuilder: KomgaUrlBuilder,
headersOptions = {},
options: KomgaRequestInit = {}
): Promise<T> {
const config = await this.getKomgaConfig();
const { path, params } = urlBuilder;
const url = this.buildUrl(config, path, params);
const headers = this.getAuthHeaders(config);
if (headersOptions) {
for (const [key, value] of Object.entries(headersOptions)) {
headers.set(key as string, value as string);
}
}
// console.log("Fetching from", url);
// console.log("Headers", headers);
// console.log("headersOptions", headersOptions);
// console.log("options", options);
const response = await fetch(url, { headers, ...options });
if (!response.ok) {

View File

@@ -6,23 +6,16 @@ import { PreferencesService } from "./preferences.service";
export class BookService extends BaseApiService {
static async getBook(bookId: string): Promise<{ book: KomgaBook; pages: number[] }> {
try {
const config = await this.getKomgaConfig();
const headers = this.getAuthHeaders(config);
return this.fetchWithCache<{ book: KomgaBook; pages: number[] }>(
`book-${bookId}`,
async () => {
// Récupération des détails du tome
const book = await this.fetchFromApi<KomgaBook>(
this.buildUrl(config, `books/${bookId}`),
headers
);
const book = await this.fetchFromApi<KomgaBook>({ path: `books/${bookId}` });
// Récupération des pages du tome
const pages = await this.fetchFromApi<{ number: number }[]>(
this.buildUrl(config, `books/${bookId}/pages`),
headers
);
const pages = await this.fetchFromApi<{ number: number }[]>({
path: `books/${bookId}/pages`,
});
return {
book,

View File

@@ -12,45 +12,47 @@ interface HomeData {
export class HomeService extends BaseApiService {
static async getHomeData(): Promise<HomeData> {
try {
const config = await this.getKomgaConfig();
const headers = this.getAuthHeaders(config);
// Construction des URLs avec des paramètres optimisés
const ongoingUrl = this.buildUrl(config, "series", {
read_status: "IN_PROGRESS",
sort: "readDate,desc",
page: "0",
size: "10",
media_status: "READY",
});
const recentlyReadUrl = this.buildUrl(config, "books/latest", {
page: "0",
size: "10",
media_status: "READY",
});
const onDeckUrl = this.buildUrl(config, "books/ondeck", {
page: "0",
size: "10",
media_status: "READY",
});
// Appels API parallèles avec cache individuel
const [ongoing, recentlyRead, onDeck] = await Promise.all([
this.fetchWithCache<LibraryResponse<KomgaSeries>>(
"home-ongoing",
async () => this.fetchFromApi<LibraryResponse<KomgaSeries>>(ongoingUrl, headers),
async () =>
this.fetchFromApi<LibraryResponse<KomgaSeries>>({
path: "series",
params: {
read_status: "IN_PROGRESS",
sort: "readDate,desc",
page: "0",
size: "10",
media_status: "READY",
},
}),
"HOME"
),
this.fetchWithCache<LibraryResponse<KomgaBook>>(
"home-recently-read",
async () => this.fetchFromApi<LibraryResponse<KomgaBook>>(recentlyReadUrl, headers),
async () =>
this.fetchFromApi<LibraryResponse<KomgaBook>>({
path: "books/latest",
params: {
page: "0",
size: "10",
media_status: "READY",
},
}),
"HOME"
),
this.fetchWithCache<LibraryResponse<KomgaBook>>(
"home-on-deck",
async () => this.fetchFromApi<LibraryResponse<KomgaBook>>(onDeckUrl, headers),
async () =>
this.fetchFromApi<LibraryResponse<KomgaBook>>({
path: "books/ondeck",
params: {
page: "0",
size: "10",
media_status: "READY",
},
}),
"HOME"
),
]);

View File

@@ -8,15 +8,12 @@ interface ImageResponse {
export class ImageService extends BaseApiService {
static async getImage(path: string): Promise<ImageResponse> {
try {
const config = await this.getKomgaConfig();
const url = this.buildUrl(config, path);
const headers = this.getAuthHeaders(config);
headers.set("Accept", "image/jpeg, image/png, image/gif, image/webp, */*");
const headers = { Accept: "image/jpeg, image/png, image/gif, image/webp, */*" };
return this.fetchWithCache<ImageResponse>(
`image-${path}`,
async () => {
const response = await this.fetchFromApi<Response>(url, headers, { isImage: true });
const response = await this.fetchFromApi<Response>({ path }, headers, { isImage: true });
const contentType = response.headers.get("content-type");
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);

View File

@@ -6,13 +6,9 @@ import { serverCacheService } from "./server-cache.service";
export class LibraryService extends BaseApiService {
static async getLibraries(): Promise<Library[]> {
try {
const config = await this.getKomgaConfig();
const url = this.buildUrl(config, "libraries");
const headers = this.getAuthHeaders(config);
return this.fetchWithCache<Library[]>(
"libraries",
async () => this.fetchFromApi<Library[]>(url, headers),
async () => this.fetchFromApi<Library[]>({ path: "libraries" }),
"LIBRARIES"
);
} catch (error) {
@@ -31,12 +27,7 @@ export class LibraryService extends BaseApiService {
static async getAllLibrarySeries(libraryId: string): Promise<Series[]> {
try {
const config = await this.getKomgaConfig();
const url = this.buildUrl(config, "series/list", {
size: "1000", // On récupère un maximum de séries
});
const headers = this.getAuthHeaders(config);
headers.set("Content-Type", "application/json");
const headers = { "Content-Type": "application/json" };
const searchBody = {
condition: {
@@ -51,10 +42,19 @@ export class LibraryService extends BaseApiService {
const response = await this.fetchWithCache<LibraryResponse<Series>>(
cacheKey,
async () =>
this.fetchFromApi<LibraryResponse<Series>>(url, headers, {
method: "POST",
body: JSON.stringify(searchBody),
}),
this.fetchFromApi<LibraryResponse<Series>>(
{
path: "series/list",
params: {
size: "1000", // On récupère un maximum de livres
},
},
headers,
{
method: "POST",
body: JSON.stringify(searchBody),
}
),
"SERIES"
);
@@ -74,7 +74,6 @@ export class LibraryService extends BaseApiService {
try {
// Récupérer toutes les séries depuis le cache
const allSeries = await this.getAllLibrarySeries(libraryId);
// Filtrer les séries
let filteredSeries = allSeries;

View File

@@ -9,13 +9,9 @@ import { serverCacheService } from "./server-cache.service";
export class SeriesService extends BaseApiService {
static async getSeries(seriesId: string): Promise<KomgaSeries> {
try {
const config = await this.getKomgaConfig();
const url = this.buildUrl(config, `series/${seriesId}`);
const headers = this.getAuthHeaders(config);
return this.fetchWithCache<KomgaSeries>(
`series-${seriesId}`,
async () => this.fetchFromApi<KomgaSeries>(url, headers),
async () => this.fetchFromApi<KomgaSeries>({ path: `series/${seriesId}` }),
"SERIES"
);
} catch (error) {
@@ -29,12 +25,7 @@ export class SeriesService extends BaseApiService {
static async getAllSeriesBooks(seriesId: string): Promise<KomgaBook[]> {
try {
const config = await this.getKomgaConfig();
const url = this.buildUrl(config, "books/list", {
size: "1000", // On récupère un maximum de livres
});
const headers = this.getAuthHeaders(config);
headers.set("Content-Type", "application/json");
const headers = { "Content-Type": "application/json" };
const searchBody = {
condition: {
@@ -49,10 +40,19 @@ export class SeriesService extends BaseApiService {
const response = await this.fetchWithCache<LibraryResponse<KomgaBook>>(
cacheKey,
async () =>
this.fetchFromApi<LibraryResponse<KomgaBook>>(url, headers, {
method: "POST",
body: JSON.stringify(searchBody),
}),
this.fetchFromApi<LibraryResponse<KomgaBook>>(
{
path: "books/list",
params: {
size: "1000", // On récupère un maximum de livres
},
},
headers,
{
method: "POST",
body: JSON.stringify(searchBody),
}
),
"BOOKS"
);
@@ -131,17 +131,13 @@ export class SeriesService extends BaseApiService {
static async getFirstBook(seriesId: string): Promise<string> {
try {
const config = await this.getKomgaConfig();
const url = this.buildUrl(config, `series/${seriesId}/books`);
const headers = this.getAuthHeaders(config);
return this.fetchWithCache<string>(
`series-first-book-${seriesId}`,
async () => {
const data = await this.fetchFromApi<LibraryResponse<KomgaBook>>(
`series/${seriesId}/books?page=0&size=1`,
headers
);
const data = await this.fetchFromApi<LibraryResponse<KomgaBook>>({
path: `series/${seriesId}/books`,
params: { page: "0", size: "1" },
});
if (!data.content || data.content.length === 0) {
throw new Error("Aucun livre trouvé dans la série");
}