← back to Omega Watches 2
src/app/(dashboard)/api-docs/page.tsx
225 lines
"use client";
const endpoints = [
{
category: "Authentication",
routes: [
{ method: "POST", path: "/api/auth/login", desc: "Login with username/password", params: "username, password", returns: "token, username, role" },
],
},
{
category: "System",
routes: [
{ method: "GET", path: "/api/health", desc: "System health check", params: "—", returns: "status, version, uptime, database stats" },
{ method: "GET", path: "/api/dashboard", desc: "Dashboard aggregated data", params: "—", returns: "recent_runs, collection_summary, alerts, 24h stats, top_movers" },
{ method: "POST", path: "/api/admin/refresh-views", desc: "Refresh materialized views", params: "—", returns: "view refresh results with timing" },
],
},
{
category: "Watch References",
routes: [
{ method: "GET", path: "/api/references", desc: "List watch references with stats", params: "collection, search, limit", returns: "references[], summary" },
{ method: "GET", path: "/api/models/:ref/timeseries", desc: "Price history for a reference", params: "event_types, date_from, date_to, include_asks", returns: "events[], msrp_series[]" },
{ method: "GET", path: "/api/models/:ref/distributions", desc: "Price & condition distributions", params: "—", returns: "price_distribution, condition_breakdown, source_breakdown" },
],
},
{
category: "MSRP Ledger",
routes: [
{ method: "GET", path: "/api/msrp/:ref", desc: "MSRP history for a reference", params: "region (default: US)", returns: "current_msrp, history[], changes[]" },
],
},
{
category: "Data Sources",
routes: [
{ method: "GET", path: "/api/sources", desc: "List data sources with health", params: "—", returns: "sources[] with reliability, runs, records" },
{ method: "GET", path: "/api/sources/:name/status", desc: "Detailed source status", params: "—", returns: "source details, event counts, metrics" },
],
},
{
category: "Collector Jobs",
routes: [
{ method: "GET", path: "/api/jobs", desc: "List collector runs", params: "date, status, limit (max 200)", returns: "runs[] with metrics" },
{ method: "POST", path: "/api/jobs/run", desc: "Trigger incremental run", params: "source_name, job_type?", returns: "run_id" },
{ method: "POST", path: "/api/jobs/backfill", desc: "Trigger backfill run", params: "source_name, date_from, date_to", returns: "run_id" },
],
},
{
category: "Quality Control",
routes: [
{ method: "GET", path: "/api/quarantine", desc: "List quarantined records", params: "resolved, severity, limit", returns: "records[] with severity breakdown" },
{ method: "POST", path: "/api/quarantine/:id/resolve", desc: "Resolve quarantine record", params: "resolution_notes, action (release|reject)", returns: "updated record" },
],
},
{
category: "Entity Resolution",
routes: [
{ method: "GET", path: "/api/entity-resolution", desc: "Unmatched events with suggestions", params: "—", returns: "unmatched[], suggestions{}, references[], stats" },
{ method: "POST", path: "/api/entity-resolution", desc: "Link event to reference", params: "event_id, reference_number", returns: "updated event" },
],
},
{
category: "Snapshots & Audit",
routes: [
{ method: "GET", path: "/api/snapshots", desc: "Raw snapshot archive", params: "source, limit (max 500)", returns: "snapshots[], stats, by_source" },
{ method: "GET", path: "/api/auditlog", desc: "Audit log entries", params: "table, operation, limit", returns: "entries[] (admin/auditor only)" },
],
},
{
category: "FX Rates",
routes: [
{ method: "GET", path: "/api/fx-rates", desc: "Currency conversion rates", params: "—", returns: "latest[], history[], stats" },
{ method: "POST", path: "/api/fx-rates", desc: "Add/update FX rate", params: "currency_from, rate, rate_date, source?", returns: "saved rate" },
],
},
{
category: "Price Alerts",
routes: [
{ method: "GET", path: "/api/alerts", desc: "List alert rules with triggered status", params: "—", returns: "rules[], summary (total, active, triggered)" },
{ method: "POST", path: "/api/alerts", desc: "Create new price alert rule", params: "reference_number, alert_type (price_below|price_above), threshold, notes?", returns: "created rule" },
{ method: "PUT", path: "/api/alerts", desc: "Toggle alert active status", params: "id, is_active", returns: "updated status" },
],
},
{
category: "Condition Mappings",
routes: [
{ method: "GET", path: "/api/condition-mappings", desc: "List condition mappings by source", params: "—", returns: "mappings[], by_source{}, stats" },
{ method: "POST", path: "/api/condition-mappings", desc: "Add/update condition mapping", params: "source_name, source_category, normalized_grade, score_0_100", returns: "saved mapping" },
],
},
{
category: "Data Export",
routes: [
{ method: "GET", path: "/api/export", desc: "Export data as CSV or JSON", params: "type (market_events|msrp), reference, collection, date_from, date_to, format (csv|json), limit", returns: "CSV file or JSON array" },
],
},
];
const methodColors: Record<string, string> = {
GET: "bg-blue-900 text-blue-300",
POST: "bg-green-900 text-green-300",
PUT: "bg-yellow-900 text-yellow-300",
DELETE: "bg-red-900 text-red-300",
};
export default function ApiDocsPage() {
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h1 className="text-xl font-bold text-gray-100">API Documentation</h1>
<div className="text-xs text-gray-500">
Base URL: <code className="rounded bg-navy-800 px-2 py-1 text-omega-400">http://45.61.58.125:7600</code>
</div>
</div>
<div className="card border-navy-700">
<h2 className="mb-3 text-sm font-semibold uppercase tracking-wider text-gray-400">
Authentication
</h2>
<div className="space-y-2 text-sm text-gray-400">
<p>All endpoints (except /api/health) require authentication via JWT token.</p>
<p>Include the token in either:</p>
<ul className="ml-4 list-disc space-y-1">
<li><code className="text-omega-400">Authorization: Bearer <token></code> header</li>
<li><code className="text-omega-400">omega_session</code> cookie (set automatically on login)</li>
</ul>
<p className="mt-2">Roles: <span className="text-red-400">admin</span>, <span className="text-orange-400">operator</span>, <span className="text-blue-400">auditor</span>, <span className="text-gray-300">viewer</span></p>
</div>
</div>
{endpoints.map((cat) => (
<div key={cat.category} className="card border-navy-700">
<h2 className="mb-4 text-sm font-semibold uppercase tracking-wider text-gray-400">
{cat.category}
</h2>
<div className="space-y-3">
{cat.routes.map((r) => (
<div key={`${r.method}-${r.path}`} className="rounded-lg bg-navy-900 p-3">
<div className="flex items-center gap-3">
<span className={`rounded px-2 py-0.5 text-xs font-bold ${methodColors[r.method] || "bg-gray-800 text-gray-300"}`}>
{r.method}
</span>
<code className="text-sm text-omega-400">{r.path}</code>
</div>
<div className="mt-2 text-sm text-gray-300">{r.desc}</div>
<div className="mt-2 grid gap-2 md:grid-cols-2">
<div>
<span className="text-xs text-gray-500">Parameters: </span>
<span className="text-xs text-gray-400">{r.params}</span>
</div>
<div>
<span className="text-xs text-gray-500">Returns: </span>
<span className="text-xs text-gray-400">{r.returns}</span>
</div>
</div>
</div>
))}
</div>
</div>
))}
<div className="card border-navy-700">
<h2 className="mb-3 text-sm font-semibold uppercase tracking-wider text-gray-400">
Response Format
</h2>
<div className="space-y-3">
<div>
<div className="mb-1 text-xs text-gray-500">Success</div>
<pre className="rounded bg-navy-900 p-3 text-xs text-gray-300">
{`{
"success": true,
"data": { ... },
"meta": { "total": 100, "timestamp": "..." }
}`}
</pre>
</div>
<div>
<div className="mb-1 text-xs text-gray-500">Error</div>
<pre className="rounded bg-navy-900 p-3 text-xs text-gray-300">
{`{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Login required"
}
}`}
</pre>
</div>
</div>
</div>
<div className="card border-navy-700">
<h2 className="mb-3 text-sm font-semibold uppercase tracking-wider text-gray-400">
Cron Schedule
</h2>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-navy-700 text-left text-xs uppercase text-gray-500">
<th className="p-2">Time (UTC)</th>
<th className="p-2">Source</th>
<th className="p-2">Script</th>
<th className="p-2">Timeout</th>
</tr>
</thead>
<tbody className="text-gray-400">
{[
["06:00", "Omega Official", "daily-msrp.sh", "5 min"],
["08:00", "Chrono24", "daily-chrono24.sh", "10 min"],
["10:00", "Antiquorum", "daily-antiquorum.sh", "10 min"],
].map(([time, source, script, timeout]) => (
<tr key={script} className="border-b border-navy-800">
<td className="p-2 font-mono text-omega-400">{time}</td>
<td className="p-2">{source}</td>
<td className="p-2 font-mono text-xs">{script}</td>
<td className="p-2">{timeout}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
);
}