Files
onebox/ts/classes/certmanager.ts
Juergen Kunz ad89f2cc1f feat: Implement repositories for authentication, certificates, metrics, and platform services
- Added AuthRepository for user and settings management with CRUD operations.
- Introduced CertificateRepository to handle domains, certificates, and requirements.
- Created MetricsRepository for managing metrics and logs.
- Developed PlatformRepository for platform services and resources management.
- Established RegistryRepository for registry and token operations.
- Implemented ServiceRepository for CRUD operations on services.
- Defined types and interfaces in types.ts for database interactions.
2025-11-25 23:27:27 +00:00

148 lines
4.2 KiB
TypeScript

/**
* SQLite-based Certificate Manager for SmartACME
*
* Implements ICertManager interface to store SSL certificates directly in SQLite database.
* Certificate PEM content is stored in the database, not on the filesystem.
*/
import * as plugins from '../plugins.ts';
import { logger } from '../logging.ts';
import { getErrorMessage } from '../utils/error.ts';
import { OneboxDatabase } from './database.ts';
export class SqliteCertManager implements plugins.smartacme.ICertManager {
private database: OneboxDatabase;
constructor(database: OneboxDatabase) {
this.database = database;
}
/**
* Initialize the certificate manager
*/
async init(): Promise<void> {
logger.info('Certificate manager initialized (database storage)');
}
/**
* Retrieve a certificate by domain name
*/
async retrieveCertificate(domainName: string): Promise<plugins.smartacme.Cert | null> {
try {
const dbCert = this.database.getSSLCertificate(domainName);
if (!dbCert || !dbCert.keyPem || !dbCert.fullchainPem) {
return null;
}
// Convert database format to SmartacmeCert format
const cert = new plugins.smartacme.Cert({
id: dbCert.id?.toString() || domainName,
domainName: dbCert.domain,
created: dbCert.createdAt,
privateKey: dbCert.keyPem,
publicKey: dbCert.fullchainPem, // Full chain as public key
csr: '', // CSR not stored separately
validUntil: dbCert.expiryDate,
});
return cert;
} catch (error) {
logger.warn(`Failed to retrieve certificate for ${domainName}: ${getErrorMessage(error)}`);
return null;
}
}
/**
* Store a certificate
*/
async storeCertificate(cert: plugins.smartacme.Cert): Promise<void> {
try {
const domain = cert.domainName;
// Extract certificate from full chain (first certificate in the chain)
const certPem = this.extractCertFromChain(cert.publicKey);
// Store/update in database with PEM content
const existing = this.database.getSSLCertificate(domain);
if (existing) {
this.database.updateSSLCertificate(domain, {
certPem: certPem,
keyPem: cert.privateKey,
fullchainPem: cert.publicKey,
expiryDate: cert.validUntil,
});
} else {
await this.database.createSSLCertificate({
domain,
certPem: certPem,
keyPem: cert.privateKey,
fullchainPem: cert.publicKey,
expiryDate: cert.validUntil,
issuer: "Let's Encrypt",
createdAt: cert.created,
updatedAt: Date.now(),
});
}
logger.success(`Certificate stored for ${domain}`);
} catch (error) {
logger.error(`Failed to store certificate for ${cert.domainName}: ${getErrorMessage(error)}`);
throw error;
}
}
/**
* Delete a certificate
*/
async deleteCertificate(domainName: string): Promise<void> {
try {
const dbCert = this.database.getSSLCertificate(domainName);
if (dbCert) {
// Delete from database
this.database.deleteSSLCertificate(domainName);
logger.info(`Certificate deleted for ${domainName}`);
}
} catch (error) {
logger.error(`Failed to delete certificate for ${domainName}: ${getErrorMessage(error)}`);
throw error;
}
}
/**
* Close the certificate manager
*/
async close(): Promise<void> {
// SQLite database is managed by OneboxDatabase, nothing to close here
logger.info('Certificate manager closed');
}
/**
* Wipe all certificates (for testing)
*/
async wipe(): Promise<void> {
try {
const certs = this.database.getAllSSLCertificates();
for (const cert of certs) {
await this.deleteCertificate(cert.domain);
}
logger.warn('All certificates wiped');
} catch (error) {
logger.error(`Failed to wipe certificates: ${getErrorMessage(error)}`);
throw error;
}
}
/**
* Extract the first certificate from a PEM chain
*/
private extractCertFromChain(fullChain: string): string {
const certMatch = fullChain.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/);
return certMatch ? certMatch[0] : fullChain;
}
}