Files
VectorDNS/components/docs/architecture-diagram.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

194 lines
4.7 KiB
TypeScript

"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>
);
}