diff --git a/package.json b/package.json index 913ebf7..513d74e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webapp", - "version": "2.31.3", + "version": "2.31.4", "private": true, "scripts": { "dev": "next dev", diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 0000000..57f2f07 --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,52 @@ +import type { MetadataRoute } from 'next'; + +import { fetchSitemapJobs } from '@/features/jobs/server/data'; +import { fetchPillarSitemapSlugs } from '@/features/pillar/server/data'; +import { clientEnv } from '@/lib/env/client'; + +export const revalidate = 3600; + +const FRONTEND_URL = clientEnv.FRONTEND_URL; +const PILLAR_CHUNK_SIZE = 3000; + +export async function generateSitemaps() { + const slugs = await fetchPillarSitemapSlugs(); + const numChunks = Math.ceil(slugs.length / PILLAR_CHUNK_SIZE); + + return [ + { id: 'static' }, + ...Array.from({ length: numChunks }, (_, i) => ({ id: `pillar-${i}` })), + { id: 'jobs' }, + ]; +} + +export default async function sitemap({ + id, +}: { + id: Promise; +}): Promise { + const resolvedId = await id; + + if (resolvedId === 'static') { + return [{ url: FRONTEND_URL }]; + } + + if (resolvedId === 'jobs') { + const jobs = await fetchSitemapJobs(); + return jobs.map(({ href, lastModified }) => ({ + url: `${FRONTEND_URL}${href}`, + lastModified, + })); + } + + // pillar-0, pillar-1, pillar-2, ... + const chunkIndex = Number(resolvedId.split('-')[1]); + const slugs = await fetchPillarSitemapSlugs(); + const start = chunkIndex * PILLAR_CHUNK_SIZE; + const chunk = slugs.slice(start, start + PILLAR_CHUNK_SIZE); + + return chunk.map(({ slug, lastModified }) => ({ + url: `${FRONTEND_URL}/${slug}`, + lastModified: new Date(lastModified), + })); +} diff --git a/src/app/sitemap.xml/route.ts b/src/app/sitemap.xml/route.ts index 01ae6b0..4897a78 100644 --- a/src/app/sitemap.xml/route.ts +++ b/src/app/sitemap.xml/route.ts @@ -1,16 +1,28 @@ +import { fetchPillarSitemapSlugs } from '@/features/pillar/server/data'; import { clientEnv } from '@/lib/env/client'; -export const dynamic = 'force-static'; +export const revalidate = 3600; const FRONTEND_URL = clientEnv.FRONTEND_URL; +const PILLAR_CHUNK_SIZE = 3000; + +export async function GET() { + const slugs = await fetchPillarSitemapSlugs(); + const numChunks = Math.ceil(slugs.length / PILLAR_CHUNK_SIZE); + + const sitemaps = [ + `${FRONTEND_URL}/sitemap/static.xml`, + ...Array.from( + { length: numChunks }, + (_, i) => `${FRONTEND_URL}/sitemap/pillar-${i}.xml`, + ), + `${FRONTEND_URL}/sitemap/jobs.xml`, + ]; -export function GET() { const xml = [ '', '', - ` ${FRONTEND_URL}/sitemap1.xml`, - ` ${FRONTEND_URL}/sitemap2.xml`, - ` ${FRONTEND_URL}/sitemap3.xml`, + ...sitemaps.map((loc) => ` ${loc}`), '', ].join('\n'); diff --git a/src/app/sitemap1.xml/route.ts b/src/app/sitemap1.xml/route.ts deleted file mode 100644 index 813ea54..0000000 --- a/src/app/sitemap1.xml/route.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { clientEnv } from '@/lib/env/client'; - -export const dynamic = 'force-static'; - -const FRONTEND_URL = clientEnv.FRONTEND_URL; - -export function GET() { - const xml = [ - '', - '', - ` ${FRONTEND_URL}hourly`, - '', - ].join('\n'); - - return new Response(xml, { - headers: { 'Content-Type': 'application/xml' }, - }); -} diff --git a/src/app/sitemap2.xml/route.ts b/src/app/sitemap2.xml/route.ts deleted file mode 100644 index 0f6a769..0000000 --- a/src/app/sitemap2.xml/route.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as Sentry from '@sentry/nextjs'; - -import { fetchPillarSitemapSlugs } from '@/features/pillar/server/data'; -import { clientEnv } from '@/lib/env/client'; - -export const revalidate = 3600; - -const FRONTEND_URL = clientEnv.FRONTEND_URL; - -export async function GET() { - let slugs: Awaited> = []; - - try { - slugs = await fetchPillarSitemapSlugs(); - } catch (error) { - if (process.env.CI) throw error; - Sentry.captureException(error); - } - - const urls = slugs.map( - ({ slug, lastModified }) => - ` ${FRONTEND_URL}/${slug}${new Date(lastModified).toISOString()}hourly`, - ); - - const xml = [ - '', - '', - ...urls, - '', - ].join('\n'); - - return new Response(xml, { - headers: { 'Content-Type': 'application/xml' }, - }); -} diff --git a/src/app/sitemap3.xml/route.ts b/src/app/sitemap3.xml/route.ts deleted file mode 100644 index 02242af..0000000 --- a/src/app/sitemap3.xml/route.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as Sentry from '@sentry/nextjs'; - -import { fetchSitemapJobs } from '@/features/jobs/server/data'; -import { clientEnv } from '@/lib/env/client'; - -export const revalidate = 3600; - -const FRONTEND_URL = clientEnv.FRONTEND_URL; - -export async function GET() { - let jobs: Awaited> = []; - - try { - jobs = await fetchSitemapJobs(); - } catch (error) { - if (process.env.CI) throw error; - Sentry.captureException(error); - } - - const urls = jobs.map( - ({ href, lastModified }) => - ` ${FRONTEND_URL}${href}${lastModified.toISOString()}hourly`, - ); - - const xml = [ - '', - '', - ...urls, - '', - ].join('\n'); - - return new Response(xml, { - headers: { 'Content-Type': 'application/xml' }, - }); -}