diff --git a/changelog.md b/changelog.md index 1a8a969..b8b2ecc 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2026-04-03 - 12.8.0 - feat(certificates) +add force renew option for domain certificate reprovisioning + +- pass an optional forceRenew flag through certificate reprovision requests from the UI to the ops handler +- use smartacme forceRenew support and return renewal-specific success messages +- update the SmartAcme dependency to version ^9.4.0 + ## 2026-04-03 - 12.7.0 - feat(opsserver) add RADIUS and VPN metrics to combined ops stats and overview dashboards, and stream live log buffer entries in follow mode diff --git a/package.json b/package.json index 677353c..870e967 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@push.rocks/lik": "^6.4.0", "@push.rocks/projectinfo": "^5.1.0", "@push.rocks/qenv": "^6.1.3", - "@push.rocks/smartacme": "^9.3.1", + "@push.rocks/smartacme": "^9.4.0", "@push.rocks/smartdata": "^7.1.3", "@push.rocks/smartdb": "^2.1.1", "@push.rocks/smartdns": "^7.9.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04b06fd..4d4ab83 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,8 +39,8 @@ importers: specifier: ^6.1.3 version: 6.1.3 '@push.rocks/smartacme': - specifier: ^9.3.1 - version: 9.3.1(socks@2.8.7) + specifier: ^9.4.0 + version: 9.4.0(socks@2.8.7) '@push.rocks/smartdata': specifier: ^7.1.3 version: 7.1.3(socks@2.8.7) @@ -1108,8 +1108,8 @@ packages: '@push.rocks/qenv@6.1.3': resolution: {integrity: sha512-+z2hsAU/7CIgpYLFqvda8cn9rUBMHqLdQLjsFfRn5jPoD7dJ5rFlpkbhfM4Ws8mHMniwWaxGKo+q/YBhtzRBLg==} - '@push.rocks/smartacme@9.3.1': - resolution: {integrity: sha512-Cl1DVQ+rfpaYkk6VVm/KYVeUYzWfXzSfTXybHfCZ5SuiACuTVHZ6jK8TouELaV1RgrdYnIp0MrbiY2Kqi8ayAw==} + '@push.rocks/smartacme@9.4.0': + resolution: {integrity: sha512-mSqsI859mHI9fCZxLfayzPf/WvukDFzVHOh02vXq3ujxbb5M+ArMnXe0MmC2egR9GeXmQTm3DTENaETX5ffMtw==} '@push.rocks/smartarchive@4.2.4': resolution: {integrity: sha512-uiqVAXPxmr8G5rv3uZvZFMOCt8l7cZC3nzvsy4YQqKf/VkPhKIEX+b7LkAeNlxPSYUiBQUkNRoawg9+5BaMcHg==} @@ -5960,7 +5960,7 @@ snapshots: '@push.rocks/smartlog': 3.2.1 '@push.rocks/smartpath': 6.0.0 - '@push.rocks/smartacme@9.3.1(socks@2.8.7)': + '@push.rocks/smartacme@9.4.0(socks@2.8.7)': dependencies: '@apiclient.xyz/cloudflare': 7.1.0 '@peculiar/x509': 2.0.0 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 97dad9a..34fb3b8 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: '12.7.0', + version: '12.8.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' } diff --git a/ts/opsserver/handlers/certificate.handler.ts b/ts/opsserver/handlers/certificate.handler.ts index e79c326..eb85464 100644 --- a/ts/opsserver/handlers/certificate.handler.ts +++ b/ts/opsserver/handlers/certificate.handler.ts @@ -43,7 +43,7 @@ export class CertificateHandler { new plugins.typedrequest.TypedHandler( 'reprovisionCertificateDomain', async (dataArg) => { - return this.reprovisionCertificateDomain(dataArg.domain); + return this.reprovisionCertificateDomain(dataArg.domain, dataArg.forceRenew); } ) ); @@ -318,7 +318,7 @@ export class CertificateHandler { /** * Domain-based reprovisioning — clears backoff first, then triggers provision */ - private async reprovisionCertificateDomain(domain: string): Promise<{ success: boolean; message?: string }> { + private async reprovisionCertificateDomain(domain: string, forceRenew?: boolean): Promise<{ success: boolean; message?: string }> { const dcRouter = this.opsServerRef.dcRouterRef; const smartProxy = dcRouter.smartProxy; @@ -337,8 +337,8 @@ export class CertificateHandler { // Try to provision via SmartAcme directly if (dcRouter.smartAcme) { try { - await dcRouter.smartAcme.getCertificateForDomain(domain); - return { success: true, message: `Certificate reprovisioning triggered for domain '${domain}'` }; + await dcRouter.smartAcme.getCertificateForDomain(domain, { forceRenew: forceRenew ?? false }); + return { success: true, message: forceRenew ? `Certificate force-renewed for domain '${domain}'` : `Certificate reprovisioning triggered for domain '${domain}'` }; } catch (err: unknown) { return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` }; } diff --git a/ts_interfaces/requests/certificate.ts b/ts_interfaces/requests/certificate.ts index fed0de2..4cd9a55 100644 --- a/ts_interfaces/requests/certificate.ts +++ b/ts_interfaces/requests/certificate.ts @@ -68,6 +68,7 @@ export interface IReq_ReprovisionCertificateDomain extends plugins.typedrequestI request: { identity: authInterfaces.IIdentity; domain: string; + forceRenew?: boolean; }; response: { success: boolean; diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 97dad9a..34fb3b8 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: '12.7.0', + version: '12.8.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' } diff --git a/ts_web/appstate.ts b/ts_web/appstate.ts index 70c66a9..9df3dcf 100644 --- a/ts_web/appstate.ts +++ b/ts_web/appstate.ts @@ -605,8 +605,8 @@ export const fetchCertificateOverviewAction = certificateStatePart.createAction( } }); -export const reprovisionCertificateAction = certificateStatePart.createAction( - async (statePartArg, domain, actionContext): Promise => { +export const reprovisionCertificateAction = certificateStatePart.createAction<{ domain: string; forceRenew?: boolean }>( + async (statePartArg, dataArg, actionContext): Promise => { const context = getActionContext(); const currentState = statePartArg.getState()!; @@ -617,7 +617,8 @@ export const reprovisionCertificateAction = certificateStatePart.createAction { + const doReprovision = async (forceRenew = false) => { await appstate.certificateStatePart.dispatchAction( appstate.reprovisionCertificateAction, - cert.domain, + { domain: cert.domain, forceRenew }, ); const { DeesToast } = await import('@design.estate/dees-catalog'); DeesToast.show({ - message: `Reprovisioning triggered for ${cert.domain}`, + message: forceRenew + ? `Force renewal triggered for ${cert.domain}` + : `Reprovisioning triggered for ${cert.domain}`, type: 'success', duration: 3000, }); @@ -336,7 +338,7 @@ export class OpsViewCertificates extends DeesElement { name: 'Force Renew', action: async (modalArg: any) => { await modalArg.destroy(); - await doReprovision(); + await doReprovision(true); }, }, ],