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>
This commit is contained in:
@@ -7,9 +7,23 @@ import { createDb, skillQueries } from '@skillhub/db';
|
||||
import { formatCompactNumber, toPersianNumber } from '@/lib/format-number';
|
||||
import { ExternalLink, Download, Package, Eye, GitFork, ArrowUpDown, FolderGit2 } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import type { Metadata } from 'next';
|
||||
import { getPageAlternates } from '@/lib/seo';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string; username: string }>;
|
||||
}): Promise<Metadata> {
|
||||
const { locale, username } = await params;
|
||||
return {
|
||||
title: `${decodeURIComponent(username)} | SkillHub`,
|
||||
alternates: getPageAlternates(locale, `/owner/${username}`),
|
||||
};
|
||||
}
|
||||
|
||||
const ITEMS_PER_PAGE = 24;
|
||||
|
||||
type SortOption = 'popularity' | 'downloads' | 'stars';
|
||||
@@ -183,11 +197,10 @@ export default async function OwnerPage({ params, searchParams }: OwnerPageProps
|
||||
<span className="text-sm text-text-secondary">{t('repo.filter')}:</span>
|
||||
<Link
|
||||
href={buildUrl({ repo: '', page: 1 })}
|
||||
className={`px-3 py-1 text-xs rounded-full transition-colors ${
|
||||
!activeRepo
|
||||
className={`px-3 py-1 text-xs rounded-full transition-colors ${!activeRepo
|
||||
? 'bg-primary text-primary-foreground font-medium'
|
||||
: 'bg-surface-elevated text-text-secondary hover:bg-surface-subtle border border-border'
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{t('repo.all')} ({locale === 'fa' ? toPersianNumber(stats.totalSkills) : stats.totalSkills})
|
||||
</Link>
|
||||
@@ -195,11 +208,10 @@ export default async function OwnerPage({ params, searchParams }: OwnerPageProps
|
||||
<Link
|
||||
key={r.repo}
|
||||
href={buildUrl({ repo: r.repo, page: 1 })}
|
||||
className={`px-3 py-1 text-xs rounded-full transition-colors ${
|
||||
activeRepo === r.repo
|
||||
className={`px-3 py-1 text-xs rounded-full transition-colors ${activeRepo === r.repo
|
||||
? 'bg-primary text-primary-foreground font-medium'
|
||||
: 'bg-surface-elevated text-text-secondary hover:bg-surface-subtle border border-border'
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{r.repo} ({locale === 'fa' ? toPersianNumber(r.skillCount) : r.skillCount})
|
||||
</Link>
|
||||
@@ -227,11 +239,10 @@ export default async function OwnerPage({ params, searchParams }: OwnerPageProps
|
||||
<Link
|
||||
key={opt.value}
|
||||
href={buildUrl({ sort: opt.value, page: 1 })}
|
||||
className={`px-3 py-1.5 text-sm rounded-lg transition-colors ${
|
||||
sort === opt.value
|
||||
className={`px-3 py-1.5 text-sm rounded-lg transition-colors ${sort === opt.value
|
||||
? 'bg-primary text-primary-foreground font-medium'
|
||||
: 'bg-surface-elevated text-text-secondary hover:bg-surface-subtle border border-border'
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{opt.label}
|
||||
</Link>
|
||||
|
||||
Reference in New Issue
Block a user