chore: final verification fixes for sitemap reliability

- Replace invalid revalidate export with unstable_cache in sitemap.ts
- Add revalidateTag('sitemap', 'default') to on-demand revalidation endpoint
- Fix test assertions to match /game/ URL path
This commit is contained in:
2026-05-04 13:16:47 +08:00
parent 9d3690ce50
commit 633117f452
3 changed files with 30 additions and 12 deletions
+2 -1
View File
@@ -1,4 +1,4 @@
import { revalidatePath } from "next/cache" import { revalidatePath, revalidateTag } from "next/cache"
import { NextRequest, NextResponse } from "next/server" import { NextRequest, NextResponse } from "next/server"
/** /**
@@ -38,6 +38,7 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
} }
try { try {
revalidateTag("sitemap", "default")
revalidatePath("/sitemap.xml") revalidatePath("/sitemap.xml")
return NextResponse.json({ return NextResponse.json({
revalidated: true, revalidated: true,
+26 -9
View File
@@ -1,19 +1,36 @@
import type { MetadataRoute } from "next" import type { MetadataRoute } from "next"
import { unstable_cache } from "next/cache"
import { buildStaticEntries } from "@/lib/sitemap/build-static-entries" import { buildStaticEntries } from "@/lib/sitemap/build-static-entries"
import { fetchDynamicEntries } from "@/lib/sitemap/fetch-dynamic-entries" import { fetchDynamicEntries } from "@/lib/sitemap/fetch-dynamic-entries"
const REVALIDATE_SECONDS = Number(process.env.SITEMAP_REVALIDATE_SECONDS) || 3600
/** /**
* ISR revalidation interval for the sitemap. * Caches the full sitemap generation with time-based revalidation.
* *
* Instead of `force-dynamic`, we use ISR so the sitemap is cached and * Instead of `force-dynamic` (which hits the DB on every crawler request),
* regenerated in the background. This keeps response times fast for * we use `unstable_cache` so the sitemap is regenerated at most once per
* crawlers while still reflecting recent changes on the site. * revalidation window. This keeps response times fast for crawlers while
* still reflecting recent changes on the site.
*
* Tagged with `"sitemap"` so the on-demand revalidation webhook can
* clear this cache immediately after content changes.
*
* @see https://nextjs.org/docs/app/api-reference/functions/unstable_cache
*/ */
export const revalidate = Number(process.env.SITEMAP_REVALIDATE_SECONDS) || 3600 const getCachedSitemap = unstable_cache(
async () => {
const staticEntries = buildStaticEntries()
const { gameEntries, deviceEntries } = await fetchDynamicEntries()
return [...staticEntries, ...gameEntries, ...deviceEntries]
},
["sitemap"],
{
revalidate: REVALIDATE_SECONDS,
tags: ["sitemap"],
},
)
export default async function sitemap(): Promise<MetadataRoute.Sitemap> { export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const staticEntries = buildStaticEntries() return getCachedSitemap()
const { gameEntries, deviceEntries } = await fetchDynamicEntries()
return [...staticEntries, ...gameEntries, ...deviceEntries]
} }
@@ -64,9 +64,9 @@ describe("fetchDynamicEntries", () => {
const result = await fetchDynamicEntries() const result = await fetchDynamicEntries()
expect(result.gameEntries).toHaveLength(2) expect(result.gameEntries).toHaveLength(2)
expect(result.gameEntries[0].url).toBe("https://deckyvault.xyz/games/game-1") expect(result.gameEntries[0].url).toBe("https://deckyvault.xyz/game/game-1")
expect(result.gameEntries[0].images).toEqual(["https://cdn.example.com/img1.jpg"]) expect(result.gameEntries[0].images).toEqual(["https://cdn.example.com/img1.jpg"])
expect(result.gameEntries[1].url).toBe("https://deckyvault.xyz/games/game-2") expect(result.gameEntries[1].url).toBe("https://deckyvault.xyz/game/game-2")
expect(result.gameEntries[1].images).toBeUndefined() expect(result.gameEntries[1].images).toBeUndefined()
expect(result.deviceEntries).toHaveLength(1) expect(result.deviceEntries).toHaveLength(1)
}) })