diff --git a/changelog.md b/changelog.md index 43bbc8e..8c1fc27 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 2026-04-03 - 9.4.0 - feat(smartacme) +add forceRenew option for certificate issuance requests + +- extends getCertificateForDomain() options with forceRenew to allow bypassing cached non-expired certificates +- preserves the existing certificate as a fallback during forced renewal instead of deleting it before successful issuance + ## 2026-03-27 - 9.3.1 - fix(acme) parse issued certificate expiry from X.509 metadata and update build compatibility for dependency upgrades diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 971c8f6..d2a32f9 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartacme', - version: '9.3.1', + version: '9.4.0', description: 'A TypeScript-based ACME client and server for certificate management with built-in CA, supporting LetsEncrypt and custom ACME authorities.' } diff --git a/ts/smartacme.classes.smartacme.ts b/ts/smartacme.classes.smartacme.ts index 46d0887..47b9aa6 100644 --- a/ts/smartacme.classes.smartacme.ts +++ b/ts/smartacme.classes.smartacme.ts @@ -360,8 +360,9 @@ export class SmartAcme { */ public async getCertificateForDomain( domainArg: string, - options?: { includeWildcard?: boolean } + options?: { includeWildcard?: boolean; forceRenew?: boolean } ): Promise { + const forceRenew = options?.forceRenew ?? false; // Determine if this is a wildcard request (e.g., '*.example.com'). const isWildcardRequest = domainArg.startsWith('*.'); // Determine the base domain for certificate retrieval/issuance. @@ -381,12 +382,14 @@ export class SmartAcme { // Retrieve any existing certificate record by base domain. const retrievedCertificate = await this.certmanager.retrieveCertificate(certDomainName); - if (retrievedCertificate && !retrievedCertificate.shouldBeRenewed()) { + if (!forceRenew && retrievedCertificate && !retrievedCertificate.shouldBeRenewed()) { return retrievedCertificate; - } else if (retrievedCertificate && retrievedCertificate.shouldBeRenewed()) { - // Remove old certificate via certManager + } else if (!forceRenew && retrievedCertificate && retrievedCertificate.shouldBeRenewed()) { + // Remove old certificate via certManager (safe — it needs renewal anyway) await this.certmanager.deleteCertificate(certDomainName); } + // When forceRenew is true, keep the existing cert in place as fallback. + // The new cert will overwrite it upon successful issuance via certmanager.storeCertificate(). // Build issuance input and trigger the constrained task const issuanceInput: ICertIssuanceInput = {