import type { MetadataRoute } from 'next'; import { locales } from '@/i18n/config'; import { fetchActiveListings } from '@/lib/listings-server'; const siteUrl = process.env['NEXT_PUBLIC_SITE_URL'] || 'https://goodgo.vn'; /** * Dynamic sitemap that includes: * - Static pages (home, search, login, register) per locale * - All active property listings per locale * * ISR: the server-side fetch in fetchActiveListings uses `next: { revalidate: 3600 }` * so the sitemap is regenerated roughly every hour. */ export default async function sitemap(): Promise { // ------------------------------------------------------------------ // 1. Static pages // ------------------------------------------------------------------ const staticRoutes: MetadataRoute.Sitemap = []; for (const locale of locales) { staticRoutes.push( { url: `${siteUrl}/${locale}`, lastModified: new Date(), changeFrequency: 'daily', priority: 1, }, { url: `${siteUrl}/${locale}/search`, lastModified: new Date(), changeFrequency: 'daily', priority: 0.9, }, { url: `${siteUrl}/${locale}/login`, lastModified: new Date(), changeFrequency: 'monthly', priority: 0.3, }, { url: `${siteUrl}/${locale}/register`, lastModified: new Date(), changeFrequency: 'monthly', priority: 0.3, }, ); } // ------------------------------------------------------------------ // 2. Dynamic listing pages // ------------------------------------------------------------------ const listingRoutes: MetadataRoute.Sitemap = []; try { // Fetch up to 1000 listings per page, paginate if needed. // For very large catalogs this could be split into multiple sitemap // indices, but for the current scale (<50k) a single file is fine. let page = 1; let totalPages = 1; do { const result = await fetchActiveListings({ page, limit: 1000 }); totalPages = result.totalPages; for (const listing of result.data) { for (const locale of locales) { listingRoutes.push({ url: `${siteUrl}/${locale}/listings/${listing.id}`, lastModified: listing.publishedAt ? new Date(listing.publishedAt) : new Date(listing.createdAt), changeFrequency: 'weekly', priority: 0.8, }); } } page++; } while (page <= totalPages); } catch { // If the API is unreachable, return static pages only. // The sitemap will be retried on the next request (ISR). } return [...staticRoutes, ...listingRoutes]; }