diff --git a/next.config.js b/next.config.js index 7d3fb1b..87a886e 100644 --- a/next.config.js +++ b/next.config.js @@ -7,6 +7,8 @@ const nextConfig = { }; return config; }, + // Configuration pour améliorer la résolution DNS + serverExternalPackages: ['dns'], }; module.exports = nextConfig; diff --git a/package.json b/package.json index 8ed5b52..785a8f1 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-i18next": "^15.4.1", + "react-zoom-pan-pinch": "^3.7.0", "sharp": "0.33.2", "tailwind-merge": "^3.0.2", "tailwindcss-animate": "1.0.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 501196c..e749ab9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: react-i18next: specifier: ^15.4.1 version: 15.7.4(i18next@24.2.3(typescript@5.3.3))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.3.3) + react-zoom-pan-pinch: + specifier: ^3.7.0 + version: 3.7.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) sharp: specifier: 0.33.2 version: 0.33.2 @@ -2729,6 +2732,13 @@ packages: '@types/react': optional: true + react-zoom-pan-pinch@3.7.0: + resolution: {integrity: sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + react: '*' + react-dom: '*' + react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -5859,6 +5869,11 @@ snapshots: optionalDependencies: '@types/react': 18.2.64 + react-zoom-pan-pinch@3.7.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react@18.2.0: dependencies: loose-envify: 1.4.0 diff --git a/src/components/ui/ErrorMessage.tsx b/src/components/ui/ErrorMessage.tsx index c54ca9b..72d3322 100644 --- a/src/components/ui/ErrorMessage.tsx +++ b/src/components/ui/ErrorMessage.tsx @@ -13,7 +13,9 @@ export const ErrorMessage = ({ errorCode, error, variant = "default" }: ErrorMes const { t } = useTranslate(); const message = t(`errors.${errorCode}`); - console.error(error); + if (error) { + console.error(error); + } if (variant === "form") { return ( diff --git a/src/lib/services/base-api.service.ts b/src/lib/services/base-api.service.ts index 66ca606..487dd6a 100644 --- a/src/lib/services/base-api.service.ts +++ b/src/lib/services/base-api.service.ts @@ -83,6 +83,26 @@ export abstract class BaseApiService { return url.toString(); } + protected static async resolveWithFallback(url: string): Promise { + try { + // Essayer de résoudre le DNS d'abord + const urlObj = new URL(url); + const hostname = urlObj.hostname; + + // Si c'est un nom de domaine, essayer de le résoudre + if (!/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) { + // Vérifier si le domaine peut être résolu + const { lookup } = await import('dns').then(dns => dns.promises); + await lookup(hostname); + } + + return url; + } catch (dnsError) { + console.warn(`DNS resolution failed for ${url}, using original URL:`, dnsError); + return url; + } + } + protected static async fetchFromApi( urlBuilder: KomgaUrlBuilder, headersOptions = {}, @@ -90,7 +110,7 @@ export abstract class BaseApiService { ): Promise { const config: AuthConfig = await this.getKomgaConfig(); const { path, params } = urlBuilder; - const url = this.buildUrl(config, path, params); + const url = await this.resolveWithFallback(this.buildUrl(config, path, params)); const headers: Headers = this.getAuthHeaders(config); if (headersOptions) { @@ -109,16 +129,38 @@ export abstract class BaseApiService { try { // Enqueue la requête pour limiter la concurrence const response = await RequestQueueService.enqueue(async () => { - return await fetch(url, { - headers, - ...options, - signal: controller.signal, - // Configure undici connection timeouts - // @ts-ignore - undici-specific options not in standard fetch types - connectTimeout: timeoutMs, - bodyTimeout: timeoutMs, - headersTimeout: timeoutMs, - }); + try { + return await fetch(url, { + headers, + ...options, + signal: controller.signal, + // Configure undici connection timeouts + // @ts-ignore - undici-specific options not in standard fetch types + connectTimeout: timeoutMs, + bodyTimeout: timeoutMs, + headersTimeout: timeoutMs, + }); + } catch (fetchError: any) { + // Gestion spécifique des erreurs DNS + if (fetchError?.cause?.code === 'EAI_AGAIN' || fetchError?.code === 'EAI_AGAIN') { + console.error(`DNS resolution failed for ${url}. Retrying with different DNS settings...`); + + // Retry avec des paramètres DNS différents + return await fetch(url, { + headers, + ...options, + signal: controller.signal, + // @ts-ignore - undici-specific options + connectTimeout: timeoutMs, + bodyTimeout: timeoutMs, + headersTimeout: timeoutMs, + // Force IPv4 si IPv6 pose problème + // @ts-ignore + family: 4, + }); + } + throw fetchError; + } }); clearTimeout(timeoutId); const endTime = performance.now();