Files
VectorDNS/app/docs/api/page.tsx
Aiden Smith 301402e2b1 Add documentation pages with interactive architecture diagram
- /docs route with sidebar navigation and index page
- Architecture section: system overview with React Flow diagram, data flow, database schema
- API reference, configuration guide, and self-hosting docs for Go DNS server
- Feature planning document for future development
- Added docs link to landing page nav
2026-02-24 18:23:09 -05:00

413 lines
13 KiB
TypeScript

import type { Metadata } from "next";
import { FileCode, ArrowRight, CheckCircle, XCircle } from "lucide-react";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
export const metadata: Metadata = {
title: "API Reference",
};
function Method({ method }: { method: string }) {
const colors: Record<string, string> = {
GET: "bg-blue-500/10 text-blue-400 border-blue-500/20",
POST: "bg-green-500/10 text-green-400 border-green-500/20",
};
return (
<span
className={`inline-flex items-center rounded border px-2 py-0.5 font-mono text-xs font-semibold ${colors[method] ?? "bg-muted text-muted-foreground"}`}
>
{method}
</span>
);
}
function CodeBlock({ code }: { code: string }) {
return (
<pre className="overflow-x-auto rounded-md border bg-muted/50 p-4 font-mono text-sm leading-relaxed">
<code>{code.trim()}</code>
</pre>
);
}
function StatusBadge({ code }: { code: number }) {
const isOk = code < 400;
return (
<span
className={`inline-flex items-center gap-1 rounded px-1.5 py-0.5 font-mono text-xs font-medium ${isOk ? "bg-green-500/10 text-green-400" : "bg-red-500/10 text-red-400"}`}
>
{isOk ? (
<CheckCircle className="size-3" />
) : (
<XCircle className="size-3" />
)}
{code}
</span>
);
}
export default function ApiReferencePage() {
return (
<div className="space-y-10">
{/* Header */}
<div>
<div className="mb-3 flex items-center gap-2 text-sm text-muted-foreground">
<FileCode className="size-4" />
<span>Go Server</span>
<ArrowRight className="size-3" />
<span>API Reference</span>
</div>
<h1 className="text-3xl font-bold tracking-tight">API Reference</h1>
<p className="mt-2 text-muted-foreground">
Complete reference for the VectorDNS Go server REST API.
</p>
</div>
{/* Base URL & Auth */}
<Card>
<CardHeader>
<CardTitle className="text-base">
Base URL &amp; Authentication
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<p className="mb-2 text-sm text-muted-foreground">Base URL</p>
<CodeBlock code="https://<your-vps-host>/api/v1" />
</div>
<div>
<p className="mb-2 text-sm text-muted-foreground">
All requests (except{" "}
<span className="font-mono text-xs">/health</span>) require an API
key header:
</p>
<CodeBlock code="X-API-Key: <your-shared-secret>" />
</div>
<p className="text-sm text-muted-foreground">
Set{" "}
<span className="font-mono text-xs bg-muted px-1 py-0.5 rounded">
API_KEY
</span>{" "}
in your server environment to enable authentication. If left empty,
auth is disabled (not recommended for production).
</p>
</CardContent>
</Card>
{/* Endpoints */}
<div className="space-y-6">
<h2 className="text-xl font-semibold tracking-tight">Endpoints</h2>
{/* DNS Lookup */}
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<Method method="POST" />
<code className="font-mono text-sm font-semibold">
/dns/lookup
</code>
</div>
<CardDescription>
Resolve DNS records for a domain. Query one or more record types
in a single request.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<p className="mb-2 text-sm font-medium">Request</p>
<CodeBlock
code={`{
"domain": "example.com",
"types": ["A", "AAAA", "MX", "TXT", "NS", "CNAME", "SOA", "CAA", "SRV"],
"nameserver": "8.8.8.8"
}`}
/>
<ul className="mt-3 space-y-1 text-sm text-muted-foreground">
<li>
<span className="font-mono text-xs text-foreground">
domain
</span>{" "}
<Badge variant="secondary" className="text-xs">
required
</Badge>{" "}
The domain to query.
</li>
<li>
<span className="font-mono text-xs text-foreground">
types
</span>{" "}
<Badge variant="outline" className="text-xs">
optional
</Badge>{" "}
Record types to query. Defaults to all 9 supported types if
omitted.
</li>
<li>
<span className="font-mono text-xs text-foreground">
nameserver
</span>{" "}
<Badge variant="outline" className="text-xs">
optional
</Badge>{" "}
Specific nameserver to query. Defaults to system resolver.
</li>
</ul>
</div>
<Separator />
<div>
<div className="mb-2 flex items-center gap-2">
<p className="text-sm font-medium">Response</p>
<StatusBadge code={200} />
</div>
<CodeBlock
code={`{
"domain": "example.com",
"nameserver": "8.8.8.8",
"records": {
"A": [{ "value": "93.184.216.34", "ttl": 300 }],
"MX": [{ "value": "mail.example.com", "priority": 10, "ttl": 3600 }]
},
"query_time_ms": 12
}`}
/>
</div>
</CardContent>
</Card>
{/* DNS Propagation */}
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<Method method="POST" />
<code className="font-mono text-sm font-semibold">
/dns/propagation
</code>
</div>
<CardDescription>
Check DNS propagation across multiple public resolvers. Queries
resolvers in parallel and compares results.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<p className="mb-2 text-sm font-medium">Request</p>
<CodeBlock
code={`{
"domain": "example.com",
"type": "A",
"resolvers": ["8.8.8.8", "1.1.1.1", "9.9.9.9"]
}`}
/>
<ul className="mt-3 space-y-1 text-sm text-muted-foreground">
<li>
<span className="font-mono text-xs text-foreground">
domain
</span>{" "}
<Badge variant="secondary" className="text-xs">
required
</Badge>{" "}
The domain to check.
</li>
<li>
<span className="font-mono text-xs text-foreground">
type
</span>{" "}
<Badge variant="secondary" className="text-xs">
required
</Badge>{" "}
The record type to check (e.g.{" "}
<span className="font-mono text-xs">A</span>,{" "}
<span className="font-mono text-xs">MX</span>).
</li>
<li>
<span className="font-mono text-xs text-foreground">
resolvers
</span>{" "}
<Badge variant="outline" className="text-xs">
optional
</Badge>{" "}
List of resolver IPs. Defaults to a built-in list of public
resolvers.
</li>
</ul>
</div>
<Separator />
<div>
<div className="mb-2 flex items-center gap-2">
<p className="text-sm font-medium">Response</p>
<StatusBadge code={200} />
</div>
<CodeBlock
code={`{
"domain": "example.com",
"type": "A",
"results": [
{ "resolver": "8.8.8.8", "values": ["93.184.216.34"], "ttl": 300 },
{ "resolver": "1.1.1.1", "values": ["93.184.216.34"], "ttl": 280 },
{ "resolver": "9.9.9.9", "values": ["93.184.216.34"], "ttl": 295 }
],
"consistent": true
}`}
/>
</div>
</CardContent>
</Card>
{/* DNSSEC Validate */}
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<Method method="POST" />
<code className="font-mono text-sm font-semibold">
/dns/validate
</code>
</div>
<CardDescription>
DNSSEC validation for a domain. Checks the AD flag and verifies
the signature chain.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<p className="mb-2 text-sm font-medium">Request</p>
<CodeBlock
code={`{
"domain": "example.com"
}`}
/>
<ul className="mt-3 space-y-1 text-sm text-muted-foreground">
<li>
<span className="font-mono text-xs text-foreground">
domain
</span>{" "}
<Badge variant="secondary" className="text-xs">
required
</Badge>{" "}
The domain to validate.
</li>
</ul>
</div>
<Separator />
<div>
<div className="mb-2 flex items-center gap-2">
<p className="text-sm font-medium">Response</p>
<StatusBadge code={200} />
</div>
<CodeBlock
code={`{
"domain": "example.com",
"dnssec": true,
"chain_valid": true,
"details": "RRSIG verified for A record"
}`}
/>
</div>
</CardContent>
</Card>
{/* Health */}
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<Method method="GET" />
<code className="font-mono text-sm font-semibold">/health</code>
</div>
<CardDescription>
Health check endpoint. No authentication required. Use this for
uptime monitoring.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<div className="mb-2 flex items-center gap-2">
<p className="text-sm font-medium">Response</p>
<StatusBadge code={200} />
</div>
<CodeBlock
code={`{
"status": "ok",
"version": "0.1.0"
}`}
/>
</div>
</CardContent>
</Card>
</div>
{/* Errors */}
<div className="space-y-4">
<h2 className="text-xl font-semibold tracking-tight">
Error Responses
</h2>
<p className="text-sm text-muted-foreground">
All errors return a consistent JSON body:
</p>
<CodeBlock
code={`{
"error": "invalid domain",
"code": "INVALID_DOMAIN"
}`}
/>
<Card>
<CardContent className="pt-6">
<table className="w-full text-sm">
<thead>
<tr className="border-b text-left text-muted-foreground">
<th className="pb-2 pr-4 font-medium">Status</th>
<th className="pb-2 pr-4 font-medium">Code</th>
<th className="pb-2 font-medium">Description</th>
</tr>
</thead>
<tbody className="divide-y">
{[
{
status: 400,
code: "INVALID_DOMAIN",
desc: "Malformed or missing domain",
},
{
status: 400,
code: "INVALID_TYPE",
desc: "Unsupported record type",
},
{
status: 401,
code: "UNAUTHORIZED",
desc: "Missing or invalid API key",
},
{
status: 500,
code: "DNS_ERROR",
desc: "Upstream DNS query failed",
},
{
status: 500,
code: "INTERNAL",
desc: "Unexpected server error",
},
].map((row) => (
<tr key={row.code}>
<td className="py-2 pr-4">
<StatusBadge code={row.status} />
</td>
<td className="py-2 pr-4">
<code className="font-mono text-xs">{row.code}</code>
</td>
<td className="py-2 text-muted-foreground">{row.desc}</td>
</tr>
))}
</tbody>
</table>
</CardContent>
</Card>
</div>
</div>
);
}