feat: mejorar el sitemap añadiendo rutas legales y asegurando la correcta codificación XML
This commit is contained in:
@@ -8,9 +8,14 @@ export default function robots(): MetadataRoute.Robots {
|
||||
allow: "/",
|
||||
disallow: [
|
||||
"/admin/",
|
||||
"/dashboard/",
|
||||
"/my-appointments/",
|
||||
"/offer-appointments/",
|
||||
"/user/",
|
||||
"/messenger/",
|
||||
"/landing/dashboard/",
|
||||
"/landing/my-appointments/",
|
||||
"/landing/current-plan/",
|
||||
"/landing/upgrade-plan/",
|
||||
"/landing/pending-ratings/",
|
||||
"/landing/subscription/",
|
||||
"/landing/recover-account/",
|
||||
],
|
||||
},
|
||||
|
||||
@@ -4,6 +4,17 @@ import { PublicServicesResults } from "@models/Service.model";
|
||||
import { sliderFrames } from "@core/app/Services/SliderFrames";
|
||||
import { getAllVideos } from "@core/app/landing/help/videoData";
|
||||
|
||||
// Escape text content so the sitemap is always well-formed XML
|
||||
// (URLs with query strings, service names/descriptions, etc.).
|
||||
function escapeXml(value: string): string {
|
||||
return value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "https://turnosxpress.com.ar";
|
||||
|
||||
@@ -83,6 +94,25 @@ export async function GET() {
|
||||
},
|
||||
];
|
||||
|
||||
// Legal pages (rarely change, low priority)
|
||||
const legalRoutes: SitemapRoute[] = [
|
||||
{
|
||||
url: `${baseUrl}/landing/terms`,
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.3,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/landing/privacy`,
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.3,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/landing/cookies`,
|
||||
changeFrequency: "yearly",
|
||||
priority: 0.3,
|
||||
},
|
||||
];
|
||||
|
||||
let serviceRoutes: SitemapRoute[] = [];
|
||||
|
||||
try {
|
||||
@@ -99,19 +129,12 @@ export async function GET() {
|
||||
if (servicesResult && servicesResult.data) {
|
||||
totalPages = servicesResult.pages || 1;
|
||||
|
||||
// Image sitemap entries are omitted: service images may be hosted
|
||||
// on arbitrary external domains, which the image extension can't verify.
|
||||
const newRoutes = servicesResult.data.map((service) => ({
|
||||
url: `${baseUrl}/landing/service/${service.id}`,
|
||||
changeFrequency: "weekly",
|
||||
priority: 0.8,
|
||||
images: [
|
||||
{
|
||||
url: service.image.startsWith("http")
|
||||
? service.image
|
||||
: `${process.env.NEXT_PUBLIC_API_URL}${service.image}`,
|
||||
title: service.name,
|
||||
caption: service.description,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
serviceRoutes = [...serviceRoutes, ...newRoutes];
|
||||
@@ -126,7 +149,7 @@ export async function GET() {
|
||||
console.error("Error generating sitemap:", error);
|
||||
}
|
||||
|
||||
const allRoutes = [...staticRoutes, ...videoRoutes, ...authRoutes, ...marketingRoutes, ...serviceRoutes];
|
||||
const allRoutes = [...staticRoutes, ...videoRoutes, ...authRoutes, ...marketingRoutes, ...legalRoutes, ...serviceRoutes];
|
||||
|
||||
const sitemapXml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
@@ -135,18 +158,18 @@ export async function GET() {
|
||||
.map((route) => {
|
||||
return `
|
||||
<url>
|
||||
<loc>${route.url}</loc>
|
||||
${route.lastModified ? `<lastmod>${route.lastModified}</lastmod>` : ""}
|
||||
<changefreq>${route.changeFrequency}</changefreq>
|
||||
<loc>${escapeXml(route.url)}</loc>
|
||||
${route.lastModified ? `<lastmod>${escapeXml(route.lastModified)}</lastmod>` : ""}
|
||||
<changefreq>${escapeXml(route.changeFrequency)}</changefreq>
|
||||
<priority>${route.priority}</priority>
|
||||
${route.images
|
||||
? route.images
|
||||
.map(
|
||||
(img) => `
|
||||
<image:image>
|
||||
<image:loc>${img.url}</image:loc>
|
||||
<image:title><![CDATA[${img.title}]]></image:title>
|
||||
<image:caption><![CDATA[${img.caption}]]></image:caption>
|
||||
<image:loc>${escapeXml(img.url)}</image:loc>
|
||||
<image:title>${escapeXml(img.title)}</image:title>
|
||||
<image:caption>${escapeXml(img.caption)}</image:caption>
|
||||
</image:image>`
|
||||
)
|
||||
.join("")
|
||||
@@ -159,7 +182,10 @@ export async function GET() {
|
||||
|
||||
return new NextResponse(sitemapXml, {
|
||||
headers: {
|
||||
"Content-Type": "application/xml",
|
||||
"Content-Type": "application/xml; charset=utf-8",
|
||||
// Regenerated on every request (fetch no-store): cache at the edge/CDN
|
||||
// so crawlers and browsers don't re-hit the services API each time.
|
||||
"Cache-Control": "public, max-age=3600, s-maxage=86400, stale-while-revalidate=86400",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user