From 8e722f5ab6de9afe4731030f90cef6d47718fd98 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Fri, 13 Feb 2026 21:37:52 +0000 Subject: [PATCH] feat(certificates): include certificate source/issuer and Rust-side status checks; pass eventComms into certProvisionFunction and record expiry information --- changelog.md | 9 +++++++ package.json | 2 +- pnpm-lock.yaml | 10 +++---- ts/00_commitinfo_data.ts | 2 +- ts/classes.dcrouter.ts | 25 +++++++++++------ ts/opsserver/handlers/certificate.handler.ts | 28 ++++++++++++++------ ts_web/00_commitinfo_data.ts | 2 +- 7 files changed, 54 insertions(+), 24 deletions(-) diff --git a/changelog.md b/changelog.md index cc07c0b..5214520 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # Changelog +## 2026-02-13 - 5.4.0 - feat(certificates) +include certificate source/issuer and Rust-side status checks; pass eventComms into certProvisionFunction and record expiry information + +- bump @push.rocks/smartproxy dependency to ^25.0.0 +- add optional 'source' field to certificate status and propagate event.source when certificates are issued, renewed, or failed +- change smartProxy.certProvisionFunction signature to accept eventComms; use it to log attempts, set source and expiryDate, and fall back to http-01 on DNS-01 failure +- make buildCertificateOverview async and query smartProxy.getCertificateStatus for a route when event-based status is unknown +- improve logging to include certificate source and more contextual messages + ## 2026-02-13 - 5.3.0 - feat(certificates) add certificate overview and reprovisioning in ops UI and API; track SmartProxy certificate events diff --git a/package.json b/package.json index e73057c..377976c 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@push.rocks/smartnetwork": "^4.4.0", "@push.rocks/smartpath": "^6.0.0", "@push.rocks/smartpromise": "^4.2.3", - "@push.rocks/smartproxy": "^24.0.0", + "@push.rocks/smartproxy": "^25.0.0", "@push.rocks/smartradius": "^1.1.1", "@push.rocks/smartrequest": "^5.0.1", "@push.rocks/smartrx": "^3.0.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be1738a..38bfbe0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,8 +75,8 @@ importers: specifier: ^4.2.3 version: 4.2.3 '@push.rocks/smartproxy': - specifier: ^24.0.0 - version: 24.0.0(@push.rocks/smartserve@2.0.1)(socks@2.8.7) + specifier: ^25.0.0 + version: 25.0.0(@push.rocks/smartserve@2.0.1)(socks@2.8.7) '@push.rocks/smartradius': specifier: ^1.1.1 version: 1.1.1 @@ -1040,8 +1040,8 @@ packages: '@push.rocks/smartpromise@4.2.3': resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==} - '@push.rocks/smartproxy@24.0.0': - resolution: {integrity: sha512-xSz6mrV59xmuiuaBgej6Fq611r9+Ay0ad2XiZAP/XGrkWykgQNeDZqzAq8dadmaCqO/3bVfH/mXlEYaKrDyTYA==} + '@push.rocks/smartproxy@25.0.0': + resolution: {integrity: sha512-FuXIyKAlTdUUSFszzYjP/WAMb3Dq//gBdluADvjgAeQn1YplFonMo/afRU+qSI7WsPsB7X7vkFwLba5ASYdiUg==} '@push.rocks/smartpuppeteer@2.0.5': resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==} @@ -6441,7 +6441,7 @@ snapshots: '@push.rocks/smartpromise@4.2.3': {} - '@push.rocks/smartproxy@24.0.0(@push.rocks/smartserve@2.0.1)(socks@2.8.7)': + '@push.rocks/smartproxy@25.0.0(@push.rocks/smartserve@2.0.1)(socks@2.8.7)': dependencies: '@push.rocks/lik': 6.2.2 '@push.rocks/smartacme': 8.0.0(@push.rocks/smartserve@2.0.1)(socks@2.8.7) diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index a2994f6..b2f8ed3 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '5.3.0', + version: '5.4.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' } diff --git a/ts/classes.dcrouter.ts b/ts/classes.dcrouter.ts index 216006e..14f2f00 100644 --- a/ts/classes.dcrouter.ts +++ b/ts/classes.dcrouter.ts @@ -189,6 +189,7 @@ export class DcRouter { domain: string; expiryDate?: string; issuedAt?: string; + source?: string; error?: string; }>(); @@ -450,9 +451,14 @@ export class DcRouter { }); await this.smartAcme.start(); - smartProxyConfig.certProvisionFunction = async (domain: string) => { + smartProxyConfig.certProvisionFunction = async (domain, eventComms) => { try { + eventComms.log(`Attempting DNS-01 via SmartAcme for ${domain}`); + eventComms.setSource('smartacme-dns-01'); const cert = await this.smartAcme.getCertificateForDomain(domain); + if (cert.validUntil) { + eventComms.setExpiryDate(new Date(cert.validUntil)); + } return { id: cert.id, domainName: cert.domainName, @@ -463,7 +469,7 @@ export class DcRouter { csr: cert.csr, }; } catch (err) { - console.error(`[DcRouter] SmartAcme DNS-01 failed for ${domain}, falling back to http-01:`, err.message); + eventComms.warn(`SmartAcme DNS-01 failed for ${domain}: ${err.message}, falling back to http-01`); return 'http01'; } }; @@ -486,34 +492,37 @@ export class DcRouter { }); if (acmeConfig) { - this.smartProxy.on('certificate-issued', (event) => { - console.log(`[DcRouter] Certificate issued for ${event.domain}, expires ${event.expiryDate}`); + this.smartProxy.on('certificate-issued', (event: plugins.smartproxy.ICertificateIssuedEvent) => { + console.log(`[DcRouter] Certificate issued for ${event.domain} via ${event.source}, 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(), + source: event.source, }); } }); - this.smartProxy.on('certificate-renewed', (event) => { - console.log(`[DcRouter] Certificate renewed for ${event.domain}, expires ${event.expiryDate}`); + this.smartProxy.on('certificate-renewed', (event: plugins.smartproxy.ICertificateIssuedEvent) => { + console.log(`[DcRouter] Certificate renewed for ${event.domain} via ${event.source}, 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(), + source: event.source, }); } }); - this.smartProxy.on('certificate-failed', (event) => { - console.error(`[DcRouter] Certificate failed for ${event.domain}:`, event.error); + this.smartProxy.on('certificate-failed', (event: plugins.smartproxy.ICertificateFailedEvent) => { + console.error(`[DcRouter] Certificate failed for ${event.domain} (${event.source}):`, event.error); const routeName = this.findRouteNameForDomain(event.domain); if (routeName) { this.certificateStatusMap.set(routeName, { status: 'failed', domain: event.domain, error: event.error, + source: event.source, }); } }); diff --git a/ts/opsserver/handlers/certificate.handler.ts b/ts/opsserver/handlers/certificate.handler.ts index 033fa0c..74f78fc 100644 --- a/ts/opsserver/handlers/certificate.handler.ts +++ b/ts/opsserver/handlers/certificate.handler.ts @@ -16,7 +16,7 @@ export class CertificateHandler { new plugins.typedrequest.TypedHandler( 'getCertificateOverview', async (dataArg) => { - const certificates = this.buildCertificateOverview(); + const certificates = await this.buildCertificateOverview(); const summary = this.buildSummary(certificates); return { certificates, summary }; } @@ -34,7 +34,7 @@ export class CertificateHandler { ); } - private buildCertificateOverview(): interfaces.requests.ICertificateInfo[] { + private async buildCertificateOverview(): Promise { const dcRouter = this.opsServerRef.dcRouterRef; const smartProxy = dcRouter.smartProxy; if (!smartProxy) return []; @@ -82,14 +82,26 @@ export class CertificateHandler { expiryDate = eventStatus.expiryDate; issuedAt = eventStatus.issuedAt; error = eventStatus.error; + if (eventStatus.source) { + issuer = eventStatus.source; + } } - // Try to get Rust-side certificate data - try { - // getCertificateStatus is async but we're in a sync context - // We'll rely on event-based data primarily - } catch { - // Ignore errors from Rust bridge + // Try Rust-side certificate status if no event data + if (status === 'unknown') { + try { + const rustStatus = await smartProxy.getCertificateStatus(route.name); + if (rustStatus) { + if (rustStatus.expiryDate) expiryDate = rustStatus.expiryDate; + if (rustStatus.issuer) issuer = rustStatus.issuer; + if (rustStatus.issuedAt) issuedAt = rustStatus.issuedAt; + if (rustStatus.status === 'valid' || rustStatus.status === 'expired') { + status = rustStatus.status; + } + } + } catch { + // Rust bridge may not support this command yet — ignore + } } // Compute status from expiry date if we have one and status is still valid/unknown diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index a2994f6..b2f8ed3 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '5.3.0', + version: '5.4.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' }