Files
edgeguard-native/internal/database/migrations/0003_http_frontend.sql
Debian b307a7b1f7 feat(db): Phase 1 — DB-Schema, goose-Migrations, GORM-Models
Initialer Schema-Set (8 Migrationen, 13 Tabellen) für EdgeGuard v1:
users + audit_log + system_settings, ha_nodes, backends/domains/
routing_rules/tls_certs, forward_proxy_acls, wireguard_peers,
firewall_rules, dns_zones/dns_records, licenses. Migrations liegen
in internal/database/migrations/ (analog mail-gateway) und werden
per //go:embed ins Binary gepackt — keine separate SQL-Dateien im
.deb. ValidateMigrations + Test schützen vor Duplicate-Versionen
(mail-gateway 2026-05-08-Vorfall). GORM-Models für alle Tabellen,
sensible Felder (password_hash, private_key_enc) sind json:"-".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 23:44:44 +02:00

89 lines
3.6 KiB
SQL

-- +goose Up
-- +goose StatementBegin
-- Upstream backends (target servers behind the reverse-proxy).
CREATE TABLE IF NOT EXISTS backends (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
scheme TEXT NOT NULL DEFAULT 'http',
address TEXT NOT NULL,
port INTEGER NOT NULL,
health_check_path TEXT,
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT backends_name_unique UNIQUE (name),
CONSTRAINT backends_scheme_check CHECK (scheme IN ('http', 'https')),
CONSTRAINT backends_port_check CHECK (port > 0 AND port < 65536)
);
CREATE INDEX IF NOT EXISTS idx_backends_active ON backends (active) WHERE active;
-- Public-facing domains. primary_backend_id is the default upstream
-- when no path-rule matches.
CREATE TABLE IF NOT EXISTS domains (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
active BOOLEAN NOT NULL DEFAULT TRUE,
primary_backend_id BIGINT REFERENCES backends(id) ON DELETE SET NULL,
http_to_https BOOLEAN NOT NULL DEFAULT TRUE,
hsts_enabled BOOLEAN NOT NULL DEFAULT FALSE,
notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT domains_name_unique UNIQUE (name)
);
CREATE INDEX IF NOT EXISTS idx_domains_active ON domains (active) WHERE active;
-- Path-based routing rules. Higher priority wins; ties broken by id.
CREATE TABLE IF NOT EXISTS routing_rules (
id BIGSERIAL PRIMARY KEY,
domain_id BIGINT NOT NULL REFERENCES domains(id) ON DELETE CASCADE,
path_prefix TEXT NOT NULL DEFAULT '/',
backend_id BIGINT NOT NULL REFERENCES backends(id) ON DELETE RESTRICT,
priority INTEGER NOT NULL DEFAULT 100,
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_routing_rules_domain ON routing_rules (domain_id);
CREATE INDEX IF NOT EXISTS idx_routing_rules_priority ON routing_rules (domain_id, priority DESC);
-- ACME-managed TLS certificates. status mirrors certbot's view:
-- pending — issue requested, not yet completed
-- active — issued, currently deployed
-- renewing — renewal in progress
-- expired — past not_after, awaiting cleanup
-- error — last attempt failed (see audit_log for detail)
CREATE TABLE IF NOT EXISTS tls_certs (
id BIGSERIAL PRIMARY KEY,
domain TEXT NOT NULL,
issuer TEXT NOT NULL DEFAULT 'letsencrypt',
status TEXT NOT NULL DEFAULT 'pending',
cert_path TEXT,
key_path TEXT,
not_before TIMESTAMPTZ,
not_after TIMESTAMPTZ,
last_renewed_at TIMESTAMPTZ,
last_error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT tls_certs_domain_unique UNIQUE (domain),
CONSTRAINT tls_certs_status_check CHECK (status IN ('pending', 'active', 'renewing', 'expired', 'error'))
);
CREATE INDEX IF NOT EXISTS idx_tls_certs_not_after ON tls_certs (not_after);
CREATE INDEX IF NOT EXISTS idx_tls_certs_status ON tls_certs (status);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS tls_certs;
DROP TABLE IF EXISTS routing_rules;
DROP TABLE IF EXISTS domains;
DROP TABLE IF EXISTS backends;
-- +goose StatementEnd