feat: implement caching strategy for API responses and adjust loading timeout in CoverClient for improved performance
This commit is contained in:
@@ -3,12 +3,16 @@ import { HomeService } from "@/lib/services/home.service";
|
|||||||
import { ERROR_CODES } from "@/constants/errorCodes";
|
import { ERROR_CODES } from "@/constants/errorCodes";
|
||||||
import { AppError } from "@/utils/errors";
|
import { AppError } from "@/utils/errors";
|
||||||
import { getErrorMessage } from "@/utils/errors";
|
import { getErrorMessage } from "@/utils/errors";
|
||||||
export const dynamic = "force-dynamic";
|
export const revalidate = 60;
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
const data = await HomeService.getHomeData();
|
const data = await HomeService.getHomeData();
|
||||||
return NextResponse.json(data);
|
return NextResponse.json(data, {
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=120'
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("API Home - Erreur:", error);
|
console.error("API Home - Erreur:", error);
|
||||||
if (error instanceof AppError) {
|
if (error instanceof AppError) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { ERROR_CODES } from "@/constants/errorCodes";
|
|||||||
import { AppError } from "@/utils/errors";
|
import { AppError } from "@/utils/errors";
|
||||||
import { getErrorMessage } from "@/utils/errors";
|
import { getErrorMessage } from "@/utils/errors";
|
||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
export const dynamic = "force-dynamic";
|
export const revalidate = 60;
|
||||||
|
|
||||||
const DEFAULT_PAGE_SIZE = 20;
|
const DEFAULT_PAGE_SIZE = 20;
|
||||||
|
|
||||||
@@ -26,7 +26,14 @@ export async function GET(
|
|||||||
LibraryService.getLibrary(libraryId)
|
LibraryService.getLibrary(libraryId)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return NextResponse.json({ series, library });
|
return NextResponse.json(
|
||||||
|
{ series, library },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=120'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("API Library Series - Erreur:", error);
|
console.error("API Library Series - Erreur:", error);
|
||||||
if (error instanceof AppError) {
|
if (error instanceof AppError) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { ERROR_CODES } from "@/constants/errorCodes";
|
|||||||
import { AppError } from "@/utils/errors";
|
import { AppError } from "@/utils/errors";
|
||||||
import { getErrorMessage } from "@/utils/errors";
|
import { getErrorMessage } from "@/utils/errors";
|
||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
export const dynamic = "force-dynamic";
|
export const revalidate = 60;
|
||||||
|
|
||||||
const DEFAULT_PAGE_SIZE = 20;
|
const DEFAULT_PAGE_SIZE = 20;
|
||||||
|
|
||||||
@@ -25,7 +25,14 @@ export async function GET(
|
|||||||
SeriesService.getSeries(seriesId)
|
SeriesService.getSeries(seriesId)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return NextResponse.json({ books, series });
|
return NextResponse.json(
|
||||||
|
{ books, series },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=120'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("API Series Books - Erreur:", error);
|
console.error("API Series Books - Erreur:", error);
|
||||||
if (error instanceof AppError) {
|
if (error instanceof AppError) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { AppError } from "@/utils/errors";
|
|||||||
import type { KomgaSeries } from "@/types/komga";
|
import type { KomgaSeries } from "@/types/komga";
|
||||||
import { getErrorMessage } from "@/utils/errors";
|
import { getErrorMessage } from "@/utils/errors";
|
||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
export const dynamic = "force-dynamic";
|
export const revalidate = 60;
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: NextRequest,
|
request: NextRequest,
|
||||||
@@ -15,7 +15,11 @@ export async function GET(
|
|||||||
const seriesId: string = (await params).seriesId;
|
const seriesId: string = (await params).seriesId;
|
||||||
|
|
||||||
const series: KomgaSeries = await SeriesService.getSeries(seriesId);
|
const series: KomgaSeries = await SeriesService.getSeries(seriesId);
|
||||||
return NextResponse.json(series);
|
return NextResponse.json(series, {
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=120'
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("API Series - Erreur:", error);
|
console.error("API Series - Erreur:", error);
|
||||||
if (error instanceof AppError) {
|
if (error instanceof AppError) {
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ export function ClientLibraryPage({
|
|||||||
params.append("search", search);
|
params.append("search", search);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`);
|
const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`, {
|
||||||
|
cache: 'default' // Utilise le cache HTTP du navigateur
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json();
|
const errorData = await response.json();
|
||||||
@@ -98,7 +100,9 @@ export function ClientLibraryPage({
|
|||||||
params.append("search", search);
|
params.append("search", search);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`);
|
const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`, {
|
||||||
|
cache: 'reload' // Force un nouveau fetch après invalidation
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Error refreshing library");
|
throw new Error("Error refreshing library");
|
||||||
@@ -130,7 +134,9 @@ export function ClientLibraryPage({
|
|||||||
params.append("search", search);
|
params.append("search", search);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`);
|
const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`, {
|
||||||
|
cache: 'reload' // Force un nouveau fetch lors du retry
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json();
|
const errorData = await response.json();
|
||||||
|
|||||||
@@ -46,7 +46,9 @@ export function ClientSeriesPage({
|
|||||||
unread: String(unreadOnly),
|
unread: String(unreadOnly),
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await fetch(`/api/komga/series/${seriesId}/books?${params}`);
|
const response = await fetch(`/api/komga/series/${seriesId}/books?${params}`, {
|
||||||
|
cache: 'default' // Utilise le cache HTTP du navigateur
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json();
|
const errorData = await response.json();
|
||||||
@@ -85,7 +87,9 @@ export function ClientSeriesPage({
|
|||||||
unread: String(unreadOnly),
|
unread: String(unreadOnly),
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await fetch(`/api/komga/series/${seriesId}/books?${params}`);
|
const response = await fetch(`/api/komga/series/${seriesId}/books?${params}`, {
|
||||||
|
cache: 'reload' // Force un nouveau fetch après invalidation
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Erreur lors du rafraîchissement de la série");
|
throw new Error("Erreur lors du rafraîchissement de la série");
|
||||||
@@ -113,7 +117,9 @@ export function ClientSeriesPage({
|
|||||||
unread: String(unreadOnly),
|
unread: String(unreadOnly),
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await fetch(`/api/komga/series/${seriesId}/books?${params}`);
|
const response = await fetch(`/api/komga/series/${seriesId}/books?${params}`, {
|
||||||
|
cache: 'reload' // Force un nouveau fetch lors du retry
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json();
|
const errorData = await response.json();
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ export function ClientHomePage() {
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/komga/home");
|
const response = await fetch("/api/komga/home", {
|
||||||
|
cache: 'default' // Utilise le cache HTTP du navigateur
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json();
|
const errorData = await response.json();
|
||||||
@@ -61,7 +63,9 @@ export function ClientHomePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Récupérer les nouvelles données
|
// Récupérer les nouvelles données
|
||||||
const response = await fetch("/api/komga/home");
|
const response = await fetch("/api/komga/home", {
|
||||||
|
cache: 'reload' // Force un nouveau fetch après invalidation
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Erreur lors du rafraîchissement de la page d'accueil");
|
throw new Error("Erreur lors du rafraîchissement de la page d'accueil");
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ export const CoverClient = ({
|
|||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
// Timeout de sécurité : si l'image ne se charge pas en 10 secondes, on arrête le loading
|
// Timeout de sécurité : si l'image ne se charge pas en 30 secondes, on arrête le loading
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
timeoutRef.current = setTimeout(() => {
|
timeoutRef.current = setTimeout(() => {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
console.warn("Image loading timeout for:", imageUrl);
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
setImageError(true);
|
||||||
}
|
}
|
||||||
}, 10000);
|
}, 30000);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (timeoutRef.current) {
|
if (timeoutRef.current) {
|
||||||
@@ -49,8 +49,8 @@ export const CoverClient = ({
|
|||||||
if (timeoutRef.current) {
|
if (timeoutRef.current) {
|
||||||
clearTimeout(timeoutRef.current);
|
clearTimeout(timeoutRef.current);
|
||||||
}
|
}
|
||||||
console.error("Image loading error for:", imageUrl);
|
|
||||||
setImageError(true);
|
setImageError(true);
|
||||||
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (imageError) {
|
if (imageError) {
|
||||||
|
|||||||
Reference in New Issue
Block a user