Add sitemap generation (#32)
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
foxtacles 2026-04-11 12:24:44 -07:00 committed by GitHub
parent 429a36d3d1
commit daf4f6bc52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 68 additions and 0 deletions

View File

@ -26,6 +26,15 @@ app.all("/api/auth/*", async (c) => {
return auth.handler(c.req.raw);
});
// Public endpoint: all memory event IDs for sitemap generation
app.get("/api/sitemap", async (c) => {
const results = await c.env.DB.prepare(
"SELECT event_id, MAX(completed_at) AS completed_at FROM memory_completions GROUP BY event_id ORDER BY completed_at DESC"
).all<{ event_id: string; completed_at: number }>();
return c.json({ entries: results.results });
});
// Public endpoint: look up a memory completion by eventId (no auth needed)
app.get("/api/memory/:eventId", async (c) => {
const eventId = c.req.param("eventId");

View File

@ -162,11 +162,62 @@ async function handleScene(encoded: string, request: Request, env: Env): Promise
}
}
async function handleSitemap(request: Request, env: Env): Promise<Response> {
const origin = new URL(request.url).origin;
try {
const apiRes = await fetch(`${env.API_URL}/api/sitemap`);
if (!apiRes.ok) {
return new Response('Failed to fetch sitemap data', { status: 502 });
}
const data = await apiRes.json() as {
entries: Array<{ event_id: string; completed_at: number }>;
};
const urls: string[] = [
` <url>`,
` <loc>${escapeHtml(origin)}/</loc>`,
` </url>`,
];
for (const entry of data.entries) {
const lastmod = new Date(entry.completed_at * 1000).toISOString().split('T')[0];
urls.push(
` <url>`,
` <loc>${escapeHtml(origin)}/memory/${escapeHtml(entry.event_id)}</loc>`,
` <lastmod>${lastmod}</lastmod>`,
` </url>`,
);
}
const xml = [
`<?xml version="1.0" encoding="UTF-8"?>`,
`<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`,
...urls,
`</urlset>`,
].join('\n');
return new Response(xml, {
headers: {
'content-type': 'application/xml; charset=utf-8',
'cache-control': 'public, max-age=3600',
},
});
} catch {
return new Response('Failed to generate sitemap', { status: 500 });
}
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const path = url.pathname;
if (path === '/sitemap.xml') {
return handleSitemap(request, env);
}
const memoryMatch = path.match(/^\/memory\/([A-Za-z0-9_-]+)$/);
if (memoryMatch) {
return handleMemory(memoryMatch[1], request, env);

View File

@ -29,6 +29,10 @@ zone_name = "isle.pizza"
pattern = "isle.pizza/scene/*"
zone_name = "isle.pizza"
[[env.production.routes]]
pattern = "isle.pizza/sitemap.xml"
zone_name = "isle.pizza"
[[env.production.routes]]
pattern = "dev.isle.pizza/memory/*"
zone_name = "isle.pizza"
@ -36,3 +40,7 @@ zone_name = "isle.pizza"
[[env.production.routes]]
pattern = "dev.isle.pizza/scene/*"
zone_name = "isle.pizza"
[[env.production.routes]]
pattern = "dev.isle.pizza/sitemap.xml"
zone_name = "isle.pizza"