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.
This commit is contained in:
2025-11-25 23:27:27 +00:00
parent 9d58971983
commit ad89f2cc1f
18 changed files with 2249 additions and 1966 deletions

View File

@@ -1,8 +1,8 @@
/**
* SQLite-based Certificate Manager for SmartACME
*
* Implements ICertManager interface to store SSL certificates in SQLite database
* and write PEM files to filesystem for use by the reverse proxy.
* 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';
@@ -12,25 +12,16 @@ import { OneboxDatabase } from './database.ts';
export class SqliteCertManager implements plugins.smartacme.ICertManager {
private database: OneboxDatabase;
private certBasePath: string;
constructor(database: OneboxDatabase, certBasePath = './.nogit/ssl/live') {
constructor(database: OneboxDatabase) {
this.database = database;
this.certBasePath = certBasePath;
}
/**
* Initialize the certificate manager
*/
async init(): Promise<void> {
try {
// Ensure certificate directory exists
await Deno.mkdir(this.certBasePath, { recursive: true });
logger.info(`Certificate manager initialized (path: ${this.certBasePath})`);
} catch (error) {
logger.error(`Failed to initialize certificate manager: ${getErrorMessage(error)}`);
throw error;
}
logger.info('Certificate manager initialized (database storage)');
}
/**
@@ -40,7 +31,7 @@ export class SqliteCertManager implements plugins.smartacme.ICertManager {
try {
const dbCert = this.database.getSSLCertificate(domainName);
if (!dbCert) {
if (!dbCert || !dbCert.keyPem || !dbCert.fullchainPem) {
return null;
}
@@ -49,8 +40,8 @@ export class SqliteCertManager implements plugins.smartacme.ICertManager {
id: dbCert.id?.toString() || domainName,
domainName: dbCert.domain,
created: dbCert.createdAt,
privateKey: await this.readPemFile(dbCert.keyPath),
publicKey: await this.readPemFile(dbCert.fullChainPath), // Full chain as public key
privateKey: dbCert.keyPem,
publicKey: dbCert.fullchainPem, // Full chain as public key
csr: '', // CSR not stored separately
validUntil: dbCert.expiryDate,
});
@@ -68,42 +59,28 @@ export class SqliteCertManager implements plugins.smartacme.ICertManager {
async storeCertificate(cert: plugins.smartacme.Cert): Promise<void> {
try {
const domain = cert.domainName;
const domainPath = `${this.certBasePath}/${domain}`;
// Create domain-specific directory
await Deno.mkdir(domainPath, { recursive: true });
// Write PEM files
const keyPath = `${domainPath}/privkey.pem`;
const certPath = `${domainPath}/cert.pem`;
const fullChainPath = `${domainPath}/fullchain.pem`;
await Deno.writeTextFile(keyPath, cert.privateKey);
await Deno.writeTextFile(fullChainPath, cert.publicKey);
// Extract certificate from full chain (first certificate in the chain)
const certOnly = this.extractCertFromChain(cert.publicKey);
await Deno.writeTextFile(certPath, certOnly);
const certPem = this.extractCertFromChain(cert.publicKey);
// Store/update in database
// Store/update in database with PEM content
const existing = this.database.getSSLCertificate(domain);
if (existing) {
this.database.updateSSLCertificate(domain, {
certPath,
keyPath,
fullChainPath,
certPem: certPem,
keyPem: cert.privateKey,
fullchainPem: cert.publicKey,
expiryDate: cert.validUntil,
updatedAt: Date.now(),
});
} else {
await this.database.createSSLCertificate({
domain,
certPath,
keyPath,
fullChainPath,
certPem: certPem,
keyPem: cert.privateKey,
fullchainPem: cert.publicKey,
expiryDate: cert.validUntil,
issuer: 'Let\'s Encrypt',
issuer: "Let's Encrypt",
createdAt: cert.created,
updatedAt: Date.now(),
});
@@ -124,17 +101,8 @@ export class SqliteCertManager implements plugins.smartacme.ICertManager {
const dbCert = this.database.getSSLCertificate(domainName);
if (dbCert) {
// Delete PEM files
const domainPath = `${this.certBasePath}/${domainName}`;
try {
await Deno.remove(domainPath, { recursive: true });
} catch (error) {
logger.warn(`Failed to delete PEM files for ${domainName}: ${getErrorMessage(error)}`);
}
// Delete from database
this.database.deleteSSLCertificate(domainName);
logger.info(`Certificate deleted for ${domainName}`);
}
} catch (error) {
@@ -169,17 +137,6 @@ export class SqliteCertManager implements plugins.smartacme.ICertManager {
}
}
/**
* Read PEM file from filesystem
*/
private async readPemFile(path: string): Promise<string> {
try {
return await Deno.readTextFile(path);
} catch (error) {
throw new Error(`Failed to read PEM file ${path}: ${getErrorMessage(error)}`);
}
}
/**
* Extract the first certificate from a PEM chain
*/