feat: split sitemap

This commit is contained in:
trafficlunar 2026-08-30 12:16:21 +01:00
parent 152576e524
commit 253edc0d1a
3 changed files with 89 additions and 60 deletions

View file

@ -0,0 +1,24 @@
import { prisma } from "@/lib/prisma";
import { CHUNK_SIZE } from "../sitemap";
import { NextResponse } from "next/server";
export async function GET() {
const baseUrl = process.env.NEXT_PUBLIC_FRONTEND_URL!;
const total = await prisma.mii.count({
where: { in_queue: false, quarantined: false },
});
const miiChunks = Math.ceil(total / CHUNK_SIZE);
const totalSitemaps = miiChunks + 1; // +1 for the static-routes sitemap
const sitemapEntries = Array.from({ length: totalSitemaps }, (_, i) => `<sitemap><loc>${baseUrl}/sitemap/${i}.xml</loc></sitemap>`).join("");
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${sitemapEntries}
</sitemapindex>`;
return new NextResponse(xml, {
headers: { "Content-Type": "application/xml" },
});
}

View file

@ -1,54 +1,31 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import type { MetadataRoute } from "next"; import type { MetadataRoute } from "next";
type SitemapRoute = MetadataRoute.Sitemap[0]; type SitemapRoute = MetadataRoute.Sitemap[0];
export const revalidate = 43200; // update every 12 hours export const revalidate = 43200; // update every 12 hours
export const CHUNK_SIZE = 45000; // stay under 50,000 limit
export default async function sitemap(): Promise<MetadataRoute.Sitemap> { export async function generateSitemaps() {
const baseUrl = process.env.NEXT_PUBLIC_FRONTEND_URL!; const total = await prisma.mii.count({
const apiUrl = process.env.NEXT_PUBLIC_BASE_URL!;
const miis = await prisma.mii.findMany({
select: {
id: true,
createdAt: true,
},
where: { where: {
in_queue: false, in_queue: false,
quarantined: false, quarantined: false,
}, },
}); });
const miiChunks = Math.ceil(total / CHUNK_SIZE);
// const users = await prisma.user.findMany({ // id 0 = static routes, ids 1..miiChunks = mii chunks
// select: { return Array.from({ length: miiChunks + 1 }, (_, id) => ({ id }));
// id: true, }
// updatedAt: true,
// },
// });
const dynamicRoutes: MetadataRoute.Sitemap = [ export default async function sitemap(props: { id: Promise<string> }): Promise<MetadataRoute.Sitemap> {
...miis.map( const baseUrl = process.env.NEXT_PUBLIC_FRONTEND_URL!;
(mii) => const apiUrl = process.env.NEXT_PUBLIC_BASE_URL!;
({
url: `${baseUrl}/mii/${mii.id}`,
lastModified: mii.createdAt,
changeFrequency: "weekly",
priority: 0.7,
images: [`${apiUrl}/mii/${mii.id}/image?type=metadata`],
}) as SitemapRoute,
),
// ...users.map(
// (user) =>
// ({
// url: `${baseUrl}/profile/${user.id}`,
// lastModified: user.updatedAt,
// changeFrequency: "weekly",
// priority: 0.2,
// }) as SitemapRoute,
// ),
];
// id 0: static routes only
if ((await props.id) === "0") {
const lastModified = new Date(); const lastModified = new Date();
return [ return [
@ -76,6 +53,34 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
changeFrequency: "yearly", changeFrequency: "yearly",
priority: 0.4, priority: 0.4,
}, },
...dynamicRoutes,
]; ];
}
// id 1+: paginated mii chunks
const chunkIndex = Number(await props.id) - 1;
const miis = await prisma.mii.findMany({
select: {
id: true,
createdAt: true,
},
where: {
in_queue: false,
quarantined: false,
},
orderBy: { id: "asc" },
skip: chunkIndex * CHUNK_SIZE,
take: CHUNK_SIZE,
});
return miis.map(
(mii) =>
({
url: `${baseUrl}/mii/${mii.id}`,
lastModified: mii.createdAt,
changeFrequency: "weekly",
priority: 0.7,
images: [`${apiUrl}/mii/${mii.id}/image?type=metadata`],
}) as SitemapRoute,
);
} }

View file

@ -10,4 +10,4 @@ Disallow: /report/mii/*
Disallow: /report/user/* Disallow: /report/user/*
Disallow: /admin Disallow: /admin
Sitemap: https://api.tomodachishare.com/sitemap.xml Sitemap: https://api.tomodachishare.com/sitemap-index.xml