import { getTranslations, setRequestLocale } from 'next-intl/server';
import { Header } from '@/components/Header';
import { Footer } from '@/components/Footer';
import { ApiEndpointSection } from '@/components/ApiEndpointSection';
import type { EndpointDef } from '@/components/ApiEndpointSection';
import Link from 'next/link';
import { ArrowLeft, ArrowRight, Search, FileCode, Users, Compass, Mail } from 'lucide-react';
import { getPageAlternates } from '@/lib/seo';
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
return {
alternates: getPageAlternates(locale, '/docs/api'),
};
}
export default async function ApiDocsPage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
setRequestLocale(locale);
const t = await getTranslations('docs');
const isRTL = locale === 'fa';
const ArrowIcon = isRTL ? ArrowLeft : ArrowRight;
const siteUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://skills.palebluedot.live';
const labels = {
parameters: t('api.labels.parameters'),
requestBody: t('api.labels.requestBody'),
required: t('api.labels.required'),
optional: t('api.labels.optional'),
default: t('api.labels.default'),
responseExample: t('api.labels.responseExample'),
rateLimit: t('api.labels.rateLimit'),
cache: t('api.labels.cache'),
authRequired: t('api.labels.authRequired'),
notes: t('api.labels.notes'),
};
// --- Section 1: Skills ---
const skillsEndpoints: EndpointDef[] = [
{
method: 'GET',
path: '/api/skills',
description: t('api.endpoints.searchSkills'),
auth: false,
rateLimit: '60 req/min',
cacheTTL: '5 min',
params: [
{ name: 'q', type: 'string', required: false, description: t('api.params.q') },
{ name: 'category', type: 'string', required: false, description: t('api.params.category') },
{ name: 'platform', type: 'string', required: false, description: t('api.params.platform') + ' (claude, codex, copilot, cursor, windsurf)' },
{ name: 'format', type: 'string', required: false, description: t('api.params.format'), default: 'skill.md' },
{ name: 'verified', type: 'boolean', required: false, description: t('api.params.verified') },
{ name: 'minStars', type: 'number', required: false, description: t('api.params.minStars') },
{ name: 'sort', type: 'string', required: false, description: t('api.params.sort') + ' (stars, downloads, rating, recent)', default: 'downloads' },
{ name: 'page', type: 'number', required: false, description: t('api.params.page'), default: '1' },
{ name: 'limit', type: 'number', required: false, description: t('api.params.limit'), default: '20' },
],
responseExample: `{
"skills": [
{
"id": "anthropic/skills/code-review",
"name": "code-review",
"description": "AI-powered code review assistant",
"githubOwner": "anthropic",
"githubRepo": "skills",
"githubStars": 1234,
"downloadCount": 567,
"securityStatus": "PASS",
"rating": 4.5,
"ratingCount": 23,
"isVerified": true,
"compatibility": { "platforms": ["claude", "cursor"] }
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 150,
"totalPages": 8
},
"searchEngine": "meilisearch"
}`,
notes: t('api.notes.searchFallback'),
},
{
method: 'GET',
path: '/api/skills/:id',
description: t('api.endpoints.getSkill'),
auth: false,
rateLimit: '120 req/min',
responseExample: `{
"id": "anthropic/skills/code-review",
"name": "code-review",
"description": "AI-powered code review assistant",
"githubOwner": "anthropic",
"githubRepo": "skills",
"skillPath": "code-review",
"branch": "main",
"version": "1.0.0",
"license": "MIT",
"githubStars": 1234,
"downloadCount": 567,
"securityScore": 95,
"securityStatus": "PASS",
"isVerified": true,
"compatibility": { "platforms": ["claude"] },
"rawContent": "# Code Review\\n...",
"sourceFormat": "skill.md"
}`,
notes: t('api.notes.viewCount'),
},
{
method: 'GET',
path: '/api/skills/featured',
description: t('api.endpoints.featuredSkills'),
auth: false,
rateLimit: '120 req/min',
cacheTTL: '2 hours',
params: [
{ name: 'limit', type: 'number', required: false, description: t('api.params.limitNum'), default: '6' },
],
},
{
method: 'GET',
path: '/api/skills/recent',
description: t('api.endpoints.recentSkills'),
auth: false,
rateLimit: '120 req/min',
cacheTTL: '1 hour',
params: [
{ name: 'limit', type: 'number', required: false, description: t('api.params.limitNum'), default: '10' },
],
},
{
method: 'POST',
path: '/api/skills/install',
description: t('api.endpoints.trackInstall'),
auth: false,
bodyParams: [
{ name: 'skillId', type: 'string', required: true, description: t('api.params.skillId') },
{ name: 'platform', type: 'string', required: false, description: t('api.params.platform') },
{ name: 'method', type: 'string', required: false, description: t('api.params.method') },
],
responseExample: `{ "success": true, "skillId": "owner/repo/skill", "platform": "claude", "method": "cli" }`,
notes: t('api.notes.installDedup'),
},
{
method: 'POST',
path: '/api/skills/add-request',
description: t('api.endpoints.addRequest'),
auth: true,
bodyParams: [
{ name: 'repositoryUrl', type: 'string', required: true, description: t('api.params.repositoryUrl') },
{ name: 'reason', type: 'string', required: false, description: t('api.params.reason') },
],
responseExample: `{ "success": true, "requestId": "uuid", "hasSkillMd": true, "skillCount": 3 }`,
},
{
method: 'POST',
path: '/api/skills/removal-request',
description: t('api.endpoints.removalRequest'),
auth: true,
bodyParams: [
{ name: 'skillId', type: 'string', required: true, description: t('api.params.skillId') },
{ name: 'reason', type: 'string', required: false, description: t('api.params.reason') },
],
responseExample: `{ "success": true, "requestId": "uuid", "blocked": true }`,
},
];
// --- Section 2: Skill Files ---
const skillFilesEndpoints: EndpointDef[] = [
{
method: 'GET',
path: '/api/skill-files',
description: t('api.endpoints.getSkillFiles'),
auth: false,
rateLimit: '60 req/min',
params: [
{ name: 'id', type: 'string', required: true, description: t('api.params.skillId') },
],
responseExample: `{
"skillId": "owner/repo/skill",
"githubOwner": "owner",
"githubRepo": "repo",
"skillPath": "skill",
"branch": "main",
"files": [
{
"name": "SKILL.md",
"path": "SKILL.md",
"type": "file",
"size": 2048,
"content": "# Skill Name\\n..."
}
],
"fromCache": true,
"cachedAt": "2026-01-15T10:30:00Z"
}`,
notes: t('api.notes.cacheInDB'),
},
{
method: 'GET',
path: '/api/skill-files/zip',
description: t('api.endpoints.downloadZip'),
auth: false,
rateLimit: '60 req/min',
params: [
{ name: 'id', type: 'string', required: true, description: t('api.params.skillId') },
{ name: 'platform', type: 'string', required: false, description: t('api.params.platformZip'), default: 'claude' },
],
notes: t('api.notes.zipTransform'),
},
];
// --- Section 3: User Actions ---
const userActionsEndpoints: EndpointDef[] = [
{
method: 'GET',
path: '/api/ratings',
description: t('api.endpoints.getRatings'),
auth: false,
rateLimit: '120 req/min',
params: [
{ name: 'skillId', type: 'string', required: true, description: t('api.params.skillId') },
{ name: 'limit', type: 'number', required: false, description: t('api.params.limitNum'), default: '10' },
{ name: 'offset', type: 'number', required: false, description: t('api.params.offset'), default: '0' },
],
responseExample: `{
"ratings": [
{
"id": "uuid",
"rating": 5,
"review": "Great skill!",
"createdAt": "2026-01-10T00:00:00Z",
"user": { "id": "uid", "username": "user1", "avatarUrl": "https://..." }
}
],
"summary": { "average": 4.5, "count": 23 }
}`,
},
{
method: 'POST',
path: '/api/ratings',
description: t('api.endpoints.createRating'),
auth: true,
rateLimit: '600 req/min',
bodyParams: [
{ name: 'skillId', type: 'string', required: true, description: t('api.params.skillId') },
{ name: 'rating', type: 'number', required: true, description: t('api.params.rating') },
{ name: 'review', type: 'string', required: false, description: t('api.params.review') },
],
responseExample: `{ "rating": { "id": "uuid", "rating": 5, "review": "..." }, "summary": { "average": 4.5, "count": 24 } }`,
},
{
method: 'GET',
path: '/api/ratings/me',
description: t('api.endpoints.getMyRating'),
auth: true,
rateLimit: '600 req/min',
params: [
{ name: 'skillId', type: 'string', required: true, description: t('api.params.skillId') },
],
responseExample: `{ "rating": { "id": "uuid", "rating": 5, "review": "Great!" } }`,
},
{
method: 'GET',
path: '/api/favorites',
description: t('api.endpoints.getFavorites'),
auth: true,
rateLimit: '600 req/min',
responseExample: `{
"favorites": [
{
"id": "owner/repo/skill",
"name": "skill-name",
"description": "...",
"githubStars": 100,
"downloadCount": 50,
"securityStatus": "PASS",
"isVerified": true,
"rating": 4.5,
"ratingCount": 10
}
]
}`,
},
{
method: 'POST',
path: '/api/favorites',
description: t('api.endpoints.addFavorite'),
auth: true,
rateLimit: '600 req/min',
bodyParams: [
{ name: 'skillId', type: 'string', required: true, description: t('api.params.skillId') },
],
responseExample: `{ "success": true, "favorited": true }`,
},
{
method: 'DELETE',
path: '/api/favorites',
description: t('api.endpoints.removeFavorite'),
auth: true,
rateLimit: '600 req/min',
bodyParams: [
{ name: 'skillId', type: 'string', required: true, description: t('api.params.skillId') },
],
responseExample: `{ "success": true, "favorited": false }`,
},
{
method: 'POST',
path: '/api/favorites/check',
description: t('api.endpoints.checkFavorites'),
auth: true,
rateLimit: '600 req/min',
bodyParams: [
{ name: 'skillIds', type: 'string[]', required: true, description: t('api.params.skillIds') },
],
responseExample: `{ "favorited": { "owner/repo/skill-a": true, "owner/repo/skill-b": false } }`,
},
];
// --- Section 4: Discovery & Metadata ---
const discoveryEndpoints: EndpointDef[] = [
{
method: 'GET',
path: '/api/categories',
description: t('api.endpoints.getCategories'),
auth: false,
rateLimit: '120 req/min',
cacheTTL: '12 hours',
responseExample: `{
"categories": [
{
"id": "cat-id",
"name": "Code Quality",
"slug": "code-quality",
"description": "...",
"icon": "🔍",
"skillCount": 15,
"sortOrder": 1
}
]
}`,
},
{
method: 'GET',
path: '/api/stats',
description: t('api.endpoints.getStats'),
auth: false,
rateLimit: '120 req/min',
cacheTTL: '1 hour',
responseExample: `{
"totalSkills": 850,
"totalDownloads": 12500,
"totalCategories": 23,
"totalContributors": 320,
"platforms": 5
}`,
},
{
method: 'GET',
path: '/api/health',
description: t('api.endpoints.healthCheck'),
auth: false,
responseExample: `{
"status": "healthy",
"timestamp": "2026-02-11T12:00:00Z",
"version": "0.2.4",
"isPrimary": true,
"services": {
"database": { "status": "healthy", "latency": 5 },
"meilisearch": { "status": "healthy", "latency": 12 },
"redis": { "status": "healthy", "latency": 3 }
}
}`,
},
{
method: 'GET',
path: '/api/attribution',
description: t('api.endpoints.getAttribution'),
auth: false,
rateLimit: '120 req/min',
cacheTTL: '1 hour',
responseExample: `{
"totalSkills": 850,
"totalContributors": 320,
"totalRepos": 150,
"licenseDistribution": [
{ "license": "MIT", "count": 50, "percentage": 50 }
],
"lastUpdated": "2026-02-11T00:00:00Z"
}`,
},
];
// --- Section 5: Newsletter ---
const newsletterEndpoints: EndpointDef[] = [
{
method: 'POST',
path: '/api/newsletter/subscribe',
description: t('api.endpoints.subscribe'),
auth: false,
rateLimit: '60 req/min',
bodyParams: [
{ name: 'email', type: 'string', required: true, description: t('api.params.email') },
{ name: 'source', type: 'string', required: false, description: t('api.params.source') },
{ name: 'locale', type: 'string', required: false, description: t('api.params.locale'), default: 'en' },
],
responseExample: `{ "success": true, "subscribed": true }`,
},
{
method: 'POST',
path: '/api/newsletter/unsubscribe',
description: t('api.endpoints.unsubscribe'),
auth: false,
rateLimit: '60 req/min',
bodyParams: [
{ name: 'email', type: 'string', required: true, description: t('api.params.email') },
],
responseExample: `{ "success": true, "unsubscribed": true }`,
notes: t('api.notes.alwaysSuccess'),
},
];
const sections = [
{ title: t('api.sections.skills.title'), description: t('api.sections.skills.description'), icon: Search, endpoints: skillsEndpoints },
{ title: t('api.sections.skillFiles.title'), description: t('api.sections.skillFiles.description'), icon: FileCode, endpoints: skillFilesEndpoints },
{ title: t('api.sections.userActions.title'), description: t('api.sections.userActions.description'), icon: Users, endpoints: userActionsEndpoints },
{ title: t('api.sections.discovery.title'), description: t('api.sections.discovery.description'), icon: Compass, endpoints: discoveryEndpoints },
{ title: t('api.sections.newsletter.title'), description: t('api.sections.newsletter.description'), icon: Mail, endpoints: newsletterEndpoints },
];
return (
{t('title')}
{t('api.title')}
{t('api.description')}
{/* Base URL */}
{t('api.baseUrl')}
{siteUrl}
{/* Authentication */}
{t('api.authSection.title')}
{t('api.authSection.description')}
{t('api.authSection.howTo')}
{/* Rate Limiting */}
{t('api.rateLimitSection.title')}
{t('api.rateLimitSection.description')}
| {t('api.rateLimitSection.tier')} |
{t('api.rateLimitSection.limit')} |
{t('api.rateLimitSection.use')} |
| {t('api.rateLimitSection.anonymous')} |
120 req/min |
{t('api.rateLimitSection.anonymousUse')} |
| {t('api.rateLimitSection.search')} |
60 req/min |
{t('api.rateLimitSection.searchUse')} |
| {t('api.rateLimitSection.authenticated')} |
600 req/min |
{t('api.rateLimitSection.authenticatedUse')} |
{t('api.rateLimitSection.headersTitle')}
X-RateLimit-Limit — {t('api.rateLimitSection.headerLimit')}
X-RateLimit-Remaining — {t('api.rateLimitSection.headerRemaining')}
X-RateLimit-Reset — {t('api.rateLimitSection.headerReset')}
{/* Error Responses */}
{t('api.errorSection.title')}
{t('api.errorSection.description')}
{`{
"error": "Too Many Requests",
"message": "Rate limit exceeded",
"retryAfter": 45,
"limit": 60
}`}
{t('api.errorSection.commonCodes')}
400 — {t('api.errorSection.code400')}
401 — {t('api.errorSection.code401')}
404 — {t('api.errorSection.code404')}
429 — {t('api.errorSection.code429')}
500 — {t('api.errorSection.code500')}
{/* Quick example */}
Quick Example
{`curl "${siteUrl}/api/skills?q=code-review&limit=5"`}
{/* Endpoint Sections */}
{sections.map((section, index) => (
))}
);
}