fix(dcrouter): re-trigger auto certificate provisioning after SmartAcme becomes ready

This commit is contained in:
2026-03-27 19:49:38 +00:00
parent d2b108317e
commit 36a3060cce
4 changed files with 22 additions and 45 deletions

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/dcrouter',
version: '11.12.2',
version: '11.12.3',
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
}

View File

@@ -389,34 +389,21 @@ export class DcRouter {
this.smartAcmeReady = true;
logger.log('info', 'SmartAcme DNS-01 provider is now ready');
// Re-provision any certificates that failed during the startup window
// (before SmartAcme was ready — the certProvisionFunction returned 'http01'
// which fails because Rust ACME is disabled when certProvisionFunction is set)
// Re-trigger certificate provisioning for all auto-cert routes.
// During startup, certProvisionFunction returned 'http01' (SmartAcme not ready),
// but Rust ACME is disabled when certProvisionFunction is set — so all domains
// failed silently (SmartProxy doesn't emit certificate-failed for this path).
// Calling updateRoutes() re-triggers provisionCertificatesViaCallback internally,
// which calls certProvisionFunction again — now with smartAcmeReady === true.
if (this.smartProxy) {
const failedDomains = [...this.certificateStatusMap.entries()]
.filter(([_, status]) => status.status === 'failed')
.map(([domain]) => domain);
if (failedDomains.length > 0) {
logger.log('info', `Re-provisioning ${failedDomains.length} certificates that failed before SmartAcme was ready`);
// Clear backoff and status for failed domains — these failures were from the startup race
for (const domain of failedDomains) {
if (this.certProvisionScheduler) {
await this.certProvisionScheduler.clearBackoff(domain);
}
this.certificateStatusMap.delete(domain);
}
// Re-trigger provisioning for all auto-cert routes
const routes = this.smartProxy.routeManager.getRoutes();
for (const route of routes) {
const tls = (route as any).action?.tls;
if (tls && tls.certificate === 'auto' && route.name) {
this.smartProxy.provisionCertificate(route.name).catch((err: any) => {
logger.log('warn', `Re-provision for route '${route.name}' failed: ${err?.message || err}`);
});
}
}
if (this.certProvisionScheduler) {
this.certProvisionScheduler.clear();
}
const currentRoutes = this.smartProxy.routeManager.getRoutes();
logger.log('info', `Re-triggering certificate provisioning for ${currentRoutes.length} routes`);
this.smartProxy.updateRoutes(currentRoutes).catch((err: any) => {
logger.log('warn', `Failed to re-trigger cert provisioning: ${err?.message || err}`);
});
}
}
})
@@ -1160,23 +1147,6 @@ export class DcRouter {
return false;
}
/**
* Find the first route name that matches a given domain
*/
private findRouteNameForDomain(domain: string): string | undefined {
if (!this.smartProxy) return undefined;
for (const route of this.smartProxy.routeManager.getRoutes()) {
if (!route.match.domains || !route.name) continue;
const routeDomains = Array.isArray(route.match.domains)
? route.match.domains
: [route.match.domains];
for (const pattern of routeDomains) {
if (this.isDomainMatch(domain, pattern)) return route.name;
}
}
return undefined;
}
/**
* Find ALL route names that match a given domain
*/