import { BaseMigration } from './base-migration.ts'; import type { TQueryFunction } from '../types.ts'; export class Migration003DomainManagement extends BaseMigration { readonly version = 3; readonly description = 'Domain management tables'; up(query: TQueryFunction): void { query(` CREATE TABLE domains ( id INTEGER PRIMARY KEY AUTOINCREMENT, domain TEXT NOT NULL UNIQUE, dns_provider TEXT, cloudflare_zone_id TEXT, is_obsolete INTEGER NOT NULL DEFAULT 0, default_wildcard INTEGER NOT NULL DEFAULT 1, created_at REAL NOT NULL, updated_at REAL NOT NULL ) `); query(` CREATE TABLE certificates ( id INTEGER PRIMARY KEY AUTOINCREMENT, domain_id INTEGER NOT NULL, cert_domain TEXT NOT NULL, is_wildcard INTEGER NOT NULL DEFAULT 0, cert_path TEXT NOT NULL, key_path TEXT NOT NULL, full_chain_path TEXT NOT NULL, expiry_date REAL NOT NULL, issuer TEXT NOT NULL, is_valid INTEGER NOT NULL DEFAULT 1, created_at REAL NOT NULL, updated_at REAL NOT NULL, FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE ) `); query(` CREATE TABLE cert_requirements ( id INTEGER PRIMARY KEY AUTOINCREMENT, service_id INTEGER NOT NULL, domain_id INTEGER NOT NULL, subdomain TEXT NOT NULL, certificate_id INTEGER, status TEXT NOT NULL DEFAULT 'pending', created_at REAL NOT NULL, updated_at REAL NOT NULL, FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE, FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE, FOREIGN KEY (certificate_id) REFERENCES certificates(id) ON DELETE SET NULL ) `); // Migrate data from old ssl_certificates table interface OldSslCert { id?: number; domain?: string; cert_path?: string; key_path?: string; full_chain_path?: string; expiry_date?: number; issuer?: string; created_at?: number; updated_at?: number; [key: number]: unknown; } const existingCerts = query('SELECT * FROM ssl_certificates'); const now = Date.now(); const domainMap = new Map(); for (const cert of existingCerts) { const domain = String(cert.domain ?? (cert as Record)[1]); if (!domainMap.has(domain)) { query( 'INSERT INTO domains (domain, dns_provider, is_obsolete, default_wildcard, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)', [domain, null, 0, 1, now, now], ); const result = query<{ id?: number; [key: number]: unknown }>( 'SELECT last_insert_rowid() as id', ); const domainId = result[0].id ?? (result[0] as Record)[0]; domainMap.set(domain, Number(domainId)); } } for (const cert of existingCerts) { const domain = String(cert.domain ?? (cert as Record)[1]); const domainId = domainMap.get(domain); query( `INSERT INTO certificates ( domain_id, cert_domain, is_wildcard, cert_path, key_path, full_chain_path, expiry_date, issuer, is_valid, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ domainId, domain, 0, String(cert.cert_path ?? (cert as Record)[2]), String(cert.key_path ?? (cert as Record)[3]), String(cert.full_chain_path ?? (cert as Record)[4]), Number(cert.expiry_date ?? (cert as Record)[5]), String(cert.issuer ?? (cert as Record)[6]), 1, Number(cert.created_at ?? (cert as Record)[7]), Number(cert.updated_at ?? (cert as Record)[8]), ], ); } query('DROP TABLE ssl_certificates'); query('CREATE INDEX IF NOT EXISTS idx_domains_cloudflare_zone ON domains(cloudflare_zone_id)'); query('CREATE INDEX IF NOT EXISTS idx_certificates_domain ON certificates(domain_id)'); query('CREATE INDEX IF NOT EXISTS idx_certificates_expiry ON certificates(expiry_date)'); query( 'CREATE INDEX IF NOT EXISTS idx_cert_requirements_service ON cert_requirements(service_id)', ); query( 'CREATE INDEX IF NOT EXISTS idx_cert_requirements_domain ON cert_requirements(domain_id)', ); } }