Files
skillhub/apps/web/lib/seo.ts
airano 3f599a23fb sync: SEO canonical URLs, curation improvements, batch scripts, stats fix
- Dynamic canonical URLs and alternates for all pages (SEO fix)
- Shared seo.ts utility for canonical path generation
- Improved duplicate detection: standalone > aggregator priority, forks/created_at tie-breakers
- browseReadyFilter now includes unique aggregator skills
- Stats aligned with browseReadyFilter (was showing 16k instead of 62k+)
- Re-review mechanism: content_hash changes trigger needs-re-review
- review_status field added to skills schema
- Batch security scan and quality score scripts
- Indexer Dockerfile updated for curation deps
- Removed roadmap from README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:40:03 +03:30

51 lines
1.5 KiB
TypeScript

import { locales } from '@/i18n';
export const primaryDomain =
process.env.NEXT_PUBLIC_APP_URL || 'https://skills.palebluedot.live';
const DEFAULT_LOCALE = 'en';
/**
* Get canonical URL path for a locale
* (no prefix for default locale, matching localePrefix: 'as-needed')
*/
export function getCanonicalPath(locale: string, route: string): string {
// Normalize route to always start with a slash and not end with a slash
let formattedRoute = route;
if (!formattedRoute.startsWith('/')) {
formattedRoute = `/${formattedRoute}`;
}
if (formattedRoute.length > 1 && formattedRoute.endsWith('/')) {
formattedRoute = formattedRoute.slice(0, -1);
}
if (locale === DEFAULT_LOCALE) {
if (formattedRoute === '/') return '';
return formattedRoute;
}
if (formattedRoute === '/') return `/${locale}`;
return `/${locale}${formattedRoute}`;
}
/**
* Generate Next.js alternates metadata for a specific route
* @param locale Current active locale
* @param route The route path (e.g. '/about', '/categories')
*/
export function getPageAlternates(locale: string, route: string) {
// Ensure we fall back to / if it's empty
const canonicalPath = getCanonicalPath(locale, route) || '/';
const languages: Record<string, string> = {};
for (const l of locales) {
languages[l] = `${primaryDomain}${getCanonicalPath(l, route) || '/'}`;
}
return {
canonical: `${primaryDomain}${canonicalPath}`,
languages,
};
}