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
This commit is contained in:
Aiden Smith
2026-02-24 18:23:09 -05:00
parent f49bdb7099
commit 301402e2b1
14 changed files with 2577 additions and 1 deletions

View File

@@ -0,0 +1,398 @@
import type { Metadata } from "next";
import {
Settings,
ArrowRight,
Shield,
Globe,
Gauge,
Database,
} 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: "Configuration",
};
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 EnvVar({
name,
defaultVal,
required,
description,
}: {
name: string;
defaultVal: string;
required?: boolean;
description: string;
}) {
return (
<tr className="border-b last:border-b-0">
<td className="py-3 pr-4 align-top">
<code className="font-mono text-xs">{name}</code>
</td>
<td className="py-3 pr-4 align-top">
{defaultVal ? (
<code className="font-mono text-xs text-muted-foreground">
{defaultVal}
</code>
) : (
<span className="text-xs text-muted-foreground"></span>
)}
</td>
<td className="py-3 pr-4 align-top">
{required ? (
<Badge variant="secondary" className="text-xs">
required
</Badge>
) : (
<Badge variant="outline" className="text-xs">
optional
</Badge>
)}
</td>
<td className="py-3 align-top text-sm text-muted-foreground">
{description}
</td>
</tr>
);
}
export default function ConfigurationPage() {
return (
<div className="space-y-10">
{/* Header */}
<div>
<div className="mb-3 flex items-center gap-2 text-sm text-muted-foreground">
<Settings className="size-4" />
<span>Go Server</span>
<ArrowRight className="size-3" />
<span>Configuration</span>
</div>
<h1 className="text-3xl font-bold tracking-tight">Configuration</h1>
<p className="mt-2 text-muted-foreground">
All server configuration is done via environment variables or a{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">.env</span>{" "}
file in the project root.
</p>
</div>
{/* .env example */}
<Card>
<CardHeader>
<CardTitle className="text-base">Example .env</CardTitle>
</CardHeader>
<CardContent>
<CodeBlock
code={`PORT=8080
API_KEY=your-secret-api-key
CORS_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
LOG_FORMAT=json`}
/>
<p className="mt-3 text-sm text-muted-foreground">
Copy{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">
.env.example
</span>{" "}
to{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">
.env
</span>{" "}
to get started:{" "}
<code className="font-mono text-xs bg-muted px-1 rounded">
cp .env.example .env
</code>
</p>
</CardContent>
</Card>
{/* Environment Variables reference */}
<div className="space-y-4">
<h2 className="text-xl font-semibold tracking-tight">
Environment Variables
</h2>
<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">Variable</th>
<th className="pb-2 pr-4 font-medium">Default</th>
<th className="pb-2 pr-4 font-medium"></th>
<th className="pb-2 font-medium">Description</th>
</tr>
</thead>
<tbody>
<EnvVar
name="PORT"
defaultVal="8080"
description="Port the HTTP server listens on."
/>
<EnvVar
name="API_KEY"
defaultVal=""
description="Shared secret for API key authentication. Auth is disabled when empty — do not leave empty in production."
/>
<EnvVar
name="CORS_ORIGINS"
defaultVal="http://localhost:3000"
description="Comma-separated list of allowed CORS origins. Set to your frontend domain in production."
/>
<EnvVar
name="LOG_FORMAT"
defaultVal="text"
description='Log output format. "text" for human-readable, "json" for structured logging (recommended for production).'
/>
</tbody>
</table>
</CardContent>
</Card>
</div>
<Separator />
{/* API Key Auth */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Shield className="size-5 text-primary" />
<h2 className="text-xl font-semibold tracking-tight">
API Key Authentication
</h2>
</div>
<p className="text-sm text-muted-foreground">
The Go server uses a shared API key for authentication. This is
intentional: the server is designed to be an internal service called
only by your Next.js backend not directly by end users.
</p>
<Card>
<CardHeader>
<CardTitle className="text-sm">How it works</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<p className="text-sm text-muted-foreground">
When{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">
API_KEY
</span>{" "}
is set, every request (except{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">
GET /health
</span>
) must include the header:
</p>
<CodeBlock code="X-API-Key: your-secret-api-key" />
<p className="text-sm text-muted-foreground">
Requests missing or sending an incorrect key receive a{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">
401 UNAUTHORIZED
</span>{" "}
response.
</p>
<CodeBlock
code={`{
"error": "missing or invalid API key",
"code": "UNAUTHORIZED"
}`}
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-sm">Calling from Next.js</CardTitle>
</CardHeader>
<CardContent>
<CodeBlock
code={`// In your Next.js server action or API route
const res = await fetch(\`\${process.env.DNS_SERVER_URL}/api/v1/dns/lookup\`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.DNS_API_KEY!,
},
body: JSON.stringify({ domain, types }),
});`}
/>
<p className="mt-3 text-sm text-muted-foreground">
Store{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">
DNS_API_KEY
</span>{" "}
as a secret environment variable in your Next.js deployment. Never
expose it client-side.
</p>
</CardContent>
</Card>
</div>
<Separator />
{/* CORS */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Globe className="size-5 text-primary" />
<h2 className="text-xl font-semibold tracking-tight">CORS</h2>
</div>
<p className="text-sm text-muted-foreground">
CORS is handled by{" "}
<a
href="https://github.com/go-chi/cors"
className="text-primary underline-offset-4 hover:underline"
target="_blank"
rel="noopener noreferrer"
>
go-chi/cors
</a>
. Configure allowed origins via the{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">
CORS_ORIGINS
</span>{" "}
variable.
</p>
<Card>
<CardContent className="space-y-3 pt-6">
<p className="text-sm font-medium">Development</p>
<CodeBlock code="CORS_ORIGINS=http://localhost:3000" />
<p className="text-sm font-medium">Production (single domain)</p>
<CodeBlock code="CORS_ORIGINS=https://app.yourdomain.com" />
<p className="text-sm font-medium">Production (multiple domains)</p>
<CodeBlock code="CORS_ORIGINS=https://app.yourdomain.com,https://yourdomain.com" />
</CardContent>
</Card>
<p className="text-sm text-muted-foreground">
Since the Go server is an internal API called server-side by Next.js,
CORS is mostly relevant for development. In production, only your
Next.js server IP/domain needs access.
</p>
</div>
<Separator />
{/* Rate Limiting */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Gauge className="size-5 text-primary" />
<h2 className="text-xl font-semibold tracking-tight">
Rate Limiting
</h2>
</div>
<p className="text-sm text-muted-foreground">
Rate limiting is provided by{" "}
<a
href="https://github.com/go-chi/httprate"
className="text-primary underline-offset-4 hover:underline"
target="_blank"
rel="noopener noreferrer"
>
go-chi/httprate
</a>{" "}
and is enabled by default.
</p>
<Card>
<CardHeader>
<CardTitle className="text-sm">Current defaults</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm text-muted-foreground">
<li> Rate limiting is applied per IP address.</li>
<li> Limits are enforced at the router middleware level.</li>
<li>
Requests exceeding the limit receive{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">
429 Too Many Requests
</span>
.
</li>
</ul>
</CardContent>
</Card>
<Card className="border-dashed">
<CardHeader>
<div className="flex items-center gap-2">
<CardTitle className="text-sm">
Per-API-key rate limiting
</CardTitle>
<Badge variant="outline" className="text-xs">
Planned
</Badge>
</div>
<CardDescription>
When billing tiers are added, rate limits will be enforced per API
key to match the user`&apos;`s plan (free vs. pro).
</CardDescription>
</CardHeader>
<CardContent>
<ul className="space-y-1.5 text-sm text-muted-foreground">
<li> Free tier: limited requests per day</li>
<li> Pro tier: higher limits, hourly checks</li>
<li> Team tier: highest limits, shared across org</li>
</ul>
</CardContent>
</Card>
</div>
<Separator />
{/* Redis Caching */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Database className="size-5 text-primary" />
<h2 className="text-xl font-semibold tracking-tight">
Redis Caching
</h2>
<Badge variant="outline" className="text-xs">
Planned
</Badge>
</div>
<p className="text-sm text-muted-foreground">
DNS result caching via Redis is on the roadmap. When available, the
server will check Redis before querying upstream DNS, storing results
with TTL-based expiry.
</p>
<Card className="border-dashed">
<CardHeader>
<CardTitle className="text-sm">
Planned Redis configuration
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<CodeBlock
code={`# Planned env vars (not yet available)
REDIS_URL=redis://localhost:6379
REDIS_TTL=300 # seconds`}
/>
<p className="text-sm text-muted-foreground">
The cache layer will use{" "}
<a
href="https://github.com/redis/go-redis"
className="text-primary underline-offset-4 hover:underline"
target="_blank"
rel="noopener noreferrer"
>
go-redis
</a>{" "}
and sit as middleware before DNS resolution. A{" "}
<span className="font-mono text-xs bg-muted px-1 rounded">
docker-compose.yml
</span>{" "}
bundling the Go server with Redis will be provided.
</p>
</CardContent>
</Card>
</div>
</div>
);
}