81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import { prisma } from "@/lib/prisma";
|
|
import { locales } from "@/i18n/locales";
|
|
import { getBaseUrl } from "@/lib/seo";
|
|
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
|
|
|
|
export type SitemapEntry = {
|
|
url: string;
|
|
lastModified: string;
|
|
changefreq?: "daily" | "weekly" | "monthly" | "yearly";
|
|
priority?: number;
|
|
};
|
|
|
|
export function generateSitemapXml(entries: SitemapEntry[]): string {
|
|
const xmlHeader = '<?xml version="1.0" encoding="UTF-8"?>';
|
|
const urlsetOpen = '<urlset xmlns="https://www.sitemaps.org/schemas/sitemap/0.9">';
|
|
const urlsetClose = "</urlset>";
|
|
|
|
const urlEntries = entries
|
|
.map((e) => {
|
|
const changefreq = e.changefreq ?? "monthly";
|
|
const priority = typeof e.priority === "number" ? e.priority : 0.8;
|
|
return `
|
|
<url>
|
|
<loc>${e.url}</loc>
|
|
<lastmod>${e.lastModified}</lastmod>
|
|
<changefreq>${changefreq}</changefreq>
|
|
<priority>${priority.toFixed(1)}</priority>
|
|
</url>`;
|
|
})
|
|
.join("");
|
|
|
|
return `${xmlHeader}${urlsetOpen}${urlEntries}${urlsetClose}`;
|
|
}
|
|
|
|
export async function getSitemapEntries(): Promise<SitemapEntry[]> {
|
|
const baseUrl = getBaseUrl();
|
|
const nowIso = new Date().toISOString();
|
|
|
|
const staticPaths = ["", "/projects", "/legal-notice", "/privacy-policy"];
|
|
const staticEntries: SitemapEntry[] = locales.flatMap((locale) =>
|
|
staticPaths.map((p) => {
|
|
const path = p === "" ? `/${locale}` : `/${locale}${p}`;
|
|
return {
|
|
url: `${baseUrl}${path}`,
|
|
lastModified: nowIso,
|
|
changefreq: p === "" ? "weekly" : p === "/projects" ? "weekly" : "yearly",
|
|
priority: p === "" ? 1.0 : p === "/projects" ? 0.8 : 0.5,
|
|
};
|
|
}),
|
|
);
|
|
|
|
// Projects: for each project slug we publish per locale (same slug)
|
|
let projects: Array<{ slug: string; updatedAt: Date | null }> = [];
|
|
try {
|
|
projects = await prisma.project.findMany({
|
|
where: { published: true },
|
|
select: { slug: true, updatedAt: true },
|
|
orderBy: { updatedAt: "desc" },
|
|
});
|
|
} catch (error) {
|
|
// If DB isn't ready/migrated yet, still serve a valid sitemap for static pages.
|
|
if (error instanceof PrismaClientKnownRequestError && (error.code === "P2021" || error.code === "P2022")) {
|
|
return staticEntries;
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
const projectEntries: SitemapEntry[] = projects.flatMap((p) => {
|
|
const lastModified = (p.updatedAt ?? new Date()).toISOString();
|
|
return locales.map((locale) => ({
|
|
url: `${baseUrl}/${locale}/projects/${p.slug}`,
|
|
lastModified,
|
|
changefreq: "monthly",
|
|
priority: 0.7,
|
|
}));
|
|
});
|
|
|
|
return [...staticEntries, ...projectEntries];
|
|
}
|
|
|