import type { Metadata } from "next";
import { Database } 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: "Database Schema",
};
type Column = {
name: string;
type: string;
notes?: string;
};
type Table = {
name: string;
description: string;
columns: Column[];
};
const currentTables: Table[] = [
{
name: "profiles",
description:
"Extends Supabase auth.users with app-specific user preferences and settings.",
columns: [
{ name: "id", type: "uuid", notes: "FK → auth.users" },
{ name: "email", type: "text" },
{ name: "display_name", type: "text", notes: "nullable" },
{ name: "avatar_url", type: "text", notes: "nullable" },
{ name: "created_at", type: "timestamptz" },
{ name: "updated_at", type: "timestamptz" },
],
},
{
name: "saved_domains",
description:
"Domains the user has added to their watchlist for monitoring.",
columns: [
{ name: "id", type: "uuid" },
{ name: "user_id", type: "uuid", notes: "FK → profiles" },
{ name: "domain", type: "text" },
{ name: "tags", type: "text[]", notes: "nullable" },
{ name: "last_checked_at", type: "timestamptz", notes: "nullable" },
{ name: "created_at", type: "timestamptz" },
],
},
{
name: "dns_history",
description:
"Snapshots of DNS records captured at each monitoring check. Used for change detection and history browsing.",
columns: [
{ name: "id", type: "uuid" },
{ name: "saved_domain_id", type: "uuid", notes: "FK → saved_domains" },
{ name: "user_id", type: "uuid", notes: "FK → profiles" },
{ name: "records", type: "jsonb", notes: "Full DNS snapshot" },
{ name: "resolver", type: "text", notes: "e.g. 8.8.8.8" },
{ name: "checked_at", type: "timestamptz" },
],
},
{
name: "availability_history",
description:
"Tracks domain availability (registered / available) over time.",
columns: [
{ name: "id", type: "uuid" },
{ name: "saved_domain_id", type: "uuid", notes: "FK → saved_domains" },
{ name: "user_id", type: "uuid", notes: "FK → profiles" },
{ name: "available", type: "boolean" },
{ name: "checked_at", type: "timestamptz" },
],
},
{
name: "notifications",
description:
"In-app notification feed. Written by the Go monitoring service when changes are detected.",
columns: [
{ name: "id", type: "uuid" },
{ name: "user_id", type: "uuid", notes: "FK → profiles" },
{ name: "saved_domain_id", type: "uuid", notes: "FK → saved_domains" },
{ name: "type", type: "text", notes: "e.g. dns_change, availability" },
{ name: "message", type: "text" },
{ name: "read", type: "boolean" },
{ name: "created_at", type: "timestamptz" },
],
},
];
type PlannedTable = {
name: string;
for: string;
description: string;
keyColumns: string[];
};
const plannedTables: PlannedTable[] = [
{
name: "teams",
for: "Team / org accounts",
description:
"Supports shared watchlists and role-based access for organizations.",
keyColumns: ["id", "name", "owner_id", "created_at"],
},
{
name: "team_members",
for: "Team membership & roles",
description:
"Maps users to teams with a role (admin / viewer). Enables team-aware RLS policies.",
keyColumns: ["team_id", "user_id", "role", "joined_at"],
},
{
name: "domain_folders",
for: "Folder organization",
description:
"Lets users organize monitored domains into named folders. saved_domains will gain an optional folder_id FK.",
keyColumns: ["id", "user_id", "name", "created_at"],
},
{
name: "shared_snapshots",
for: "Public shareable links",
description:
"Stores DNS snapshots accessible via a unique token — no auth required to view.",
keyColumns: [
"id",
"saved_domain_id",
"share_token",
"dns_snapshot",
"expires_at",
],
},
{
name: "notification_channels",
for: "Webhook / Slack / Discord",
description:
"User-configured alert destinations beyond in-app and email. Config stored as JSONB.",
keyColumns: ["id", "user_id", "type", "config", "enabled"],
},
{
name: "subscriptions",
for: "Billing tier tracking",
description:
"Tracks the active plan (free / pro / team) per user for feature gating.",
keyColumns: ["id", "user_id", "plan", "valid_until", "created_at"],
},
];
function TableCard({ table }: { table: Table }) {
return (
{table.columns.map((col) => (
Column
Type
Notes
))}
{col.name}
{col.type}
{col.notes ?? "—"}
VectorDNS uses Supabase (managed Postgres) with Row Level Security. There are currently 5 tables in production, with 6 more planned as features are built out.
These tables will be added as the corresponding features are implemented. Schema details may evolve.
{col}
))}
All tables have RLS enabled. Policies enforce that users can only
read and write their own rows (
user_id = auth.uid()
). Future team-aware policies will extend this to allow team
members access to shared resources based on their role.