/** * Certificate Repository * Handles CRUD operations for domains, certificates, cert_requirements, and legacy ssl_certificates */ import { BaseRepository } from '../base.repository.ts'; import type { TBindValue } from '../types.ts'; import type { IDomain, ICertificate, ICertRequirement, ISslCertificate } from '../../types.ts'; export class CertificateRepository extends BaseRepository { // ============ Domains ============ createDomain(domain: Omit): IDomain { this.query( `INSERT INTO domains (domain, dns_provider, cloudflare_zone_id, is_obsolete, default_wildcard, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)`, [ domain.domain, domain.dnsProvider, domain.cloudflareZoneId, domain.isObsolete ? 1 : 0, domain.defaultWildcard ? 1 : 0, domain.createdAt, domain.updatedAt, ] ); return this.getDomainByName(domain.domain)!; } getDomainByName(domain: string): IDomain | null { const rows = this.query('SELECT * FROM domains WHERE domain = ?', [domain]); return rows.length > 0 ? this.rowToDomain(rows[0]) : null; } getDomainById(id: number): IDomain | null { const rows = this.query('SELECT * FROM domains WHERE id = ?', [id]); return rows.length > 0 ? this.rowToDomain(rows[0]) : null; } getAllDomains(): IDomain[] { const rows = this.query('SELECT * FROM domains ORDER BY domain ASC'); return rows.map((row) => this.rowToDomain(row)); } getDomainsByProvider(provider: 'cloudflare' | 'manual'): IDomain[] { const rows = this.query('SELECT * FROM domains WHERE dns_provider = ? ORDER BY domain ASC', [provider]); return rows.map((row) => this.rowToDomain(row)); } updateDomain(id: number, updates: Partial): void { const fields: string[] = []; const values: TBindValue[] = []; if (updates.domain !== undefined) { fields.push('domain = ?'); values.push(updates.domain); } if (updates.dnsProvider !== undefined) { fields.push('dns_provider = ?'); values.push(updates.dnsProvider); } if (updates.cloudflareZoneId !== undefined) { fields.push('cloudflare_zone_id = ?'); values.push(updates.cloudflareZoneId); } if (updates.isObsolete !== undefined) { fields.push('is_obsolete = ?'); values.push(updates.isObsolete ? 1 : 0); } if (updates.defaultWildcard !== undefined) { fields.push('default_wildcard = ?'); values.push(updates.defaultWildcard ? 1 : 0); } fields.push('updated_at = ?'); values.push(Date.now()); values.push(id); this.query(`UPDATE domains SET ${fields.join(', ')} WHERE id = ?`, values); } deleteDomain(id: number): void { this.query('DELETE FROM domains WHERE id = ?', [id]); } private rowToDomain(row: any): IDomain { return { id: Number(row.id || row[0]), domain: String(row.domain || row[1]), dnsProvider: (row.dns_provider || row[2]) as IDomain['dnsProvider'], cloudflareZoneId: row.cloudflare_zone_id || row[3] || undefined, isObsolete: Boolean(row.is_obsolete || row[4]), defaultWildcard: Boolean(row.default_wildcard || row[5]), createdAt: Number(row.created_at || row[6]), updatedAt: Number(row.updated_at || row[7]), }; } // ============ Certificates ============ createCertificate(cert: Omit): ICertificate { this.query( `INSERT INTO certificates (domain_id, cert_domain, is_wildcard, cert_pem, key_pem, fullchain_pem, expiry_date, issuer, is_valid, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ cert.domainId, cert.certDomain, cert.isWildcard ? 1 : 0, cert.certPem, cert.keyPem, cert.fullchainPem, cert.expiryDate, cert.issuer, cert.isValid ? 1 : 0, cert.createdAt, cert.updatedAt, ] ); const rows = this.query('SELECT * FROM certificates WHERE id = last_insert_rowid()'); return this.rowToCertificate(rows[0]); } getCertificateById(id: number): ICertificate | null { const rows = this.query('SELECT * FROM certificates WHERE id = ?', [id]); return rows.length > 0 ? this.rowToCertificate(rows[0]) : null; } getCertificatesByDomain(domainId: number): ICertificate[] { const rows = this.query('SELECT * FROM certificates WHERE domain_id = ? ORDER BY expiry_date DESC', [domainId]); return rows.map((row) => this.rowToCertificate(row)); } getAllCertificates(): ICertificate[] { const rows = this.query('SELECT * FROM certificates ORDER BY expiry_date DESC'); return rows.map((row) => this.rowToCertificate(row)); } updateCertificate(id: number, updates: Partial): void { const fields: string[] = []; const values: TBindValue[] = []; if (updates.certDomain !== undefined) { fields.push('cert_domain = ?'); values.push(updates.certDomain); } if (updates.isWildcard !== undefined) { fields.push('is_wildcard = ?'); values.push(updates.isWildcard ? 1 : 0); } if (updates.certPem !== undefined) { fields.push('cert_pem = ?'); values.push(updates.certPem); } if (updates.keyPem !== undefined) { fields.push('key_pem = ?'); values.push(updates.keyPem); } if (updates.fullchainPem !== undefined) { fields.push('fullchain_pem = ?'); values.push(updates.fullchainPem); } if (updates.expiryDate !== undefined) { fields.push('expiry_date = ?'); values.push(updates.expiryDate); } if (updates.issuer !== undefined) { fields.push('issuer = ?'); values.push(updates.issuer); } if (updates.isValid !== undefined) { fields.push('is_valid = ?'); values.push(updates.isValid ? 1 : 0); } fields.push('updated_at = ?'); values.push(Date.now()); values.push(id); this.query(`UPDATE certificates SET ${fields.join(', ')} WHERE id = ?`, values); } deleteCertificate(id: number): void { this.query('DELETE FROM certificates WHERE id = ?', [id]); } private rowToCertificate(row: any): ICertificate { return { id: Number(row.id || row[0]), domainId: Number(row.domain_id || row[1]), certDomain: String(row.cert_domain || row[2]), isWildcard: Boolean(row.is_wildcard || row[3]), certPem: String(row.cert_pem || row[4] || ''), keyPem: String(row.key_pem || row[5] || ''), fullchainPem: String(row.fullchain_pem || row[6] || ''), expiryDate: Number(row.expiry_date || row[7]), issuer: String(row.issuer || row[8]), isValid: Boolean(row.is_valid || row[9]), createdAt: Number(row.created_at || row[10]), updatedAt: Number(row.updated_at || row[11]), }; } // ============ Certificate Requirements ============ createCertRequirement(req: Omit): ICertRequirement { this.query( `INSERT INTO cert_requirements (service_id, domain_id, subdomain, certificate_id, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)`, [ req.serviceId, req.domainId, req.subdomain, req.certificateId, req.status, req.createdAt, req.updatedAt, ] ); const rows = this.query('SELECT * FROM cert_requirements WHERE id = last_insert_rowid()'); return this.rowToCertRequirement(rows[0]); } getCertRequirementById(id: number): ICertRequirement | null { const rows = this.query('SELECT * FROM cert_requirements WHERE id = ?', [id]); return rows.length > 0 ? this.rowToCertRequirement(rows[0]) : null; } getCertRequirementsByService(serviceId: number): ICertRequirement[] { const rows = this.query('SELECT * FROM cert_requirements WHERE service_id = ?', [serviceId]); return rows.map((row) => this.rowToCertRequirement(row)); } getCertRequirementsByDomain(domainId: number): ICertRequirement[] { const rows = this.query('SELECT * FROM cert_requirements WHERE domain_id = ?', [domainId]); return rows.map((row) => this.rowToCertRequirement(row)); } getAllCertRequirements(): ICertRequirement[] { const rows = this.query('SELECT * FROM cert_requirements ORDER BY created_at DESC'); return rows.map((row) => this.rowToCertRequirement(row)); } updateCertRequirement(id: number, updates: Partial): void { const fields: string[] = []; const values: TBindValue[] = []; if (updates.subdomain !== undefined) { fields.push('subdomain = ?'); values.push(updates.subdomain); } if (updates.certificateId !== undefined) { fields.push('certificate_id = ?'); values.push(updates.certificateId); } if (updates.status !== undefined) { fields.push('status = ?'); values.push(updates.status); } fields.push('updated_at = ?'); values.push(Date.now()); values.push(id); this.query(`UPDATE cert_requirements SET ${fields.join(', ')} WHERE id = ?`, values); } deleteCertRequirement(id: number): void { this.query('DELETE FROM cert_requirements WHERE id = ?', [id]); } private rowToCertRequirement(row: any): ICertRequirement { return { id: Number(row.id || row[0]), serviceId: Number(row.service_id || row[1]), domainId: Number(row.domain_id || row[2]), subdomain: String(row.subdomain || row[3]), certificateId: row.certificate_id || row[4] || undefined, status: String(row.status || row[5]) as ICertRequirement['status'], createdAt: Number(row.created_at || row[6]), updatedAt: Number(row.updated_at || row[7]), }; } // ============ SSL Certificates (Legacy API) ============ async createSSLCertificate(cert: Omit): Promise { // First, ensure domain exists in domains table let domainRecord = this.getDomainByName(cert.domain); if (!domainRecord) { const now = Date.now(); domainRecord = this.createDomain({ domain: cert.domain, dnsProvider: null, isObsolete: false, defaultWildcard: true, createdAt: now, updatedAt: now, }); } const now = Date.now(); this.query( `INSERT INTO certificates (domain_id, cert_domain, is_wildcard, cert_pem, key_pem, fullchain_pem, expiry_date, issuer, is_valid, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ domainRecord.id, cert.domain, 0, cert.certPem, cert.keyPem, cert.fullchainPem, cert.expiryDate, cert.issuer, 1, now, now, ] ); return this.getSSLCertificate(cert.domain)!; } getSSLCertificate(domain: string): ISslCertificate | null { const rows = this.query('SELECT * FROM certificates WHERE cert_domain = ?', [domain]); return rows.length > 0 ? this.rowToSSLCert(rows[0]) : null; } getAllSSLCertificates(): ISslCertificate[] { const rows = this.query('SELECT * FROM certificates ORDER BY expiry_date ASC'); return rows.map((row) => this.rowToSSLCert(row)); } updateSSLCertificate(domain: string, updates: Partial): void { const fields: string[] = []; const values: TBindValue[] = []; if (updates.certPem) { fields.push('cert_pem = ?'); values.push(updates.certPem); } if (updates.keyPem) { fields.push('key_pem = ?'); values.push(updates.keyPem); } if (updates.fullchainPem) { fields.push('fullchain_pem = ?'); values.push(updates.fullchainPem); } if (updates.expiryDate) { fields.push('expiry_date = ?'); values.push(updates.expiryDate); } fields.push('updated_at = ?'); values.push(Date.now()); values.push(domain); this.query(`UPDATE certificates SET ${fields.join(', ')} WHERE cert_domain = ?`, values); } deleteSSLCertificate(domain: string): void { this.query('DELETE FROM certificates WHERE cert_domain = ?', [domain]); } private rowToSSLCert(row: any): ISslCertificate { return { id: Number(row.id || row[0]), domain: String(row.cert_domain || row[2] || ''), certPem: String(row.cert_pem || row[4] || ''), keyPem: String(row.key_pem || row[5] || ''), fullchainPem: String(row.fullchain_pem || row[6] || ''), expiryDate: Number(row.expiry_date || row[7]), issuer: String(row.issuer || row[8]), createdAt: Number(row.created_at || row[10]), updatedAt: Number(row.updated_at || row[11]), }; } }