/opt/canhelp/apps/web/src/app
Edit: /opt/canhelp/apps/web/src/app/sitemap.ts (3664B)
import type { MetadataRoute } from 'next'
function normalizeAppUrl(raw?: string): string {
const fallback = 'http://localhost:3000'
const base = (raw && raw.trim()) || fallback
try {
const url = new URL(base)
if (url.hostname !== 'localhost' && url.protocol !== 'https:') {
url.protocol = 'https:'
}
return url.toString().replace(/\/$/, '')
} catch {
return fallback
}
}
const APP_URL = normalizeAppUrl(process.env.NEXT_PUBLIC_APP_URL)
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000'
const STATIC_ROUTES: MetadataRoute.Sitemap = [
{
url: `${APP_URL}/`,
changeFrequency: 'daily',
priority: 1.0,
},
{
url: `${APP_URL}/tasks`,
changeFrequency: 'hourly',
priority: 0.9,
},
{
url: `${APP_URL}/specialists`,
changeFrequency: 'hourly',
priority: 0.9,
},
{
url: `${APP_URL}/pricing`,
changeFrequency: 'weekly',
priority: 0.6,
},
{
url: `${APP_URL}/support`,
changeFrequency: 'monthly',
priority: 0.4,
},
{
url: `${APP_URL}/become-specialist`,
changeFrequency: 'monthly',
priority: 0.7,
},
]
async function fetchAllTasks(): Promise
{
const entries: MetadataRoute.Sitemap = []
let page = 1
const limit = 100
try {
while (true) {
const res = await fetch(
`${API_URL}/api/tasks?page=${page}&limit=${limit}&status=open`,
{ next: { revalidate: 3600 } },
)
if (!res.ok) break
const json = await res.json()
const tasks: { task: { id: string; updatedAt: string } }[] = json.data ?? []
for (const { task } of tasks) {
entries.push({
url: `${APP_URL}/tasks/${task.id}`,
lastModified: task.updatedAt ? new Date(task.updatedAt) : undefined,
changeFrequency: 'daily',
priority: 0.8,
})
}
if (entries.length >= json.total || tasks.length < limit) break
page++
}
} catch {
// silently skip if API is unavailable during build
}
return entries
}
async function fetchAllSpecialists(): Promise {
const entries: MetadataRoute.Sitemap = []
let page = 1
const limit = 100
try {
while (true) {
const res = await fetch(
`${API_URL}/api/users/specialists?page=${page}&limit=${limit}`,
{ next: { revalidate: 3600 } },
)
if (!res.ok) break
const json = await res.json()
const specialists: {
id: string
updatedAt?: string
personalSiteSlug?: string | null
}[] = json.data ?? []
for (const specialist of specialists) {
entries.push({
url: `${APP_URL}/users/${specialist.id}`,
lastModified: specialist.updatedAt ? new Date(specialist.updatedAt) : undefined,
changeFrequency: 'weekly',
priority: 0.75,
})
if (specialist.personalSiteSlug) {
entries.push({
url: `${APP_URL}/site/${specialist.personalSiteSlug}`,
lastModified: specialist.updatedAt
? new Date(specialist.updatedAt)
: undefined,
changeFrequency: 'weekly',
priority: 0.85,
})
}
}
if (entries.length >= json.total || specialists.length < limit) break
page++
}
} catch {
// silently skip if API is unavailable during build
}
return entries
}
export default async function sitemap(): Promise {
const [tasks, specialists] = await Promise.all([
fetchAllTasks(),
fetchAllSpecialists(),
])
return [...STATIC_ROUTES, ...tasks, ...specialists]
}