feat(certificates): add certificate overview and reprovisioning in ops UI and API; track SmartProxy certificate events

This commit is contained in:
2026-02-13 17:05:33 +00:00
parent d10896196d
commit c5e2c262b7
16 changed files with 757 additions and 15 deletions

View File

@@ -183,6 +183,15 @@ export class DcRouter {
public cacheDb?: CacheDb;
public cacheCleaner?: CacheCleaner;
// Certificate status tracking from SmartProxy events
public certificateStatusMap = new Map<string, {
status: 'valid' | 'failed';
domain: string;
expiryDate?: string;
issuedAt?: string;
error?: string;
}>();
// TypedRouter for API endpoints
public typedrouter = new plugins.typedrequest.TypedRouter();
@@ -479,14 +488,34 @@ export class DcRouter {
if (acmeConfig) {
this.smartProxy.on('certificate-issued', (event) => {
console.log(`[DcRouter] Certificate issued for ${event.domain}, expires ${event.expiryDate}`);
const routeName = this.findRouteNameForDomain(event.domain);
if (routeName) {
this.certificateStatusMap.set(routeName, {
status: 'valid', domain: event.domain,
expiryDate: event.expiryDate, issuedAt: new Date().toISOString(),
});
}
});
this.smartProxy.on('certificate-renewed', (event) => {
console.log(`[DcRouter] Certificate renewed for ${event.domain}, expires ${event.expiryDate}`);
const routeName = this.findRouteNameForDomain(event.domain);
if (routeName) {
this.certificateStatusMap.set(routeName, {
status: 'valid', domain: event.domain,
expiryDate: event.expiryDate, issuedAt: new Date().toISOString(),
});
}
});
this.smartProxy.on('certificate-failed', (event) => {
console.error(`[DcRouter] Certificate failed for ${event.domain}:`, event.error);
const routeName = this.findRouteNameForDomain(event.domain);
if (routeName) {
this.certificateStatusMap.set(routeName, {
status: 'failed', domain: event.domain, error: event.error,
});
}
});
}
@@ -658,6 +687,21 @@ export class DcRouter {
return false;
}
/**
* Find the 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];
if (routeDomains.includes(domain)) return route.name;
}
return undefined;
}
public async stop() {
console.log('Stopping DcRouter services...');