All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 49s
Add ETag header to API thumbnail responses for 304 Not Modified support. Forward If-None-Match/ETag through the Next.js proxy route handler and add next.revalidate for 24h server-side fetch caching to reduce SSR-to-API round trips on the libraries page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { config } from "@/lib/api";
|
|
|
|
export async function GET(
|
|
request: NextRequest,
|
|
{ params }: { params: Promise<{ bookId: string }> }
|
|
) {
|
|
const { bookId } = await params;
|
|
|
|
try {
|
|
const { baseUrl, token } = config();
|
|
const ifNoneMatch = request.headers.get("if-none-match");
|
|
|
|
const fetchHeaders: Record<string, string> = {
|
|
Authorization: `Bearer ${token}`,
|
|
};
|
|
if (ifNoneMatch) {
|
|
fetchHeaders["If-None-Match"] = ifNoneMatch;
|
|
}
|
|
|
|
const response = await fetch(`${baseUrl}/books/${bookId}/thumbnail`, {
|
|
headers: fetchHeaders,
|
|
next: { revalidate: 86400 },
|
|
});
|
|
|
|
// Forward 304 Not Modified as-is
|
|
if (response.status === 304) {
|
|
return new NextResponse(null, { status: 304 });
|
|
}
|
|
|
|
if (!response.ok) {
|
|
return new NextResponse(`Failed to fetch thumbnail: ${response.status}`, {
|
|
status: response.status
|
|
});
|
|
}
|
|
|
|
const contentType = response.headers.get("content-type") || "image/webp";
|
|
const etag = response.headers.get("etag");
|
|
|
|
const headers: Record<string, string> = {
|
|
"Content-Type": contentType,
|
|
"Cache-Control": "public, max-age=31536000, immutable",
|
|
};
|
|
if (etag) {
|
|
headers["ETag"] = etag;
|
|
}
|
|
|
|
return new NextResponse(response.body, { headers });
|
|
} catch (error) {
|
|
console.error("Error fetching thumbnail:", error);
|
|
return new NextResponse("Failed to fetch thumbnail", { status: 500 });
|
|
}
|
|
}
|