← 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 &lt;token&gt;</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>
  );
}