1
mirror of https://github.com/jakejarvis/hoot.git synced 2025-10-18 20:14:25 -04:00
Files
hoot/server/db/schema.ts

268 lines
8.3 KiB
TypeScript

import { sql } from "drizzle-orm";
import {
boolean,
check,
doublePrecision,
index,
integer,
jsonb,
pgEnum,
pgTable,
text,
timestamp,
unique,
uuid,
} from "drizzle-orm/pg-core";
import type { Registration } from "@/lib/schemas";
// Enums
export const providerCategory = pgEnum("provider_category", [
"hosting",
"email",
"dns",
"ca",
"registrar",
]);
export const dnsRecordType = pgEnum("dns_record_type", [
"A",
"AAAA",
"MX",
"TXT",
"NS",
]);
export const dnsResolver = pgEnum("dns_resolver", ["cloudflare", "google"]);
// Providers
export const providers = pgTable(
"providers",
{
id: uuid("id").primaryKey().defaultRandom(),
category: providerCategory("category").notNull(),
name: text("name").notNull(),
domain: text("domain"),
slug: text("slug").notNull(),
createdAt: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true })
.defaultNow()
.notNull(),
},
(t) => [unique("u_providers_category_slug").on(t.category, t.slug)],
);
// Domains
export const domains = pgTable(
"domains",
{
id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull(),
tld: text("tld").notNull(),
unicodeName: text("unicode_name").notNull(),
createdAt: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true })
.defaultNow()
.notNull(),
},
(t) => [
unique("u_domains_name").on(t.name),
index("i_domains_tld").on(t.tld),
],
);
// Registration (snapshot)
export const registrations = pgTable(
"registrations",
{
domainId: uuid("domain_id")
.primaryKey()
.references(() => domains.id, { onDelete: "cascade" }),
isRegistered: boolean("is_registered").notNull(),
privacyEnabled: boolean("privacy_enabled"),
registry: text("registry"),
creationDate: timestamp("creation_date", { withTimezone: true }),
updatedDate: timestamp("updated_date", { withTimezone: true }),
expirationDate: timestamp("expiration_date", { withTimezone: true }),
deletionDate: timestamp("deletion_date", { withTimezone: true }),
transferLock: boolean("transfer_lock"),
statuses: jsonb("statuses")
.$type<Registration["statuses"]>()
.notNull()
.default(sql`'[]'::jsonb`),
contacts: jsonb("contacts")
.$type<{ contacts?: Registration["contacts"] }>()
.notNull()
.default(sql`'{}'::jsonb`),
whoisServer: text("whois_server"),
rdapServers: jsonb("rdap_servers")
.$type<string[]>()
.notNull()
.default(sql`'[]'::jsonb`),
source: text("source").notNull(),
registrarProviderId: uuid("registrar_provider_id").references(
() => providers.id,
),
resellerProviderId: uuid("reseller_provider_id").references(
() => providers.id,
),
fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull(),
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
},
(t) => [
index("i_reg_registrar").on(t.registrarProviderId),
index("i_reg_expires").on(t.expiresAt),
],
);
export const registrationNameservers = pgTable(
"registration_nameservers",
{
id: uuid("id").primaryKey().defaultRandom(),
domainId: uuid("domain_id")
.notNull()
.references(() => domains.id, { onDelete: "cascade" }),
host: text("host").notNull(),
ipv4: jsonb("ipv4").$type<string[]>().notNull().default(sql`'[]'::jsonb`),
ipv6: jsonb("ipv6").$type<string[]>().notNull().default(sql`'[]'::jsonb`),
},
(t) => [
unique("u_reg_ns").on(t.domainId, t.host),
index("i_reg_ns_host").on(t.host),
],
);
// DNS (per-record rows)
export const dnsRecords = pgTable(
"dns_records",
{
id: uuid("id").primaryKey().defaultRandom(),
domainId: uuid("domain_id")
.notNull()
.references(() => domains.id, { onDelete: "cascade" }),
type: dnsRecordType("type").notNull(),
name: text("name").notNull(),
value: text("value").notNull(),
ttl: integer("ttl"),
priority: integer("priority"),
isCloudflare: boolean("is_cloudflare"),
resolver: dnsResolver("resolver").notNull(),
fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull(),
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
},
(t) => [
unique("u_dns_record").on(t.domainId, t.type, t.name, t.value),
index("i_dns_domain_type").on(t.domainId, t.type),
index("i_dns_type_value").on(t.type, t.value),
index("i_dns_expires").on(t.expiresAt),
],
);
// TLS certificates (latest)
export const certificates = pgTable(
"certificates",
{
id: uuid("id").primaryKey().defaultRandom(),
domainId: uuid("domain_id")
.notNull()
.references(() => domains.id, { onDelete: "cascade" }),
issuer: text("issuer").notNull(),
subject: text("subject").notNull(),
altNames: jsonb("alt_names").notNull().default(sql`'[]'::jsonb`),
validFrom: timestamp("valid_from", { withTimezone: true }).notNull(),
validTo: timestamp("valid_to", { withTimezone: true }).notNull(),
caProviderId: uuid("ca_provider_id").references(() => providers.id),
fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull(),
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
},
(t) => [
index("i_certs_domain").on(t.domainId),
index("i_certs_valid_to").on(t.validTo),
index("i_certs_expires").on(t.expiresAt),
// Ensure validTo >= validFrom
check("ck_cert_valid_window", sql`${t.validTo} >= ${t.validFrom}`),
// GIN on alt_names via raw migration
],
);
// HTTP headers (latest set)
export const httpHeaders = pgTable(
"http_headers",
{
id: uuid("id").primaryKey().defaultRandom(),
domainId: uuid("domain_id")
.notNull()
.references(() => domains.id, { onDelete: "cascade" }),
name: text("name").notNull(),
value: text("value").notNull(),
fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull(),
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
},
(t) => [
unique("u_http_header").on(t.domainId, t.name),
index("i_http_name").on(t.name),
],
);
// Hosting (latest)
export const hosting = pgTable(
"hosting",
{
domainId: uuid("domain_id")
.primaryKey()
.references(() => domains.id, { onDelete: "cascade" }),
hostingProviderId: uuid("hosting_provider_id").references(
() => providers.id,
),
emailProviderId: uuid("email_provider_id").references(() => providers.id),
dnsProviderId: uuid("dns_provider_id").references(() => providers.id),
geoCity: text("geo_city"),
geoRegion: text("geo_region"),
geoCountry: text("geo_country"),
geoCountryCode: text("geo_country_code"),
geoLat: doublePrecision("geo_lat"),
geoLon: doublePrecision("geo_lon"),
fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull(),
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
},
(t) => [
index("i_hosting_providers").on(
t.hostingProviderId,
t.emailProviderId,
t.dnsProviderId,
),
],
);
// SEO (latest)
export const seo = pgTable(
"seo",
{
domainId: uuid("domain_id")
.primaryKey()
.references(() => domains.id, { onDelete: "cascade" }),
sourceFinalUrl: text("source_final_url"),
sourceStatus: integer("source_status"),
metaOpenGraph: jsonb("meta_open_graph").notNull().default(sql`'{}'::jsonb`),
metaTwitter: jsonb("meta_twitter").notNull().default(sql`'{}'::jsonb`),
metaGeneral: jsonb("meta_general").notNull().default(sql`'{}'::jsonb`),
previewTitle: text("preview_title"),
previewDescription: text("preview_description"),
previewImageUrl: text("preview_image_url"),
previewImageUploadedUrl: text("preview_image_uploaded_url"),
canonicalUrl: text("canonical_url"),
robots: jsonb("robots").notNull().default(sql`'{}'::jsonb`),
robotsSitemaps: jsonb("robots_sitemaps")
.notNull()
.default(sql`'[]'::jsonb`),
errors: jsonb("errors").notNull().default(sql`'[]'::jsonb`),
fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull(),
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
},
(t) => [
index("i_seo_src_final_url").on(t.sourceFinalUrl),
index("i_seo_canonical").on(t.canonicalUrl),
],
);