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,193 @@
"use client";
import {
ReactFlow,
Background,
type Node,
type Edge,
type NodeProps,
Handle,
Position,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import { Globe, Server, Database, Wifi } from "lucide-react";
type ServiceNodeData = {
label: string;
subtitle: string;
icon: React.ElementType;
color: string;
items?: string[];
};
function ServiceNode({ data }: NodeProps<Node<ServiceNodeData>>) {
const Icon = data.icon;
return (
<div
className="rounded-lg border bg-card px-5 py-4 shadow-sm"
style={{ minWidth: 200 }}
>
<Handle type="target" position={Position.Top} className="opacity-0" />
<Handle type="source" position={Position.Bottom} className="opacity-0" />
<Handle
type="source"
position={Position.Right}
id="right"
className="opacity-0"
/>
<Handle
type="target"
position={Position.Left}
id="left"
className="opacity-0"
/>
<div className="flex items-center gap-2.5">
<div
className="flex size-8 items-center justify-center rounded-md"
style={{ backgroundColor: data.color }}
>
<Icon className="size-4 text-white" />
</div>
<div>
<div className="text-sm font-semibold text-foreground">
{data.label}
</div>
<div className="text-xs text-muted-foreground">{data.subtitle}</div>
</div>
</div>
{data.items && (
<ul className="mt-3 space-y-1 border-t pt-3">
{data.items.map((item) => (
<li
key={item}
className="flex items-center gap-1.5 text-[11px] text-muted-foreground"
>
<span
className="size-1 shrink-0 rounded-full"
style={{ backgroundColor: data.color }}
/>
{item}
</li>
))}
</ul>
)}
</div>
);
}
const nodeTypes = { service: ServiceNode };
const nodes: Node<ServiceNodeData>[] = [
{
id: "nextjs",
type: "service",
position: { x: 0, y: 0 },
data: {
label: "Next.js",
subtitle: "Vercel (Frontend)",
icon: Globe,
color: "#3b82f6",
items: ["UI / SSR", "Auth (Supabase)", "WHOIS lookups", "Static pages"],
},
},
{
id: "go",
type: "service",
position: { x: 350, y: 0 },
data: {
label: "Go DNS API",
subtitle: "VPS (Microservice)",
icon: Server,
color: "#10b981",
items: [
"DNS resolution (miekg/dns)",
"DNSSEC validation",
"Propagation checks",
"Monitoring cron",
],
},
},
{
id: "supabase",
type: "service",
position: { x: 0, y: 230 },
data: {
label: "Supabase",
subtitle: "Database & Auth",
icon: Database,
color: "#8b5cf6",
items: ["Postgres DB", "Auth (OAuth + email)", "Row Level Security"],
},
},
{
id: "dns",
type: "service",
position: { x: 350, y: 230 },
data: {
label: "DNS Resolvers",
subtitle: "Authoritative NS",
icon: Wifi,
color: "#f59e0b",
items: ["Google (8.8.8.8)", "Cloudflare (1.1.1.1)", "Authoritative NS"],
},
},
];
const edges: Edge[] = [
{
id: "nextjs-go",
source: "nextjs",
target: "go",
sourceHandle: "right",
targetHandle: "left",
label: "HTTPS",
animated: true,
style: { stroke: "#3b82f6" },
labelStyle: { fontSize: 15, fill: "#94a3b8", fontWeight: "bold" },
labelBgStyle: { fill: "transparent" },
},
{
id: "nextjs-supabase",
source: "nextjs",
target: "supabase",
label: "SDK",
style: { stroke: "#8b5cf6" },
labelStyle: { fontSize: 15, fill: "#94a3b8", fontWeight: "bold" },
labelBgStyle: { fill: "transparent" },
},
{
id: "go-dns",
source: "go",
target: "dns",
label: "UDP/TCP",
animated: true,
style: { stroke: "#10b981" },
labelStyle: { fontSize: 15, fill: "#94a3b8", fontWeight: "bold" },
labelBgStyle: { fill: "transparent" },
},
];
export function ArchitectureDiagram() {
return (
<div className="h-105 w-full rounded-lg border bg-background">
<ReactFlow
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
fitView
fitViewOptions={{ padding: 0.3 }}
proOptions={{ hideAttribution: true }}
nodesDraggable={false}
nodesConnectable={false}
elementsSelectable={false}
panOnDrag={false}
zoomOnScroll={false}
zoomOnPinch={false}
zoomOnDoubleClick={false}
preventScrolling={false}
>
<Background gap={20} size={1} className="opacity-30" />
</ReactFlow>
</div>
);
}

View File

@@ -0,0 +1,85 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import {
BookOpen,
Layers,
ArrowRightLeft,
Database,
FileCode,
Settings,
Server,
} from "lucide-react";
const sections = [
{
title: "Getting Started",
items: [{ title: "Overview", href: "/docs", icon: BookOpen }],
},
{
title: "Architecture",
items: [
{ title: "System Overview", href: "/docs/architecture", icon: Layers },
{
title: "Data Flow",
href: "/docs/architecture/data-flow",
icon: ArrowRightLeft,
},
{
title: "Database Schema",
href: "/docs/architecture/database",
icon: Database,
},
],
},
{
title: "API",
items: [
{ title: "API Reference", href: "/docs/api", icon: FileCode },
{ title: "Configuration", href: "/docs/configuration", icon: Settings },
],
},
{
title: "Deployment",
items: [
{ title: "Self-Hosting", href: "/docs/self-hosting", icon: Server },
],
},
];
export function DocsSidebar() {
const pathname = usePathname();
return (
<nav className="space-y-6">
{sections.map((section) => (
<div key={section.title}>
<h4 className="mb-2 text-sm font-semibold tracking-tight text-muted-foreground">
{section.title}
</h4>
<ul className="space-y-1">
{section.items.map((item) => {
const isActive = pathname === item.href;
return (
<li key={item.href}>
<Link
href={item.href}
className={`flex items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors hover:bg-muted ${
isActive
? "bg-muted font-medium text-foreground"
: "text-muted-foreground"
}`}
>
<item.icon className="size-4" />
{item.title}
</Link>
</li>
);
})}
</ul>
</div>
))}
</nav>
);
}