canina/frontend/application/app/sitemap.ts
parsa aghaei 75b9d55a24
Some checks failed
Deploy Canina / deploy (push) Has been cancelled
feat(seo): implement technical SEO for product catalog, dynamic sitemaps, RSS feeds and graphify update
2026-08-24 17:04:31 +03:30

119 lines
3.4 KiB
TypeScript

import { MetadataRoute } from 'next';
import { productService } from '../lib/services/productService';
import { PRODUCTS } from '../lib/data/products';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://canina.ir';
// Static routes
const staticRoutes: MetadataRoute.Sitemap = [
{
url: `${baseUrl}`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
{
url: `${baseUrl}/shop`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 0.9,
},
{
url: `${baseUrl}/wiki`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
},
{
url: `${baseUrl}/blog`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
},
{
url: `${baseUrl}/search`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.5,
},
{
url: `${baseUrl}/about`,
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.6,
},
{
url: `${baseUrl}/contact`,
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.6,
},
];
// Dynamic products
let productRoutes: MetadataRoute.Sitemap = [];
try {
const res = await productService.getProducts({ limit: 999 });
const products = res.data && res.data.length > 0 ? res.data : PRODUCTS;
productRoutes = products
.filter((product) => !product.noIndex)
.map((product) => ({
url: `${baseUrl}/shop/${product.slug || product.artNo || product.id}`,
lastModified: product.updatedAt ? new Date(product.updatedAt) : new Date(),
changeFrequency: 'daily',
priority: 0.9,
}));
} catch {
productRoutes = PRODUCTS.map((product) => ({
url: `${baseUrl}/shop/${product.slug || product.artNo || product.id}`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 0.9,
}));
}
// Dynamic shop categories
let categoryRoutes: MetadataRoute.Sitemap = [];
try {
const filters = await productService.getActiveFilters();
if (filters?.categories?.length) {
categoryRoutes = filters.categories.map((cat) => ({
url: `${baseUrl}/shop?category=${cat.slug}`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
}));
}
} catch {
categoryRoutes = [];
}
// Dynamic blogs
let blogRoutes: MetadataRoute.Sitemap = [];
try {
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api';
const apiBase = apiUrl.endsWith('/api') ? apiUrl : `${apiUrl.replace(/\/$/, '')}/api`;
const res = await fetch(`${apiBase}/blogs?page=1&limit=999`, { next: { revalidate: 3600 } });
if (res.ok) {
const data = await res.json();
const blogsList = data.data || [];
blogRoutes = blogsList
.filter((b: any) => !b.noIndex && b.status === 'PUBLISHED')
.map((b: any) => ({
url: `${baseUrl}/blog/${b.slug}`,
lastModified: b.updatedAt ? new Date(b.updatedAt) : new Date(),
changeFrequency: 'weekly',
priority: b.featured ? 0.9 : 0.7,
}));
}
} catch {
// Fallback if backend not reachable at build time
blogRoutes = [];
}
return [...staticRoutes, ...categoryRoutes, ...productRoutes, ...blogRoutes];
}