mirror of
https://github.com/certd/certd.git
synced 2026-10-13 15:49:25 +08:00
* feat(plugin): add Fastly custom TLS certificates integration Introduces a complete set of Fastly plugins for automating custom TLS certificates deployment: - `FastlyAccess`: Handles Fastly API authentication and provides helpers for dynamic data fetching (Services, Domains, Configs). - `FastlyUploadCert`: Uploads a new private key and certificate (2-step POST process) or updates an existing one (PATCH). - `FastlyDeployCert`: Activates a custom TLS certificate by linking it to a TLS configuration and domain via TLS Activations API. - `FastlyRefreshCert`: Bulk updates existing custom TLS certificates. - `FastlyPurgeCache`: Purges all cache for a specified Fastly Service. Includes comprehensive unit tests covering API request formats and error handling for all tasks. * fix(fastly): drop invalid 'multiple' option from refresh-cert input define createRemoteSelectInputDefine has no 'multiple' option; multi-select is already its default (mode: "tags", single: false). The excess property broke the production build (TS2353). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015dRf6EbAmwb37cpnvgBDrh * fix(fastly): correct TLS list unwrapping, paginate, harden purge - access: ctx.http already unwraps to the response body, so the JSON:API item array is at body.data, not body.data.data — getTlsConfigurations / getTlsDomains / getCertificates always returned [] and the selectors were empty. Fetch via listAllJsonApi, which also pages past Fastly's 20-item default so accounts with more than one page are fully listed. - purge-cache: send no body to /purge_all (auth is header-only) instead of an empty JSON:API object. - upload-cert / refresh-cert: note in the helper text that Fastly requires the renewed cert to reuse the original private key for PATCH updates. - test: cover the list-helper unwrapping, pagination aggregation, and the bare-array /service response. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015dRf6EbAmwb37cpnvgBDrh * fix(fastly): use output-selector for deploy cert ID input The FastlyDeployCert `certificateId` input was a free-text box whose helper pointed users at a non-existent "advanced settings" workflow to grab the upstream output variable. certd only surfaces a prior step's @TaskOutput through an input whose component is `output-selector`, so the FastlyUploadCert `fastlyCertId` output was never selectable. Bind the input to `output-selector` with `from: ["FastlyUploadCert"]`, matching the pattern used by plugin-aliyun deploy-to-cdn. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TFPQmZU9uvAfR9jHgaPP9J * fix(fastly): accept cert or uploaded id in deploy, share upload helpers Rework FastlyDeployCert to mirror the AWS CloudFront pattern instead of demanding a hand-typed / separately-uploaded certificate id: - The `certificateId` text box (and the earlier FastlyUploadCert-only output-selector) is replaced by a `cert: CertInfo | string` input whose output-selector accepts both a cert-apply step's cert and a FastlyUploadCert step's `fastlyCertId`. - execute() binds the id directly when it got a string; when it got a raw CertInfo it uploads to Fastly first (new optional `name` input), then creates the TLS activation. So a plain "apply cert -> activate" pipeline needs no separate upload step. Extract the Fastly certificate create (2-step key + cert) and update (PATCH) flows into FastlyAccess.createCertificate / updateCertificate and reuse them from both FastlyUploadCert and FastlyDeployCert. Tests updated to drive the plugins through a real FastlyAccess with only doRequestApi stubbed; added a deploy case for the raw-CertInfo path. Note: build/tests not run locally (no Node toolchain on this machine) — needs CI / local verification. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TFPQmZU9uvAfR9jHgaPP9J * fix(fastly): reuse existing key/cert when Fastly reports "already exists" Fastly deduplicates TLS private keys (and byte-identical certificates) by content, so re-running an upload fails with 400 "... already exists: '<id>'". Recover that id and carry on instead of aborting: - ensurePrivateKey(): on "already exists", parse the id from the error (or match an existing key by name), else throw actionable guidance. - createCertificate(): same treatment for the /tls/certificates POST; when no id is recoverable, tell the user to supply the certificate id so the PATCH (update) path is used. - Shared FastlyAccess.extractExistingId() helper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TFPQmZU9uvAfR9jHgaPP9J * fix(fastly): per-domain TLS activation, idempotent on renewal The TLS 域名 / TLS 配置 selectors use createRemoteSelectInputDefine, which defaults to multi-select ("tags"), so domainId arrived as an array and was written straight into tls_domain.data.id -> Fastly 500. A tls_activation also binds exactly one domain, so wildcard + apex needs one each. - domainId -> domainIds (string[]); loop, one activation per domain. configurationId gets single:true; both inputs are still normalised in execute() (array-or-scalar, plus legacy `domainId` key). - New FastlyAccess.deployActivation(): looks up an existing activation for the domain+configuration and PATCHes it onto the new certificate; otherwise POSTs a fresh one. Renewals no longer fail on a duplicate. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TFPQmZU9uvAfR9jHgaPP9J * feat(fastly): list service domains for TLS activation, not cert SANs The TLS 域名 selector was populated from GET /tls/domains, which only echoes certificate SAN entries — so a wildcard cert offered "*.g0l.net", which cannot be activated unless a service actually serves that wildcard. onGetTlsDomainList now lists the domains configured on the active version of each Fastly service (new FastlyAccess.getServiceDomains()), which are the names you can actually activate (e.g. www.g0l.net, covered by the wildcard). Cert-derived TLS domains are still appended, tagged "(来自证书)". The field stays free-entry so an unlisted host can be typed in. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TFPQmZU9uvAfR9jHgaPP9J --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>