feat/SW-550 sitemap route

* feat(SW-550): Added rewrites to handle sitemap paths

* feat(SW-550): Added sitemap-index generation

* feat(SW-550): Added sitemap xml file generation

* feat(SW-550): Added feature flag 'HIDE_FOR_NEXT_RELEASE' to sitemap routes


Approved-by: Linus Flood
This commit is contained in:
Erik Tiekstra
2025-03-17 07:17:08 +00:00
parent f0b245bdfe
commit ca93046aad
6 changed files with 96 additions and 6 deletions

3
.gitignore vendored
View File

@@ -11,3 +11,6 @@ node_modules
# Turbo
.turbo
# Local Netlify folder
.netlify

View File

@@ -1,6 +1,8 @@
import { Lang } from "@/constants/languages"
import { env } from "@/env/server"
import { removeTrailingSlash } from "@/utils/url"
import {
mergeEntriesCounter,
mergeEntriesSuccessCounter,
@@ -114,13 +116,11 @@ function mapEntriesToSitemapEntry(entries: SyncItem[]) {
if (mainEntry) {
const { locale, url } = mainEntry.data
const sitemapEntry: SitemapEntry = {
url: `${env.PUBLIC_URL}/${locale}${url}`,
url: removeTrailingSlash(`${env.PUBLIC_URL}/${locale}${url}`),
lastModified,
changeFrequency,
priority,
}
if (alternates) {
sitemapEntry.alternates = alternates
alternates,
}
return sitemapEntry
}

View File

@@ -0,0 +1,50 @@
import { notFound } from "next/navigation"
import { env } from "@/env/server"
import { getSitemapDataById } from "@/utils/sitemap"
import type { NextRequest } from "next/server"
export const dynamic = "force-dynamic"
export async function GET(
_request: NextRequest,
context: { params: { sitemapId: string } }
) {
if (env.HIDE_FOR_NEXT_RELEASE) {
notFound()
}
const sitemapId = context.params.sitemapId
if (!sitemapId) {
notFound()
}
const sitemapData = await getSitemapDataById(Number(sitemapId))
if (!sitemapData.length) {
notFound()
}
const entries = sitemapData.map((entry) => {
const alternates = Object.entries(entry.alternates).map(
([lang, url]) =>
`<xhtml:link rel="alternate" hreflang="${lang}" href="${url}" />`
)
return `
<url>
<loc>${entry.url}</loc>
<lastmod>${entry.lastModified}</lastmod>
<changefreq>${entry.changeFrequency}</changefreq>
<priority>${entry.priority}</priority>
${alternates.join("")}
</url>`
})
const sitemapXML = `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">${entries.join("")}\n</urlset>`
return new Response(sitemapXML, {
headers: { "Content-Type": "text/xml" },
})
}

View File

@@ -0,0 +1,29 @@
import { notFound } from "next/navigation"
import { env } from "@/env/server"
import { getLastUpdated, getSitemapIds } from "@/utils/sitemap"
export const dynamic = "force-dynamic"
export async function GET() {
if (env.HIDE_FOR_NEXT_RELEASE) {
notFound()
}
const lastUpdated = await getLastUpdated()
const sitemaps = await getSitemapIds()
const urls = sitemaps.map(
(id) => `<sitemap>
<loc>${env.PUBLIC_URL}/sitemap-${id}.xml</loc>
<lastmod>${lastUpdated}</lastmod>
</sitemap>`
)
const sitemapIndexXML = `<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${urls.join("")}\n</sitemapindex>`
return new Response(sitemapIndexXML, {
headers: { "Content-Type": "text/xml" },
})
}

View File

@@ -308,6 +308,14 @@ const nextConfig = {
source: findMyBooking.sv,
destination: "/sv/hotelreservation/get-booking",
},
{
source: `/sitemap-:id(\\d{1,}).xml`,
destination: `/sitemap/:id`,
},
{
source: `/sitemap-index.xml`,
destination: `/sitemap`,
},
],
}
},

View File

@@ -16,7 +16,7 @@ export interface SitemapEntry {
lastModified: string
changeFrequency: ChangeFrequency
priority: number
alternates?: Partial<Record<Lang, string>>
alternates: Partial<Record<Lang, string>>
}
export type SitemapData = SitemapEntry[]