Files
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

312 lines
9.6 KiB
TypeScript

import type { Metadata } from "next";
import { ArrowRightLeft, ArrowDown } from "lucide-react";
import {
Card,
CardContent,
CardHeader,
CardTitle,
CardDescription,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
export const metadata: Metadata = {
title: "Data Flow",
};
const dnsQuerySteps = [
{
step: "1",
actor: "User",
action: "Submits a domain name in the VectorDNS UI",
detail: "Browser sends a request to a Next.js API route on Vercel.",
},
{
step: "2",
actor: "Next.js API Route",
action: "Proxies the request to the Go DNS service",
detail:
"Adds the shared API key header and forwards to the Go server over HTTPS.",
},
{
step: "3",
actor: "Go Microservice",
action: "Resolves DNS records via UDP/TCP",
detail:
"Uses miekg/dns to query resolvers (Google 8.8.8.8, Cloudflare 1.1.1.1) or authoritative nameservers directly.",
},
{
step: "4",
actor: "DNS Resolvers",
action: "Return DNS records",
detail:
"Authoritative nameservers or recursive resolvers respond with the requested record types.",
},
{
step: "5",
actor: "Go Microservice",
action: "Returns structured JSON to Next.js",
detail: "Parsed and normalized DNS records are sent back over HTTPS.",
},
{
step: "6",
actor: "Next.js",
action: "Stores results in Supabase & returns to client",
detail:
"DNS history is written to Supabase for authenticated users. The response is returned to the browser.",
},
];
const monitoringSteps = [
{
step: "1",
actor: "Go Cron Job",
action: "Triggers on a schedule (daily by default)",
detail:
"A native Go cron scheduler runs inside the VPS process — no serverless timeouts.",
},
{
step: "2",
actor: "Go Microservice",
action: "Fetches monitored domains from Supabase",
detail:
"Reads the saved_domains table to get the list of domains and their last-known DNS snapshot.",
},
{
step: "3",
actor: "Go Microservice",
action: "Re-queries DNS for each domain",
detail:
"Performs fresh DNS lookups and diffs the result against the stored snapshot.",
},
{
step: "4",
actor: "Go Microservice",
action: "Detects changes",
detail:
"If records changed, writes a new entry to dns_history and availability_history.",
},
{
step: "5",
actor: "Go Microservice",
action: "Triggers notifications",
detail:
"Writes to the notifications table. Next.js picks these up for in-app display; Resend handles email delivery.",
},
];
const whoIsSteps = [
{
step: "1",
actor: "User",
action: "Requests WHOIS data for a domain",
detail: "Next.js handles this entirely — no Go service involved.",
},
{
step: "2",
actor: "Next.js API Route",
action: "Calls the whoiser library",
detail:
"whoiser queries the appropriate WHOIS server directly from Vercel.",
},
{
step: "3",
actor: "Next.js",
action: "Returns parsed WHOIS data to the client",
detail: "Registrar, expiry, nameservers, and registration details.",
},
];
type FlowStep = {
step: string;
actor: string;
action: string;
detail: string;
};
function FlowSteps({ steps }: { steps: FlowStep[] }) {
return (
<div className="space-y-2">
{steps.map((s, i) => (
<div key={s.step}>
<div className="flex gap-4">
<div className="flex flex-col items-center">
<div className="flex size-7 shrink-0 items-center justify-center rounded-full bg-primary text-xs font-bold text-primary-foreground">
{s.step}
</div>
{i < steps.length - 1 && (
<div className="mt-1 flex flex-1 flex-col items-center">
<div className="w-px flex-1 bg-border" />
<ArrowDown className="size-3 text-muted-foreground" />
</div>
)}
</div>
<div className="pb-4">
<div className="flex flex-wrap items-center gap-2">
<Badge variant="outline" className="text-xs">
{s.actor}
</Badge>
<span className="text-sm font-medium">{s.action}</span>
</div>
<p className="mt-1 text-sm text-muted-foreground">{s.detail}</p>
</div>
</div>
</div>
))}
</div>
);
}
export default function DataFlowPage() {
return (
<div className="space-y-10">
{/* Page header */}
<div className="space-y-3">
<div className="flex items-center gap-2">
<ArrowRightLeft className="size-6 text-primary" />
<h1 className="text-3xl font-bold tracking-tight">Data Flow</h1>
</div>
<p className="text-lg text-muted-foreground">
How requests travel through VectorDNS from the browser through
Next.js and the Go DNS service to resolvers, and how results are
stored.
</p>
</div>
<Separator />
{/* DNS query flow */}
<div className="space-y-4">
<div className="space-y-1">
<h2 className="text-xl font-semibold tracking-tight">
DNS Record Lookup
</h2>
<p className="text-sm text-muted-foreground">
User-initiated DNS query flow.
</p>
</div>
<Card className="overflow-hidden">
<CardContent className="p-0">
<pre className="overflow-x-auto bg-muted/50 p-4 font-mono text-xs text-foreground">
User Next.js API Route Go DNS API DNS Resolvers
</pre>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<FlowSteps steps={dnsQuerySteps} />
</CardContent>
</Card>
</div>
<Separator />
{/* Monitoring flow */}
<div className="space-y-4">
<div className="space-y-1">
<h2 className="text-xl font-semibold tracking-tight">
Domain Monitoring
</h2>
<p className="text-sm text-muted-foreground">
Scheduled background job runs on the VPS, no user trigger
required.
</p>
</div>
<Card className="overflow-hidden">
<CardContent className="p-0">
<pre className="overflow-x-auto bg-muted/50 p-4 font-mono text-xs text-foreground">
Go Cron Supabase (read) DNS Resolvers Supabase (write)
Notifications
</pre>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<FlowSteps steps={monitoringSteps} />
</CardContent>
</Card>
</div>
<Separator />
{/* WHOIS flow */}
<div className="space-y-4">
<div className="space-y-1">
<h2 className="text-xl font-semibold tracking-tight">
WHOIS Lookups
</h2>
<p className="text-sm text-muted-foreground">
Handled entirely by Next.js the Go service is not involved.
</p>
</div>
<Card className="overflow-hidden">
<CardContent className="p-0">
<pre className="overflow-x-auto bg-muted/50 p-4 font-mono text-xs text-foreground">
User Next.js API Route (whoiser) WHOIS Servers
</pre>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<FlowSteps steps={whoIsSteps} />
</CardContent>
</Card>
</div>
<Separator />
{/* Key design notes */}
<div className="space-y-4">
<h2 className="text-xl font-semibold tracking-tight">
Key Design Notes
</h2>
<div className="grid gap-4 sm:grid-cols-2">
<Card>
<CardHeader>
<CardTitle className="text-sm">No direct client Go</CardTitle>
<CardDescription className="text-sm">
The browser never calls the Go service directly. All requests go
through Next.js API routes, which validate the session and add
the API key.
</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-sm">
Persistent process on VPS
</CardTitle>
<CardDescription className="text-sm">
Unlike serverless functions, the Go service runs continuously
enabling native cron jobs and long-running monitoring without
timeout constraints.
</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-sm">UDP/TCP, not DoH</CardTitle>
<CardDescription className="text-sm">
DNS queries use direct UDP/TCP to resolvers or authoritative
nameservers via miekg/dns faster and more capable than
DNS-over-HTTPS.
</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-sm">
Supabase as source of truth
</CardTitle>
<CardDescription className="text-sm">
Both Next.js and the Go service read/write Supabase. Row Level
Security ensures users only access their own data.
</CardDescription>
</CardHeader>
</Card>
</div>
</div>
</div>
);
}