BREAKING CHANGE(db): replace StorageManager and CacheDb with a unified smartdata-backed database layer
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import type { OpsServer } from '../classes.opsserver.js';
|
||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||
import { AcmeCertDoc, ProxyCertDoc } from '../../db/index.js';
|
||||
|
||||
export class CertificateHandler {
|
||||
constructor(private opsServerRef: OpsServer) {
|
||||
@@ -187,30 +188,28 @@ export class CertificateHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Check persisted cert data from StorageManager
|
||||
// Check persisted cert data from smartdata document classes
|
||||
if (status === 'unknown') {
|
||||
const cleanDomain = domain.replace(/^\*\.?/, '');
|
||||
let certData = await dcRouter.storageManager.getJSON(`/certs/${cleanDomain}`);
|
||||
if (!certData) {
|
||||
// Also check certStore path (proxy-certs)
|
||||
certData = await dcRouter.storageManager.getJSON(`/proxy-certs/${domain}`);
|
||||
}
|
||||
if (certData?.validUntil) {
|
||||
expiryDate = new Date(certData.validUntil).toISOString();
|
||||
if (certData.created) {
|
||||
issuedAt = new Date(certData.created).toISOString();
|
||||
const acmeDoc = await AcmeCertDoc.findByDomain(cleanDomain);
|
||||
const proxyDoc = !acmeDoc ? await ProxyCertDoc.findByDomain(domain) : null;
|
||||
|
||||
if (acmeDoc?.validUntil) {
|
||||
expiryDate = new Date(acmeDoc.validUntil).toISOString();
|
||||
if (acmeDoc.created) {
|
||||
issuedAt = new Date(acmeDoc.created).toISOString();
|
||||
}
|
||||
issuer = 'smartacme-dns-01';
|
||||
} else if (certData?.publicKey) {
|
||||
} else if (proxyDoc?.publicKey) {
|
||||
// certStore has the cert — parse PEM for expiry
|
||||
try {
|
||||
const x509 = new plugins.crypto.X509Certificate(certData.publicKey);
|
||||
const x509 = new plugins.crypto.X509Certificate(proxyDoc.publicKey);
|
||||
expiryDate = new Date(x509.validTo).toISOString();
|
||||
issuedAt = new Date(x509.validFrom).toISOString();
|
||||
} catch { /* PEM parsing failed */ }
|
||||
status = 'valid';
|
||||
issuer = 'cert-store';
|
||||
} else if (certData) {
|
||||
} else if (acmeDoc || proxyDoc) {
|
||||
status = 'valid';
|
||||
issuer = 'cert-store';
|
||||
}
|
||||
@@ -366,18 +365,17 @@ export class CertificateHandler {
|
||||
const dcRouter = this.opsServerRef.dcRouterRef;
|
||||
const cleanDomain = domain.replace(/^\*\.?/, '');
|
||||
|
||||
// Delete from all known storage paths
|
||||
const paths = [
|
||||
`/proxy-certs/${domain}`,
|
||||
`/proxy-certs/${cleanDomain}`,
|
||||
`/certs/${cleanDomain}`,
|
||||
];
|
||||
// Delete from smartdata document classes
|
||||
const acmeDoc = await AcmeCertDoc.findByDomain(cleanDomain);
|
||||
if (acmeDoc) {
|
||||
await acmeDoc.delete();
|
||||
}
|
||||
|
||||
for (const path of paths) {
|
||||
try {
|
||||
await dcRouter.storageManager.delete(path);
|
||||
} catch {
|
||||
// Path may not exist — ignore
|
||||
// Try both original domain and clean domain for proxy certs
|
||||
for (const d of [domain, cleanDomain]) {
|
||||
const proxyDoc = await ProxyCertDoc.findByDomain(d);
|
||||
if (proxyDoc) {
|
||||
await proxyDoc.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,43 +406,41 @@ export class CertificateHandler {
|
||||
};
|
||||
message?: string;
|
||||
}> {
|
||||
const dcRouter = this.opsServerRef.dcRouterRef;
|
||||
const cleanDomain = domain.replace(/^\*\.?/, '');
|
||||
|
||||
// Try SmartAcme /certs/ path first (has full ICert fields)
|
||||
let certData = await dcRouter.storageManager.getJSON(`/certs/${cleanDomain}`);
|
||||
if (certData && certData.publicKey && certData.privateKey) {
|
||||
// Try AcmeCertDoc first (has full ICert fields)
|
||||
const acmeDoc = await AcmeCertDoc.findByDomain(cleanDomain);
|
||||
if (acmeDoc && acmeDoc.publicKey && acmeDoc.privateKey) {
|
||||
return {
|
||||
success: true,
|
||||
cert: {
|
||||
id: certData.id || plugins.crypto.randomUUID(),
|
||||
domainName: certData.domainName || domain,
|
||||
created: certData.created || Date.now(),
|
||||
validUntil: certData.validUntil || 0,
|
||||
privateKey: certData.privateKey,
|
||||
publicKey: certData.publicKey,
|
||||
csr: certData.csr || '',
|
||||
id: acmeDoc.id || plugins.crypto.randomUUID(),
|
||||
domainName: acmeDoc.domainName || domain,
|
||||
created: acmeDoc.created || Date.now(),
|
||||
validUntil: acmeDoc.validUntil || 0,
|
||||
privateKey: acmeDoc.privateKey,
|
||||
publicKey: acmeDoc.publicKey,
|
||||
csr: acmeDoc.csr || '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback: try /proxy-certs/ with original domain
|
||||
certData = await dcRouter.storageManager.getJSON(`/proxy-certs/${domain}`);
|
||||
if (!certData || !certData.publicKey) {
|
||||
// Try with clean domain
|
||||
certData = await dcRouter.storageManager.getJSON(`/proxy-certs/${cleanDomain}`);
|
||||
// Fallback: try ProxyCertDoc with original domain, then clean domain
|
||||
let proxyDoc = await ProxyCertDoc.findByDomain(domain);
|
||||
if (!proxyDoc || !proxyDoc.publicKey) {
|
||||
proxyDoc = await ProxyCertDoc.findByDomain(cleanDomain);
|
||||
}
|
||||
|
||||
if (certData && certData.publicKey && certData.privateKey) {
|
||||
if (proxyDoc && proxyDoc.publicKey && proxyDoc.privateKey) {
|
||||
return {
|
||||
success: true,
|
||||
cert: {
|
||||
id: plugins.crypto.randomUUID(),
|
||||
domainName: domain,
|
||||
created: certData.validFrom || Date.now(),
|
||||
validUntil: certData.validUntil || 0,
|
||||
privateKey: certData.privateKey,
|
||||
publicKey: certData.publicKey,
|
||||
created: proxyDoc.validFrom || Date.now(),
|
||||
validUntil: proxyDoc.validUntil || 0,
|
||||
privateKey: proxyDoc.privateKey,
|
||||
publicKey: proxyDoc.publicKey,
|
||||
csr: '',
|
||||
},
|
||||
};
|
||||
@@ -476,26 +472,32 @@ export class CertificateHandler {
|
||||
const dcRouter = this.opsServerRef.dcRouterRef;
|
||||
const cleanDomain = cert.domainName.replace(/^\*\.?/, '');
|
||||
|
||||
// Save to /certs/ (SmartAcme-compatible path)
|
||||
await dcRouter.storageManager.setJSON(`/certs/${cleanDomain}`, {
|
||||
id: cert.id,
|
||||
domainName: cert.domainName,
|
||||
created: cert.created,
|
||||
validUntil: cert.validUntil,
|
||||
privateKey: cert.privateKey,
|
||||
publicKey: cert.publicKey,
|
||||
csr: cert.csr || '',
|
||||
});
|
||||
// Save to AcmeCertDoc (SmartAcme-compatible)
|
||||
let acmeDoc = await AcmeCertDoc.findByDomain(cleanDomain);
|
||||
if (!acmeDoc) {
|
||||
acmeDoc = new AcmeCertDoc();
|
||||
acmeDoc.domainName = cleanDomain;
|
||||
}
|
||||
acmeDoc.id = cert.id;
|
||||
acmeDoc.created = cert.created;
|
||||
acmeDoc.validUntil = cert.validUntil;
|
||||
acmeDoc.privateKey = cert.privateKey;
|
||||
acmeDoc.publicKey = cert.publicKey;
|
||||
acmeDoc.csr = cert.csr || '';
|
||||
await acmeDoc.save();
|
||||
|
||||
// Also save to /proxy-certs/ (proxy-cert format)
|
||||
await dcRouter.storageManager.setJSON(`/proxy-certs/${cert.domainName}`, {
|
||||
domain: cert.domainName,
|
||||
publicKey: cert.publicKey,
|
||||
privateKey: cert.privateKey,
|
||||
ca: undefined,
|
||||
validUntil: cert.validUntil,
|
||||
validFrom: cert.created,
|
||||
});
|
||||
// Also save to ProxyCertDoc (proxy-cert format)
|
||||
let proxyDoc = await ProxyCertDoc.findByDomain(cert.domainName);
|
||||
if (!proxyDoc) {
|
||||
proxyDoc = new ProxyCertDoc();
|
||||
proxyDoc.domain = cert.domainName;
|
||||
}
|
||||
proxyDoc.publicKey = cert.publicKey;
|
||||
proxyDoc.privateKey = cert.privateKey;
|
||||
proxyDoc.ca = '';
|
||||
proxyDoc.validUntil = cert.validUntil;
|
||||
proxyDoc.validFrom = cert.created;
|
||||
await proxyDoc.save();
|
||||
|
||||
// Update in-memory status map
|
||||
dcRouter.certificateStatusMap.set(cert.domainName, {
|
||||
|
||||
@@ -33,11 +33,9 @@ export class ConfigHandler {
|
||||
const resolvedPaths = dcRouter.resolvedPaths;
|
||||
|
||||
// --- System ---
|
||||
const storageBackend: 'filesystem' | 'custom' | 'memory' = opts.storage?.readFunction
|
||||
const storageBackend: 'filesystem' | 'custom' | 'memory' = opts.dbConfig?.mongoDbUrl
|
||||
? 'custom'
|
||||
: opts.storage?.fsPath
|
||||
? 'filesystem'
|
||||
: 'memory';
|
||||
: 'filesystem';
|
||||
|
||||
// Resolve proxy IPs: fall back to SmartProxy's runtime proxyIPs if not in opts
|
||||
let proxyIps = opts.proxyIps || [];
|
||||
@@ -55,7 +53,7 @@ export class ConfigHandler {
|
||||
proxyIps,
|
||||
uptime: Math.floor(process.uptime()),
|
||||
storageBackend,
|
||||
storagePath: opts.storage?.fsPath || null,
|
||||
storagePath: opts.dbConfig?.storagePath || resolvedPaths.defaultTsmDbPath,
|
||||
};
|
||||
|
||||
// --- SmartProxy ---
|
||||
@@ -151,15 +149,15 @@ export class ConfigHandler {
|
||||
keyPath: opts.tls?.keyPath || null,
|
||||
};
|
||||
|
||||
// --- Cache ---
|
||||
const cacheConfig = opts.cacheConfig;
|
||||
// --- Database ---
|
||||
const dbConfig = opts.dbConfig;
|
||||
const cache: interfaces.requests.IConfigData['cache'] = {
|
||||
enabled: cacheConfig?.enabled !== false,
|
||||
storagePath: cacheConfig?.storagePath || resolvedPaths.defaultTsmDbPath,
|
||||
dbName: cacheConfig?.dbName || 'dcrouter',
|
||||
defaultTTLDays: cacheConfig?.defaultTTLDays || 30,
|
||||
cleanupIntervalHours: cacheConfig?.cleanupIntervalHours || 1,
|
||||
ttlConfig: cacheConfig?.ttlConfig ? { ...cacheConfig.ttlConfig } as Record<string, number> : {},
|
||||
enabled: dbConfig?.enabled !== false,
|
||||
storagePath: dbConfig?.storagePath || resolvedPaths.defaultTsmDbPath,
|
||||
dbName: dbConfig?.dbName || 'dcrouter',
|
||||
defaultTTLDays: 30,
|
||||
cleanupIntervalHours: dbConfig?.cleanupIntervalHours || 1,
|
||||
ttlConfig: {},
|
||||
};
|
||||
|
||||
// --- RADIUS ---
|
||||
@@ -185,7 +183,8 @@ export class ConfigHandler {
|
||||
tlsMode = 'custom';
|
||||
} else if (riCfg?.hubDomain) {
|
||||
try {
|
||||
const stored = await dcRouter.storageManager.getJSON(`/proxy-certs/${riCfg.hubDomain}`);
|
||||
const { ProxyCertDoc } = await import('../../db/index.js');
|
||||
const stored = await ProxyCertDoc.findByDomain(riCfg.hubDomain);
|
||||
if (stored?.publicKey && stored?.privateKey) {
|
||||
tlsMode = 'acme';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user