42 lines
1.1 KiB
TypeScript
42 lines
1.1 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
|
|
function normalizeTag(tag: string) {
|
|
return tag.trim().replace(/^W\//i, "");
|
|
}
|
|
|
|
function ifNoneMatchHit(ifNoneMatch: string | null, etag: string) {
|
|
if (!ifNoneMatch) return false;
|
|
const target = normalizeTag(etag);
|
|
return ifNoneMatch
|
|
.split(",")
|
|
.map((part) => normalizeTag(part))
|
|
.some((part) => part === "*" || part === target);
|
|
}
|
|
|
|
function buildEtag(body: string) {
|
|
const hash = createHash("sha1").update(body).digest("hex");
|
|
return `"${hash}"`;
|
|
}
|
|
|
|
export function buildCachedJsonResponse(
|
|
req: NextRequest,
|
|
payload: unknown,
|
|
cacheControl: string,
|
|
) {
|
|
const body = JSON.stringify(payload);
|
|
const etag = buildEtag(body);
|
|
const headers = new Headers({
|
|
"Cache-Control": cacheControl,
|
|
"Cloudflare-CDN-Cache-Control": cacheControl,
|
|
ETag: etag,
|
|
"Content-Type": "application/json; charset=utf-8",
|
|
});
|
|
|
|
if (ifNoneMatchHit(req.headers.get("if-none-match"), etag)) {
|
|
return new NextResponse(null, { status: 304, headers });
|
|
}
|
|
|
|
return new NextResponse(body, { status: 200, headers });
|
|
}
|