diff --git a/backend/src/app/sitemap-index.xml/route.ts b/backend/src/app/sitemap-index.xml/route.ts
new file mode 100644
index 0000000..7d6c719
--- /dev/null
+++ b/backend/src/app/sitemap-index.xml/route.ts
@@ -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) => `${baseUrl}/sitemap/${i}.xml`).join("");
+
+ const xml = `
+
+${sitemapEntries}
+`;
+
+ return new NextResponse(xml, {
+ headers: { "Content-Type": "application/xml" },
+ });
+}
diff --git a/backend/src/app/sitemap.ts b/backend/src/app/sitemap.ts
index 17c114c..1fab28f 100644
--- a/backend/src/app/sitemap.ts
+++ b/backend/src/app/sitemap.ts
@@ -1,14 +1,64 @@
import { prisma } from "@/lib/prisma";
+import { Prisma } from "@prisma/client";
import type { MetadataRoute } from "next";
type SitemapRoute = MetadataRoute.Sitemap[0];
export const revalidate = 43200; // update every 12 hours
+export const CHUNK_SIZE = 45000; // stay under 50,000 limit
-export default async function sitemap(): Promise {
+export async function generateSitemaps() {
+ const total = await prisma.mii.count({
+ where: {
+ in_queue: false,
+ quarantined: false,
+ },
+ });
+ const miiChunks = Math.ceil(total / CHUNK_SIZE);
+
+ // id 0 = static routes, ids 1..miiChunks = mii chunks
+ return Array.from({ length: miiChunks + 1 }, (_, id) => ({ id }));
+}
+
+export default async function sitemap(props: { id: Promise }): Promise {
const baseUrl = process.env.NEXT_PUBLIC_FRONTEND_URL!;
const apiUrl = process.env.NEXT_PUBLIC_BASE_URL!;
+ // id 0: static routes only
+ if ((await props.id) === "0") {
+ const lastModified = new Date();
+
+ return [
+ {
+ url: baseUrl,
+ lastModified,
+ changeFrequency: "always",
+ priority: 1,
+ },
+ {
+ url: `${baseUrl}/login`,
+ lastModified,
+ changeFrequency: "monthly",
+ priority: 0.6,
+ },
+ {
+ url: `${baseUrl}/privacy`,
+ lastModified,
+ changeFrequency: "yearly",
+ priority: 0.4,
+ },
+ {
+ url: `${baseUrl}/terms-of-service`,
+ lastModified,
+ changeFrequency: "yearly",
+ priority: 0.4,
+ },
+ ];
+ }
+
+ // id 1+: paginated mii chunks
+ const chunkIndex = Number(await props.id) - 1;
+
const miis = await prisma.mii.findMany({
select: {
id: true,
@@ -18,64 +68,19 @@ export default async function sitemap(): Promise {
in_queue: false,
quarantined: false,
},
+ orderBy: { id: "asc" },
+ skip: chunkIndex * CHUNK_SIZE,
+ take: CHUNK_SIZE,
});
- // const users = await prisma.user.findMany({
- // select: {
- // id: true,
- // updatedAt: true,
- // },
- // });
-
- const dynamicRoutes: MetadataRoute.Sitemap = [
- ...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,
- ),
- // ...users.map(
- // (user) =>
- // ({
- // url: `${baseUrl}/profile/${user.id}`,
- // lastModified: user.updatedAt,
- // changeFrequency: "weekly",
- // priority: 0.2,
- // }) as SitemapRoute,
- // ),
- ];
-
- const lastModified = new Date();
-
- return [
- {
- url: baseUrl,
- lastModified,
- changeFrequency: "always",
- priority: 1,
- },
- {
- url: `${baseUrl}/login`,
- lastModified,
- changeFrequency: "monthly",
- priority: 0.6,
- },
- {
- url: `${baseUrl}/privacy`,
- lastModified,
- changeFrequency: "yearly",
- priority: 0.4,
- },
- {
- url: `${baseUrl}/terms-of-service`,
- lastModified,
- changeFrequency: "yearly",
- priority: 0.4,
- },
- ...dynamicRoutes,
- ];
+ 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,
+ );
}
diff --git a/frontend/public/robots.txt b/frontend/public/robots.txt
index 3e0910d..0b0cca7 100644
--- a/frontend/public/robots.txt
+++ b/frontend/public/robots.txt
@@ -10,4 +10,4 @@ Disallow: /report/mii/*
Disallow: /report/user/*
Disallow: /admin
-Sitemap: https://api.tomodachishare.com/sitemap.xml
+Sitemap: https://api.tomodachishare.com/sitemap-index.xml