feat: add on-demand sitemap revalidation webhook

This commit is contained in:
2026-05-04 13:12:37 +08:00
parent 0a138c3099
commit 9d3690ce50
2 changed files with 65 additions and 1 deletions
+11 -1
View File
@@ -86,4 +86,14 @@ STEAMGRIDDB_API_KEY="your_steamgriddb_api_key_here"
# CONTACT FORM
# -----------------------------------------------------------------------------
# Discord webhook URL for contact/report submissions
DISCORD_WEBHOOK_URL=""
DISCORD_WEBHOOK_URL=""
# -----------------------------------------------------------------------------
# SITEMAP
# -----------------------------------------------------------------------------
# Secret token for on-demand sitemap revalidation (generate with: openssl rand -base64 32)
# Leave empty to disable the revalidation endpoint
REVALIDATE_SECRET=""
# ISR revalidation interval in seconds for sitemap.xml (default: 3600 = 1 hour)
SITEMAP_REVALIDATE_SECONDS=""
+54
View File
@@ -0,0 +1,54 @@
import { revalidatePath } from "next/cache"
import { NextRequest, NextResponse } from "next/server"
/**
* On-demand sitemap revalidation webhook.
*
* What this endpoint does:
* Revalidates the `/sitemap.xml` path so Next.js regenerates the sitemap
* at the edge instead of waiting for the next ISR interval.
*
* When to call it:
* - After adding, updating, or removing games
* - After adding, updating, or removing hardware
* - After any bulk import or migration that affects public-facing URLs
*
* How to call it:
* ```bash
* curl -X POST https://<your-domain>/api/revalidate-sitemap \
* -H "Authorization: Bearer $REVALIDATE_SECRET"
* ```
*
* @see https://nextjs.org/docs/app/building-your-application/caching#on-demand-revalidation
*/
export async function POST(request: NextRequest): Promise<NextResponse> {
const authHeader = request.headers.get("authorization")
const secret = process.env.REVALIDATE_SECRET
if (!secret) {
return NextResponse.json(
{ error: "Revalidation not configured. Set REVALIDATE_SECRET env var." },
{ status: 503 },
)
}
const token = authHeader?.replace("Bearer ", "")
if (token !== secret) {
return NextResponse.json({ error: "Invalid secret" }, { status: 401 })
}
try {
revalidatePath("/sitemap.xml")
return NextResponse.json({
revalidated: true,
path: "/sitemap.xml",
now: Date.now(),
})
} catch (error) {
console.error("[Revalidate Sitemap] Failed:", error)
return NextResponse.json(
{ error: "Revalidation failed" },
{ status: 500 },
)
}
}