Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cf5d616769 | |||
| 8e722f5ab6 |
@@ -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
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@serve.zone/dcrouter",
|
||||
"private": false,
|
||||
"version": "5.3.0",
|
||||
"version": "5.4.0",
|
||||
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
@@ -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",
|
||||
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -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)
|
||||
|
||||
@@ -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.'
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ export class CertificateHandler {
|
||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetCertificateOverview>(
|
||||
'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<interfaces.requests.ICertificateInfo[]> {
|
||||
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
|
||||
|
||||
@@ -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.'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user