BREAKING CHANGE(certProvisioner): Refactor: Introduce unified CertProvisioner to centralize certificate provisioning and renewal; remove legacy ACME config from Port80Handler and update SmartProxy to delegate certificate lifecycle management.

This commit is contained in:
2025-05-02 11:19:14 +00:00
parent 09aadc702e
commit 8a396a04fa
11 changed files with 447 additions and 342 deletions

View File

@ -1,26 +1,47 @@
## Plan: Centralize Certificate Renewal for all certificates
## Refactor: Introduce a Unified CertProvisioner for Certificate Lifecycle
- [ ] Remove renewal logic from Port80Handler
- Delete `startRenewalTimer()` and `checkForRenewals()` methods
- Remove `renewThresholdDays` and `renewCheckIntervalHours` options from `IPort80HandlerOptions`
- [ ] Expose certificate status from Port80Handler
- Ensure `getDomainCertificateStatus()` returns `{certObtained, expiryDate}` for each domain
- [ ] Add renewal settings to SmartProxy
- Extend `port80HandlerConfig` to include `renewThresholdDays` and `renewCheckIntervalHours`
- [ ] Implement renewal scheduler in SmartProxy using taskbuffer
- Add dependency on `@push.rocks/taskbuffer` and import `{ Task, TaskManager }` in `SmartProxy`
- Add `performRenewals()` to iterate domains and trigger renewals where `daysRemaining <= renewThresholdDays`
- Instantiate a `TaskManager` and define a `Task` that wraps `performRenewals()`
- Use `taskManager.addAndScheduleTask(task, cronExpr)` to schedule renewals, building `cronExpr` from `renewCheckIntervalHours` (e.g. `0 0 */${renewCheckIntervalHours} * * *`)
- Call `taskManager.start()` in `SmartProxy.start()`
- [ ] Clean shutdown handling
- Call `taskManager.stop()` in `SmartProxy.stop()` alongside other cleanup
- [ ] Throttling and safety
- Skip domains already in `obtainingInProgress`
- Optionally batch or stagger renewal calls for large domain sets
- [ ] Tests
- Unit test `performRenewals()`, mocking `getDomainCertificateStatus()` to simulate expiring certificates
- Integration test using an in-memory `Port80Handler` to verify that scheduled renewals invoke `obtainCertificate()` correctly
- [ ] Documentation
- Update `readme.plan.md` (this section)
- Update `README.md` and code comments to document new renewal settings and workflow
- [x] Ensure Port80Handler is challenge-only:
- Remove any internal scheduling and deprecated ACME flows (`getAcmeClient`, `processAuthorizations`, `handleAcmeChallenge`) from Port80Handler.
- Remove legacy ACME options (`renewThresholdDays`, `renewCheckIntervalHours`, `mongoDescriptor`, etc.) from `IPort80HandlerOptions`.
- Retain only methods for HTTP-01 challenge and direct renewals (`obtainCertificate`, `renewCertificate`, `getDomainCertificateStatus`).
- [x] Clean up deprecated `acme` configuration:
- Remove the `acme` property from `IPortProxySettings` and all legacy references in code.
- [x] Implement `CertProvisioner` component:
- [x] Create class `ts/smartproxy/classes.pp.certprovisioner.ts`.
- [x] Constructor accepts:
* `domainConfigs: IDomainConfig[]`
* `port80Handler: Port80Handler`
* `networkProxyBridge: NetworkProxyBridge`
* optional `certProvider: (domain) => Promise<ICert | 'http01'>`
* `renewThresholdDays`, `renewCheckIntervalHours`, `autoRenew` settings.
- Responsibilities:
* Initial provisioning: static vs HTTP-01.
* Subscribe to Port80Handler events (CERTIFICATE_ISSUED/RENEWED) and to static cert updates.
* Re-emit unified `'certificate'` events to SmartProxy.
* Central scheduling of renewals via `@push.rocks/taskbuffer`.
- [x] Refactor SmartProxy:
- [x] Remove existing scheduling / renewal logic.
- [x] Instantiate `CertProvisioner` in `start()`, delegate cert workflows entirely.
- [x] Forward CertProvisioner events to SmartProxys `'certificate'` listener.
- [x] CertProvisioner lifecycle methods:
- [x] `start()`: provision all domains, start scheduler.
- [x] `stop()`: stop scheduler.
- [x] `requestCertificate(domain)`: on-demand provisioning.
- [x] Handle static certificate auto-refresh:
- [x] In the renewal scheduler, for domains with static certs, re-call `certProvider(domain)` near expiry.
- [x] Apply returned cert via `networkProxyBridge.applyExternalCertificate()`.
- [ ] Tests:
- Unit tests for `CertProvisioner`, mocking Port80Handler and `certProvider`:
* Validate initial provisioning and dynamic/static flows.
* Validate scheduling triggers correct renewals.
- Integration tests:
* Use actual in-memory Port80Handler with short intervals to verify renewals and event emission.
- [ ] Documentation:
- Add code-level TS doc for `CertProvisioner` API (options, methods, events).
- Update root `README.md` and architecture diagrams to show `CertProvisioner` role.