diff --git a/.editorconfig b/.editorconfig index f0b25219..14d9f966 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,5 +10,7 @@ trim_trailing_whitespace = true insert_final_newline = true [*.go] +charset = utf-8 +end_of_line = lf indent_size = 2 indent_style = tab diff --git a/.github/ISSUE_TEMPLATE/3-questions.yml b/.github/ISSUE_TEMPLATE/3-questions.yml index fd847486..f4918034 100644 --- a/.github/ISSUE_TEMPLATE/3-questions.yml +++ b/.github/ISSUE_TEMPLATE/3-questions.yml @@ -1,6 +1,6 @@ name: "❓ Questions" description: "遇到了困难需要求助? / Have problem in use and need help?" -title: "[Feature] 简要描述你遇到的问题" +title: "简要描述你遇到的问题" body: - type: markdown attributes: @@ -19,6 +19,14 @@ body: 3. Yes, I've read the [documentation](https://docs.certimate.me/en/) and didn't find any similar. 4. Please describe the problem in detail according to the template specification, otherwise the issue will be closed directly. + - type: input + attributes: + label: 软件版本 / Release Version + description: 请提供 Certimate 的具体版本。 / Please provide the specific version of Certimate. + placeholder: (e.g. v1.0.0) + validations: + required: true + - type: textarea attributes: label: 问题描述 / Description diff --git a/README.md b/README.md index 0e32d29e..18efb6f1 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决 - 支持 20+ 域名托管商(如阿里云、腾讯云、Cloudflare 等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-dns-providers)); - 支持 70+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-host-providers)); - 支持邮件、钉钉、飞书、企业微信、Webhook 等多种通知渠道; -- 支持 Let's Encrypt、ZeroSSL、Google Trust Services 等多种 ACME 证书颁发机构; +- 支持 Let's Encrypt、Buypass、Google Trust Services、SSL.com、ZeroSSL 等多种 ACME 证书颁发机构; - 更多特性等待探索。 ## ⏱️ 快速启动 diff --git a/README_EN.md b/README_EN.md index 55999bfc..5b62f12d 100644 --- a/README_EN.md +++ b/README_EN.md @@ -41,7 +41,7 @@ Certimate aims to provide users with a secure and user-friendly SSL certificate - Supports more than 20+ domain registrars (e.g., Alibaba Cloud, Tencent Cloud, Cloudflare, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-dns-providers)); - Supports more than 70+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-host-providers)); - Supports multiple notification channels including email, DingTalk, Feishu, WeCom, Webhook, and more; -- Supports multiple ACME CAs including Let's Encrypt, ZeroSSL, Google Trust Services, and more; +- Supports multiple ACME CAs including Let's Encrypt, Buypass, Google Trust Services,SSL.com, ZeroSSL, and more; - More features waiting to be discovered. ## ⏱️ Fast Track diff --git a/go.mod b/go.mod index 7c8723af..fd7c0355 100644 --- a/go.mod +++ b/go.mod @@ -5,29 +5,32 @@ go 1.23.0 toolchain go1.23.2 require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 - github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0 - github.com/G-Core/gcorelabscdn-go v1.0.28 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1 + github.com/Edgio/edgio-api v0.0.0-workspace + github.com/G-Core/gcorelabscdn-go v1.0.29 github.com/alibabacloud-go/alb-20200616/v2 v2.2.8 + github.com/alibabacloud-go/apig-20240327/v3 v3.2.2 github.com/alibabacloud-go/cas-20200407/v3 v3.0.4 github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2 - github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4 - github.com/alibabacloud-go/esa-20240910/v2 v2.23.0 - github.com/alibabacloud-go/fc-20230330/v4 v4.1.7 + github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.2 + github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7 + github.com/alibabacloud-go/esa-20240910/v2 v2.31.1 + github.com/alibabacloud-go/fc-20230330/v4 v4.3.4 github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 github.com/alibabacloud-go/live-20161101 v1.1.1 github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3 github.com/alibabacloud-go/slb-20140515/v4 v4.0.10 - github.com/alibabacloud-go/tea v1.3.4 - github.com/alibabacloud-go/vod-20170321/v4 v4.7.0 - github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.5 + github.com/alibabacloud-go/tea v1.3.9 + github.com/alibabacloud-go/vod-20170321/v4 v4.8.3 + github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.1 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible - github.com/aws/aws-sdk-go-v2/service/acm v1.31.1 - github.com/aws/aws-sdk-go-v2/service/cloudfront v1.45.2 - github.com/baidubce/bce-sdk-go v0.9.221 - github.com/byteplus-sdk/byteplus-sdk-golang v1.0.42 - github.com/go-acme/lego/v4 v4.22.2 + github.com/aws/aws-sdk-go-v2/service/acm v1.31.3 + github.com/aws/aws-sdk-go-v2/service/cloudfront v1.45.3 + github.com/baidubce/bce-sdk-go v0.9.224 + github.com/byteplus-sdk/byteplus-sdk-golang v1.0.44 + github.com/go-acme/lego/v4 v4.23.1 github.com/go-resty/resty/v2 v2.16.5 github.com/go-viper/mapstructure/v2 v2.2.1 github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141 @@ -36,28 +39,28 @@ require ( github.com/libdns/libdns v0.2.3 github.com/nikoksr/notify v1.3.0 github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 - github.com/pkg/sftp v1.13.8 + github.com/pkg/sftp v1.13.9 github.com/pocketbase/dbx v1.11.0 - github.com/pocketbase/pocketbase v0.26.1 + github.com/pocketbase/pocketbase v0.27.1 github.com/povsister/scp v0.0.0-20240802064259-28781e87b246 - github.com/qiniu/go-sdk/v7 v7.25.2 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1115 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1127 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1127 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1117 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1115 + github.com/qiniu/go-sdk/v7 v7.25.3 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1136 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1143 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1147 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1143 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1126 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1115 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1125 - github.com/ucloud/ucloud-sdk-go v0.22.31 - github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.9 - github.com/volcengine/volc-sdk-golang v1.0.199 - github.com/volcengine/volcengine-go-sdk v1.0.187 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1138 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1136 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1147 + github.com/ucloud/ucloud-sdk-go v0.22.33 + github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.11 + github.com/volcengine/volc-sdk-golang v1.0.204 + github.com/volcengine/volcengine-go-sdk v1.1.4 gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 - golang.org/x/crypto v0.36.0 - golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 + golang.org/x/crypto v0.37.0 + golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 k8s.io/api v0.32.3 k8s.io/apimachinery v0.32.3 k8s.io/client-go v0.32.3 @@ -65,21 +68,25 @@ require ( ) require ( - github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/alibabacloud-go/alibabacloud-gateway-fc-util v0.0.7 // indirect + github.com/alibabacloud-go/ddoscoo-20200101/v4 v4.0.0 // indirect github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1 // indirect github.com/alibabacloud-go/tea-fileform v1.1.1 // indirect github.com/alibabacloud-go/tea-oss-sdk v1.1.3 // indirect github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect github.com/avast/retry-go v3.0.0+incompatible // indirect - github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 // indirect github.com/blinkbean/dingtalk v1.1.3 // indirect + github.com/buger/goterm v1.0.4 // indirect + github.com/diskfs/go-diskfs v1.5.0 // indirect + github.com/djherbis/times v1.6.0 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-lark/lark v1.15.1 // indirect @@ -94,19 +101,24 @@ require ( github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect + github.com/jinzhu/copier v0.3.4 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/luthermonson/go-proxmox v0.2.2 // indirect + github.com/magefile/mage v1.14.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect + github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect github.com/nrdcg/desec v0.10.0 // indirect github.com/nrdcg/mailinabox v0.2.0 // indirect github.com/nrdcg/porkbun v0.4.0 // indirect @@ -115,7 +127,7 @@ require ( github.com/qiniu/dyn v1.3.0 // indirect github.com/qiniu/x v1.10.5 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect github.com/x448/float16 v0.8.4 // indirect go.mongodb.org/mongo-driver v1.17.2 // indirect @@ -132,7 +144,7 @@ require ( ) require ( - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0 github.com/alibabacloud-go/debug v1.0.1 // indirect @@ -140,35 +152,35 @@ require ( github.com/alibabacloud-go/openapi-util v0.1.1 // indirect github.com/alibabacloud-go/tea-utils v1.4.5 // indirect github.com/alibabacloud-go/tea-xml v1.1.3 // indirect - github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 // indirect - github.com/aliyun/credentials-go v1.4.3 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 // indirect + github.com/aliyun/credentials-go v1.4.5 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go-v2 v1.36.3 - github.com/aws/aws-sdk-go-v2/config v1.29.5 - github.com/aws/aws-sdk-go-v2/credentials v1.17.58 - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.9 + github.com/aws/aws-sdk-go-v2/credentials v1.17.62 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect github.com/aws/smithy-go v1.22.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/cloudflare/cloudflare-go v0.114.0 // indirect + github.com/cloudflare/cloudflare-go v0.115.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/disintegration/imaging v1.6.2 // indirect github.com/domodwyer/mailyak/v3 v3.6.2 github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.18.0 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect - github.com/ganigeorgiev/fexpr v0.4.1 // indirect - github.com/go-jose/go-jose/v4 v4.0.4 // indirect + github.com/ganigeorgiev/fexpr v0.5.0 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect - github.com/goccy/go-json v0.10.4 // indirect + github.com/goccy/go-json v0.10.5 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.6.0 github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -177,40 +189,42 @@ require ( github.com/kr/fs v0.1.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/miekg/dns v1.1.62 // indirect + github.com/miekg/dns v1.1.64 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nrdcg/namesilo v0.2.1 // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect - github.com/pkg/errors v0.9.1 + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.10.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect - golang.org/x/image v0.25.0 // indirect + golang.org/x/image v0.26.0 // indirect golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.37.0 // indirect - golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.12.0 - golang.org/x/sys v0.31.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.9.0 - golang.org/x/tools v0.31.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/oauth2 v0.29.0 // indirect + golang.org/x/sync v0.13.0 + golang.org/x/sys v0.32.0 // indirect + golang.org/x/term v0.31.0 // indirect + golang.org/x/text v0.24.0 // indirect + golang.org/x/time v0.11.0 + golang.org/x/tools v0.32.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/libc v1.61.13 // indirect + modernc.org/libc v1.62.1 // indirect modernc.org/mathutil v1.7.1 // indirect - modernc.org/memory v1.8.2 // indirect - modernc.org/sqlite v1.36.1 // indirect + modernc.org/memory v1.9.1 // indirect + modernc.org/sqlite v1.37.0 // indirect ) -replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 => ./internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0 +replace github.com/Edgio/edgio-api v0.0.0-workspace => ./internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace -replace gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 => ./internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1 +replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 => ./internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0 + +replace gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 => ./internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1 diff --git a/go.sum b/go.sum index 98e8211f..7b7fcb73 100644 --- a/go.sum +++ b/go.sum @@ -34,18 +34,14 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= -github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0 h1:btEsytNrA4TG3edZnnUnzOz8W2MjOd6Bu3/7xyOXSOY= -github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0/go.mod h1:5SlTxxL1U4LLipEr7pAbnu6Ck5y3aIEu4L/tVbGmpsY= -github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw= -github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= @@ -56,19 +52,23 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourceg github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1 h1:HUJQzFYTv7t3V1dxPms52eEgl0l9xCNqutDrY45Lvmw= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1/go.mod h1:ig/8nSkzmfxm5QGeIy5JYIEj8JEFy5JxvY3OB1YNRC4= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/G-Core/gcorelabscdn-go v1.0.28 h1:6ymVMV3HPTICO5BWJCEcZZzgY+Pc/+/TQMzeXMN77GQ= -github.com/G-Core/gcorelabscdn-go v1.0.28/go.mod h1:iSGXaTvZBzDHQW+rKFS918BgFVpONcyLEijwh8WsXpE= +github.com/G-Core/gcorelabscdn-go v1.0.29 h1:9jNCwzNZAgihTPe+nrsLD2c0GHjxvpuV3VEA74L5Kkk= +github.com/G-Core/gcorelabscdn-go v1.0.29/go.mod h1:iSGXaTvZBzDHQW+rKFS918BgFVpONcyLEijwh8WsXpE= github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -95,10 +95,14 @@ github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do2 github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g= +github.com/alibabacloud-go/apig-20240327/v3 v3.2.2 h1:yH84ePgqtA2tF3ly7Tf3AA5ogl2SC8kqCNG4+zz4yo4= +github.com/alibabacloud-go/apig-20240327/v3 v3.2.2/go.mod h1:XLaCapbSH7olJTs42wisDO9JvX9BGy5acZk0bLNejDs= github.com/alibabacloud-go/cas-20200407/v3 v3.0.4 h1:ngRlctbt135zoujwX0lXSv9m4h1/bmg/yalQS0z1EWc= github.com/alibabacloud-go/cas-20200407/v3 v3.0.4/go.mod h1:6n9MZ9SH3HlSzfe2oKwjOqhJx3dxvW2gMDO+lq8t9U4= github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2 h1:+KJOPukTM+xMyiLOW5qBwYKG2df3Ar7coRsqc1juKO8= github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2/go.mod h1:GnPiPL3HlzCi8SGiLiVgKrAFkP1vTtcF4yGtjsl4wfo= +github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.2 h1:Ug50clztqiQAy5t0R9Vejibz2Xgxm1Tpw2Y6A9eAwRE= +github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.2/go.mod h1:l9Zd2FanDUO2UqHJSPnOv+cY9DVT+YXcr97zfpSHywo= github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY= github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI= github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE= @@ -109,14 +113,17 @@ github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNd github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.5/go.mod h1:kUe8JqFmoVU7lfBauaDD5taFaW7mBI+xVsyHutYtabg= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE= -github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4 h1:IGSZHlOnWwBbLtX5xDplQvZOH0nkrV7Wmq+Fto7JK5w= -github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4/go.mod h1:Wxis0IBFusdbo44HO6KYYCJR1rRkoh47QQOYWvaheSU= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.11/go.mod h1:wHxkgZT1ClZdcwEVP/pDgYK/9HucsnCfMipmJgCz4xY= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7 h1:ASXSBga98QrGMxbIThCD6jAti09gedLfvry6yJtsoBE= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7/go.mod h1:TBpgqm3XofZz2LCYjZhektGPU7ArEgascyzbm4SjFo4= github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg= github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ= github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo= github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0 h1:EQmKhYju6y38kJ1ZvZROeJG2Q1Wk6hlc8KQrVhvGyaw= github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0/go.mod h1:b9qzvr/2V1f0r1Z6xUmkLqEouKcPGy4LCC22yV+6HQo= +github.com/alibabacloud-go/ddoscoo-20200101/v4 v4.0.0 h1:z9dPOvBRxzpD+FQ2uu/p2Z92I+PY9MUZMauwC+8AC6M= +github.com/alibabacloud-go/ddoscoo-20200101/v4 v4.0.0/go.mod h1:Cdg3Fu4jFByamRzt3AkeiBssoVPRNDs+EPYMP2fIj78= github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= @@ -124,10 +131,10 @@ github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/ql github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= github.com/alibabacloud-go/endpoint-util v1.1.1 h1:ZkBv2/jnghxtU0p+upSU0GGzW1VL9GQdZO3mcSUTUy8= github.com/alibabacloud-go/endpoint-util v1.1.1/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= -github.com/alibabacloud-go/esa-20240910/v2 v2.23.0 h1:Z/AALmxhOfzN+35tNCvm62/pET4IlxhDQn4nsdLqNzk= -github.com/alibabacloud-go/esa-20240910/v2 v2.23.0/go.mod h1:P1w/+i7dE2xSXVHJznEOVImlLtqqrzUJQQk2AsyBJ6o= -github.com/alibabacloud-go/fc-20230330/v4 v4.1.7 h1:rQvPfzPaouL/WGNgMDMCplA4wDscmVFff7aLCUkjv4g= -github.com/alibabacloud-go/fc-20230330/v4 v4.1.7/go.mod h1:ssEfKO6MskPtq7QaQnyiOHGWLXOZcl7a8YIf8u56DGc= +github.com/alibabacloud-go/esa-20240910/v2 v2.31.1 h1:LACf71RxZjaystAfcWXa3EMtueVKNGxsCR3L+UihKtU= +github.com/alibabacloud-go/esa-20240910/v2 v2.31.1/go.mod h1:qa4hC7W/BQOc9liuJckLnBLxILEzYjg2xhAZ+UVeUUQ= +github.com/alibabacloud-go/fc-20230330/v4 v4.3.4 h1:DMUkeW24CWuvChy9uOD1DzMh3ToVARCB6m3xxWBslic= +github.com/alibabacloud-go/fc-20230330/v4 v4.3.4/go.mod h1:vEJimQ6E/e+m2z0/oXdeQWlFw/Pi/Ar6NKcMrSvcILE= github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 h1:A3D8Mp6qf8DfR6Dt5MpS8aDVaWfS4N85T5CvGUvgrjM= github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12/go.mod h1:F5c0E5UB3k8v6neTtw3FBcJ1YCNFzVoL1JPRHTe33u4= github.com/alibabacloud-go/live-20161101 v1.1.1 h1:rUGfA8RHmCMtQ5M3yMSyRde+yRXWqVecmiXBU3XrGJ8= @@ -153,9 +160,9 @@ github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.2.1/go.mod h1:qbzof29bM/IFhLMtJPrgTGK3eauV5J2wSyEUo4OEmnA= github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= -github.com/alibabacloud-go/tea v1.3.2/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= -github.com/alibabacloud-go/tea v1.3.4 h1:QGTns2930y+ANmoNcUS74TgYpsoqusSrLIyYDOvIFFI= -github.com/alibabacloud-go/tea v1.3.4/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= +github.com/alibabacloud-go/tea v1.3.8/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= +github.com/alibabacloud-go/tea v1.3.9 h1:bjgt1bvdY780vz/17iWNNtbXl4A77HWntWMeaUF3So0= +github.com/alibabacloud-go/tea v1.3.9/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= github.com/alibabacloud-go/tea-fileform v1.1.1 h1:1YG6erAP3joQ0XdCXYIotuD7zyOM6qCR49xkp5FZDeU= github.com/alibabacloud-go/tea-fileform v1.1.1/go.mod h1:ZeCV91o4ISmxidd686f0ebdS5EDHWU+vW+TkjLhrsFE= github.com/alibabacloud-go/tea-oss-sdk v1.1.3 h1:EhAHI6edMeqgkZEqP7r4nc9iMWAUBKGxJHoBsOSKTtU= @@ -177,20 +184,20 @@ github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0= github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= -github.com/alibabacloud-go/vod-20170321/v4 v4.7.0 h1:hpsnJBX5EeMrFujopMCjfq+p8XbNvPhFw6LOTV/WHd8= -github.com/alibabacloud-go/vod-20170321/v4 v4.7.0/go.mod h1:TkgLKMSLu0qZN8Qdcu8svfHREyI64kjFvrp/GhrD4VQ= -github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.5 h1:ldAm1nvsCq66igjtcZyGhAoLClr+2eZ/pMIBUdKCOMM= -github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.5/go.mod h1:DohGoS8BnMxHXghHebtjPP7+GMdxPsRN19T3nn2HcCU= -github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 h1:YBkf7H5CSgrlb3C1aWcpDt7Vk8UEGFPeD2OOirtt6IM= -github.com/aliyun/alibaba-cloud-sdk-go v1.63.83/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= +github.com/alibabacloud-go/vod-20170321/v4 v4.8.3 h1:IXDfINF3Wc88SKIijYgqy9HF3NiA68F97wgVeiRRwkc= +github.com/alibabacloud-go/vod-20170321/v4 v4.8.3/go.mod h1:5ocQ6hIc9tpGixD2iy099aOGwIgpzjT2le4Krd4aLn8= +github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.1 h1:7gHYtb2swx96tG7rflKoiFOdjKZ/W3N7azS6LT1TVFI= +github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.1/go.mod h1:DohGoS8BnMxHXghHebtjPP7+GMdxPsRN19T3nn2HcCU= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 h1:yUkCbrSM1cWtgBfRVKMQtdt22KhDvKY7g4V+92eG9wA= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.100/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM= github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= -github.com/aliyun/credentials-go v1.4.3 h1:N3iHyvHRMyOwY1+0qBLSf3hb5JFiOujVSVuEpgeGttY= -github.com/aliyun/credentials-go v1.4.3/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= +github.com/aliyun/credentials-go v1.4.5 h1:O76WYKgdy1oQYYiJkERjlA2dxGuvLRrzuO2ScrtGWSk= +github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -206,40 +213,40 @@ github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= -github.com/aws/aws-sdk-go-v2/config v1.29.5 h1:4lS2IB+wwkj5J43Tq/AwvnscBerBJtQQ6YS7puzCI1k= -github.com/aws/aws-sdk-go-v2/config v1.29.5/go.mod h1:SNzldMlDVbN6nWxM7XsUiNXPSa1LWlqiXtvh/1PrJGg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.58 h1:/d7FUpAPU8Lf2KUdjniQvfNdlMID0Sd9pS23FJ3SS9Y= -github.com/aws/aws-sdk-go-v2/credentials v1.17.58/go.mod h1:aVYW33Ow10CyMQGFgC0ptMRIqJWvJ4nxZb0sUiuQT/A= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 h1:KwsodFKVQTlI5EyhRSugALzsV6mG/SGrdjlMXSZSdso= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28/go.mod h1:EY3APf9MzygVhKuPXAc5H+MkGb8k/DOSQjWS0LgkKqI= +github.com/aws/aws-sdk-go-v2/config v1.29.9 h1:Kg+fAYNaJeGXp1vmjtidss8O2uXIsXwaRqsQJKXVr+0= +github.com/aws/aws-sdk-go-v2/config v1.29.9/go.mod h1:oU3jj2O53kgOU4TXq/yipt6ryiooYjlkqqVaZk7gY/U= +github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU= +github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/service/acm v1.31.1 h1:FB1PgU6vlXbqehxZiHuYQRWo5Ou6sQrFJcUaRe27lRo= -github.com/aws/aws-sdk-go-v2/service/acm v1.31.1/go.mod h1:3sKYAgRbuBa2QMYGh/WEclwnmfx+QoPhhX25PdSQSQM= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.45.2 h1:S3JpsBLyn/jqSJ6GgsbDQHubmop6fshQk/iOaOeotsc= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.45.2/go.mod h1:FIBJ48TS+qJb+Ne4qJ+0NeIhtPTVXItXooTeNeVI4Po= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/acm v1.31.3 h1:GwlU39usxM7E1LIhZchk93PtTQm2j3jb63of/YkBd+o= +github.com/aws/aws-sdk-go-v2/service/acm v1.31.3/go.mod h1:3sKYAgRbuBa2QMYGh/WEclwnmfx+QoPhhX25PdSQSQM= +github.com/aws/aws-sdk-go-v2/service/cloudfront v1.45.3 h1:xQnjN34F4I3a/I3Xj0g9vmD5hAqC7u5y3SC3eC6T1E8= +github.com/aws/aws-sdk-go-v2/service/cloudfront v1.45.3/go.mod h1:FIBJ48TS+qJb+Ne4qJ+0NeIhtPTVXItXooTeNeVI4Po= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13/go.mod h1:kizuDaLX37bG5WZaoxGPQR/LNFXpxp0vsUnqfkWXfNE= -github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1 h1:njgAP7Rtt4DGdTGFPhJ4gaZXCD1CDj/SZDa5W4ZgSTs= -github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1/go.mod h1:TN4PcCL0lvqmYcv+AV8iZFC4Sd0FM06QDaoBXrFEftU= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.15/go.mod h1:2PCJYpi7EKeA5SkStAmZlF6fi0uUABuhtF8ILHjGc3Y= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14/go.mod h1:RVwIw3y/IqxC2YEXSIkAzRDdEU1iRabDPaYjpGCbCGQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 h1:TzeR06UCMUq+KA3bDkujxK1GVGy+G8qQN/QVYzGLkQE= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98wpaTeqt5+DMidZD0A9BYTizc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= +github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 h1:/nkJHXtJXJeelXHqG0898+fWKgvfaXBhGzbCsSmn9j8= +github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 h1:KwuLovgQPcdjNMfFt9OhUd9a2OwcOKhxfvF4glTzLuA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= -github.com/baidubce/bce-sdk-go v0.9.221 h1:x5uTXND33m5TE3UBXYhlePuXcJi5rxNnBBt+bP7kPe0= -github.com/baidubce/bce-sdk-go v0.9.221/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= +github.com/baidubce/bce-sdk-go v0.9.224 h1:z2L8alGw/y3IUHjrLRyrxrgCvMssYTjgCd7OQdb4gt0= +github.com/baidubce/bce-sdk-go v0.9.224/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -247,8 +254,10 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blinkbean/dingtalk v1.1.3 h1:MbidFZYom7DTFHD/YIs+eaI7kRy52kmWE/sy0xjo6E4= github.com/blinkbean/dingtalk v1.1.3/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto= -github.com/byteplus-sdk/byteplus-sdk-golang v1.0.42 h1:Dm9FDjQP2SlAjUH7WAV7DFYDBFQz2uBxX6JULs0Sxs0= -github.com/byteplus-sdk/byteplus-sdk-golang v1.0.42/go.mod h1:CIL/T2dxgbIA79os+wl0Fq0vCbADTZNIddV6PNYB6DY= +github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= +github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= +github.com/byteplus-sdk/byteplus-sdk-golang v1.0.44 h1:men5pKZNho+cw9/YU7TFerTspS3lKayS64zctl/D7Fk= +github.com/byteplus-sdk/byteplus-sdk-golang v1.0.44/go.mod h1:CIL/T2dxgbIA79os+wl0Fq0vCbADTZNIddV6PNYB6DY= github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= @@ -271,8 +280,8 @@ github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.114.0 h1:ucoti4/7Exo0XQ+rzpn1H+IfVVe++zgiM+tyKtf0HUA= -github.com/cloudflare/cloudflare-go v0.114.0/go.mod h1:O7fYfFfA6wKqKFn2QIR9lhj7FDw6VQCGOY6hd2TBtd0= +github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM= +github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -290,6 +299,10 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/diskfs/go-diskfs v1.5.0 h1:0SANkrab4ifiZBytk380gIesYh5Gc+3i40l7qsrYP4s= +github.com/diskfs/go-diskfs v1.5.0/go.mod h1:bRFumZeGFCO8C2KNswrQeuj2m1WCVr4Ms5IjWMczMDk= +github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= +github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8= github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -328,16 +341,16 @@ github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXE github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw= -github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k= -github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE= +github.com/ganigeorgiev/fexpr v0.5.0 h1:XA9JxtTE/Xm+g/JFI6RfZEHSiQlk+1glLvRK1Lpv/Tk= +github.com/ganigeorgiev/fexpr v0.5.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-acme/lego/v4 v4.22.2 h1:ck+HllWrV/rZGeYohsKQ5iKNnU/WAZxwOdiu6cxky+0= -github.com/go-acme/lego/v4 v4.22.2/go.mod h1:E2FndyI3Ekv0usNJt46mFb9LVpV/XBYT+4E3tz02Tzo= +github.com/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV4= +github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= -github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= @@ -388,8 +401,8 @@ github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= -github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= @@ -399,8 +412,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -470,8 +483,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20250315033105-103756e64e1d h1:tx51Lf+wdE+aavqH8TcPJoCjTf4cE8hrMzROghCely0= -github.com/google/pprof v0.0.0-20250315033105-103756e64e1d/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -487,6 +500,8 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= @@ -498,6 +513,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -537,6 +554,8 @@ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJk github.com/jdcloud-api/jdcloud-sdk-go v1.64.0 h1:xZc/ZRcrOhDx9Ra9htu6ui2gUUttmLsXIqH61LcvY4U= github.com/jdcloud-api/jdcloud-sdk-go v1.64.0/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jinzhu/copier v0.3.4 h1:mfU6jI9PtCeUjkjQ322dlff9ELjGDu975C2p/nrubVI= +github.com/jinzhu/copier v0.3.4/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -560,8 +579,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= +github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= @@ -593,6 +612,10 @@ github.com/libdns/dynv6 v1.0.0/go.mod h1:65PL/bAlyH0J+0WGlOJYnMpoIuXcg/FmW4dTBYW github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8= github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= +github.com/luthermonson/go-proxmox v0.2.2 h1:BZ7VEj302wxw2i/EwTcyEiBzQib8teocB2SSkLHyySY= +github.com/luthermonson/go-proxmox v0.2.2/go.mod h1:oyFgg2WwTEIF0rP6ppjiixOHa5ebK1p8OaRiFhvICBQ= +github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= +github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -613,8 +636,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= -github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ= +github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= @@ -649,6 +672,8 @@ github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nikoksr/notify v1.3.0 h1:UxzfxzAYGQD9a5JYLBTVx0lFMxeHCke3rPCkfWdPgLs= github.com/nikoksr/notify v1.3.0/go.mod h1:Xor2hMmkvrCfkCKvXGbcrESez4brac2zQjhd6U2BbeM= +github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 h1:ouZ2JWDl8IW5k1qugYbmpbmW8hn85Ig6buSMBRlz3KI= +github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3/go.mod h1:ZwadWt7mVhMHMbAQ1w8IhDqtWO3eWqWq72W7trnaiE8= github.com/nrdcg/desec v0.10.0 h1:qrEDiqnsvNU9QE7lXIXi/tIHAfyaFXKxF2/8/52O8uM= github.com/nrdcg/desec v0.10.0/go.mod h1:5+4vyhMRTs49V9CNoODF/HwT8Mwxv9DJ6j+7NekUnBs= github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk= @@ -694,15 +719,15 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.13.8 h1:Xt7eJ/xqXv7s0VuzFw7JXhZj6Oc1zI6l4GK8KP9sFB0= -github.com/pkg/sftp v1.13.8/go.mod h1:DmvEkvKE2lshEeuo2JMp06yqcx9HVnR7e3zqQl42F3U= +github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw= +github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pocketbase/dbx v1.11.0 h1:LpZezioMfT3K4tLrqA55wWFw1EtH1pM4tzSVa7kgszU= github.com/pocketbase/dbx v1.11.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs= -github.com/pocketbase/pocketbase v0.26.1 h1:0WBqIRKKPCqp+xHPVLB4fevkoT9HVlR4BSuNwAt5oJ0= -github.com/pocketbase/pocketbase v0.26.1/go.mod h1:t5y5pfnhrEg//RuSzSg0a926OLZ0oQj66jYs3BzDJwA= +github.com/pocketbase/pocketbase v0.27.1 h1:KGCsS8idUVTC5QHxTj91qHDhIXOb5Yb50wwHhNvJRTQ= +github.com/pocketbase/pocketbase v0.27.1/go.mod h1:aTpwwloVJzeJ7MlwTRrbI/x62QNR2/kkCrovmyrXpqs= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/povsister/scp v0.0.0-20240802064259-28781e87b246 h1:c4D8BPWLOxxdaxQLfLKQXH2YXY/E9yo3jrDSL54XrTw= @@ -729,14 +754,14 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/qiniu/dyn v1.3.0 h1:s+xPTeV0H8yikgM4ZMBc7Rrefam8UNI3asBlkaOQg5o= github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk= -github.com/qiniu/go-sdk/v7 v7.25.2 h1:URwgZpxySdiwu2yQpHk93X4LXWHyFRp1x3Vmlk/YWvo= -github.com/qiniu/go-sdk/v7 v7.25.2/go.mod h1:dmKtJ2ahhPWFVi9o1D5GemmWoh/ctuB9peqTowyTO8o= +github.com/qiniu/go-sdk/v7 v7.25.3 h1:eYHh02q4i5MrlEn3qy823w7moieymFzb4dsP38Y43AI= +github.com/qiniu/go-sdk/v7 v7.25.3/go.mod h1:dmKtJ2ahhPWFVi9o1D5GemmWoh/ctuB9peqTowyTO8o= github.com/qiniu/x v1.10.5 h1:7V/CYWEmo9axJULvrJN6sMYh2FdY+esN5h8jwDkA4b0= github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= -github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -758,6 +783,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0= +github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -793,32 +820,32 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1115 h1:HsrXyqKQB2mKfGq+ZkbylRCMrbtPCmmUBrwA8MhhEX0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1115/go.mod h1:5cz1DtLlXK98U1Hh36oW4PjVOU+mbKg5wtCDmCc9Fcs= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1127 h1:PiLZflqaW0690YsqIM/hqaVYjZJ3+cCJp4NHfw7h/uw= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1127/go.mod h1:V1+julLUOH0jKoVH6o6xgM4STWowzAL57M4VanUEEag= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1115/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1117/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1136 h1:H1pjtH5uZ4XZPj9qQ9tt9jzeWqZzrd8qYIw01Q60/08= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1136/go.mod h1:K6absuzpElv6mw2d7j8xkphOkwd23qvG0Rcmhl4rqlk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1143 h1:7OL/ThUCqkntItSiqbY1g3s0Ua26Qr08G8fcSzyrAqA= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1143/go.mod h1:XO18PkKinF17cQOSlhbP7GOnj04N5L2iCaHn64yiMtE= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1120/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1124/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1125/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1126/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1127 h1:1uG8zc0b9gLbyTr27T0CzGtcdrL86CGGJ6Flkq867f4= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1127/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 h1:kwctN0WQYt8/iKP+iRCTCwdzEMIXsXklbRIib5rjeQ8= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084/go.mod h1:qE67ApiBzeRvzeDsV+GxyIDbVIDemsKpHXllQATz/Vw= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1117 h1:Yc/r5zUAyukVI3huIuwE7koowCjDjOWqeRpBILCvOSE= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1117/go.mod h1:YxsorHl3sTRw+2GsUObMqcumDqAQ3zo9rLMtf3Cxj8U= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1115 h1:Qi7VWmJ0AQxEMlwKpbWfnsLA5QdNxekdcLJTBVdO85U= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1115/go.mod h1:P16nIMvmpSY+arTc2m2HyJmrYQP6CFnr48glz0+abyw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1136/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1138/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1143/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1147 h1:6v559jM1v6A4KJinNZ28RqVZs+ipKMzCWtYWcWy+zZ4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1147/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 h1:mrJ5Fbkd7sZIJ5F6oRfh5zebPQaudPH9Y0+GUmFytYU= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128/go.mod h1:zbsYIBT+VTX4z4ocjTAdLBIWyNYj3z0BRqd0iPdnjsk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1143 h1:fvK9kOsPquDTWrT2aXLWVnAMUokr4gFK7uYeY3JMB6U= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1143/go.mod h1:SLYgasv8DdvRnesG+SLdqFdEBIJzietfVDytke8ASKQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120 h1:z0t0lb5h1mZirXftO8MRg25COYZHx0ubQjSPhZT/LY0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120/go.mod h1:IFZL44Keyl+MHrhpFwUaQmJvMDwGr+t+cUfFAC+74lU= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124 h1:LQKAlxFb0sYiE8ojK5h9+seuFzogoJtYnXmiRF+4F4Q= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124/go.mod h1:tYbK0FbHVG+78od7eZpzczE8qk0JWKO/osTQWuiJ3Fo= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1126 h1:+CJQNXLdLP0GLaz2fnPECQsU+WdOmW3BQ54cNoQgMKA= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1126/go.mod h1:eY3GoWilNoCPOEw2Lp4o+h02nEfc+BoZnqK3TlK6F7I= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1115 h1:rO0LdbcNtT5VlL9sB/K3Ve848uLp1rgg3R8igT9xsFQ= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1115/go.mod h1:jJR8Y5sHuujSXZy0cpCgBk180TvPNsLw9hEoSH9w7iA= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1125 h1:IR9pJqHjHr7KyncRVxld9iltfnmy9sCC+0USZrs3rOw= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1125/go.mod h1:5+5QrF7x+AW1KPM7F+YRzD74L88RXHZ6BxDF07b8QkE= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1138 h1:SrQ+rlWLwnXU/6S8ULGhFaiV5faAeqL0ysdsqV6P1AA= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1138/go.mod h1:XvXgF+4yO4Ni6gYoqMszSkNNqFLkOxx2j5F7+u3lpKQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1136 h1:9GqM1URHNySj0f8TkUcKT6qSDiGep3IB1hWWu1ti6rY= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1136/go.mod h1:b5JZEbM4ROYUSVcgNkDHuHWdTJX5Qe4wC1asq2n0yes= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1147 h1:SxZsn9N4c1yx40kZOINIh9AnUKcgChUWbZoDiv6VvmQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1147/go.mod h1:T4sxG9+SJ038MBsam2upsEYRpQ82JpX+IkZ08+P9RlE= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= @@ -827,16 +854,16 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ucloud/ucloud-sdk-go v0.22.31 h1:izZK+Re9ZkJAd1fHSVpFzgh8uKda4f5G6++iUw4n/mE= -github.com/ucloud/ucloud-sdk-go v0.22.31/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw= +github.com/ucloud/ucloud-sdk-go v0.22.33 h1:YKY8VpFNttdnVNb0o3owGeZRoUtRJmoWPJYJPfcCf9A= +github.com/ucloud/ucloud-sdk-go v0.22.33/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.9 h1:fEnScn2dXfvfNcFnvJnpf/cYdj8kLIe5QC5qORlFO2c= -github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.9/go.mod h1:IrjK84IJJTuOZOTMv/P18Ydjy/x+ow7fF7q11jAxXLM= +github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.11 h1:J4AweXxLqlSwb1Aam9npcb5optZmszDIrKWa/hs+e4U= +github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.11/go.mod h1:IrjK84IJJTuOZOTMv/P18Ydjy/x+ow7fF7q11jAxXLM= github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU= -github.com/volcengine/volc-sdk-golang v1.0.199 h1:zv9QOqTl/IsLwtfC37GlJtcz6vMAHi+pjq8ILWjLYUc= -github.com/volcengine/volc-sdk-golang v1.0.199/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= -github.com/volcengine/volcengine-go-sdk v1.0.187 h1:YpZjydoyHDA/ofnF6mYCelbOoo9pJsBEiQOOSJzGSOY= -github.com/volcengine/volcengine-go-sdk v1.0.187/go.mod h1:gfEDc1s7SYaGoY+WH2dRrS3qiuDJMkwqyfXWCa7+7oA= +github.com/volcengine/volc-sdk-golang v1.0.204 h1:Njid6coReHV2gWc3bsqWMQf+K8jveauzW8zEX08CTzI= +github.com/volcengine/volc-sdk-golang v1.0.204/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= +github.com/volcengine/volcengine-go-sdk v1.1.4 h1:xPT4KOy8VkXxhY7dbXzzvLvKQXUe4J6AtkQdNQU3wRY= +github.com/volcengine/volcengine-go-sdk v1.1.4/go.mod h1:gfEDc1s7SYaGoY+WH2dRrS3qiuDJMkwqyfXWCa7+7oA= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -904,8 +931,8 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -919,14 +946,14 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= -golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= +golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= +golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1009,16 +1036,16 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= +golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1036,8 +1063,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1090,6 +1117,7 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1098,6 +1126,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1114,8 +1143,8 @@ golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1131,8 +1160,8 @@ golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1151,15 +1180,15 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1214,8 +1243,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1367,27 +1396,27 @@ k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8X k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= -modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo= -modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo= +modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic= +modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU= +modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw= modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw= -modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= -modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s= +modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= -modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g= +modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ= -modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= +modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= +modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/internal/applicant/acme_ca.go b/internal/applicant/acme_ca.go index 57f24b6e..67b7693e 100644 --- a/internal/applicant/acme_ca.go +++ b/internal/applicant/acme_ca.go @@ -1,38 +1,30 @@ -package applicant +package applicant + +import "github.com/usual2970/certimate/internal/domain" const ( - sslProviderLetsEncrypt = "letsencrypt" - sslProviderLetsEncryptStaging = "letsencrypt_staging" - sslProviderZeroSSL = "zerossl" - sslProviderGoogleTrustServices = "gts" -) -const defaultSSLProvider = sslProviderLetsEncrypt + sslProviderLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt) + sslProviderLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging) + sslProviderBuypass = string(domain.CAProviderTypeBuypass) + sslProviderGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices) + sslProviderSSLCom = string(domain.CAProviderTypeSSLCom) + sslProviderZeroSSL = string(domain.CAProviderTypeZeroSSL) -const ( - letsencryptUrl = "https://acme-v02.api.letsencrypt.org/directory" - letsencryptStagingUrl = "https://acme-staging-v02.api.letsencrypt.org/directory" - zerosslUrl = "https://acme.zerossl.com/v2/DV90" - gtsUrl = "https://dv.acme-v02.api.pki.goog/directory" + sslProviderDefault = sslProviderLetsEncrypt ) var sslProviderUrls = map[string]string{ - sslProviderLetsEncrypt: letsencryptUrl, - sslProviderLetsEncryptStaging: letsencryptStagingUrl, - sslProviderZeroSSL: zerosslUrl, - sslProviderGoogleTrustServices: gtsUrl, + sslProviderLetsEncrypt: "https://acme-v02.api.letsencrypt.org/directory", + sslProviderLetsEncryptStaging: "https://acme-staging-v02.api.letsencrypt.org/directory", + sslProviderBuypass: "https://api.buypass.com/acme/directory", + sslProviderGoogleTrustServices: "https://dv.acme-v02.api.pki.goog/directory", + sslProviderSSLCom: "https://acme.ssl.com/sslcom-dv-rsa", + sslProviderSSLCom + "RSA": "https://acme.ssl.com/sslcom-dv-rsa", + sslProviderSSLCom + "ECC": "https://acme.ssl.com/sslcom-dv-ecc", + sslProviderZeroSSL: "https://acme.zerossl.com/v2/DV90", } type acmeSSLProviderConfig struct { - Config acmeSSLProviderConfigContent `json:"config"` - Provider string `json:"provider"` -} - -type acmeSSLProviderConfigContent struct { - ZeroSSL acmeSSLProviderEabConfig `json:"zerossl"` - GoogleTrustServices acmeSSLProviderEabConfig `json:"gts"` -} - -type acmeSSLProviderEabConfig struct { - EabHmacKey string `json:"eabHmacKey"` - EabKid string `json:"eabKid"` + Config map[domain.CAProviderType]map[string]any `json:"config"` + Provider string `json:"provider"` } diff --git a/internal/applicant/acme_user.go b/internal/applicant/acme_user.go index 107e417c..29ac80cd 100644 --- a/internal/applicant/acme_user.go +++ b/internal/applicant/acme_user.go @@ -1,4 +1,4 @@ -package applicant +package applicant import ( "context" @@ -13,7 +13,8 @@ import ( "golang.org/x/sync/singleflight" "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" + maputil "github.com/usual2970/certimate/internal/pkg/utils/map" "github.com/usual2970/certimate/internal/repository" ) @@ -76,16 +77,11 @@ func (u *acmeUser) getPrivateKeyPEM() string { return u.privkey } -type acmeAccountRepository interface { - GetByCAAndEmail(ca, email string) (*domain.AcmeAccount, error) - Save(ca, email, key string, resource *registration.Resource) error -} - var registerGroup singleflight.Group -func registerAcmeUserWithSingleFlight(client *lego.Client, sslProviderConfig *acmeSSLProviderConfig, user *acmeUser) (*registration.Resource, error) { - resp, err, _ := registerGroup.Do(fmt.Sprintf("register_acme_user_%s_%s", sslProviderConfig.Provider, user.GetEmail()), func() (interface{}, error) { - return registerAcmeUser(client, sslProviderConfig, user) +func registerAcmeUserWithSingleFlight(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) { + resp, err, _ := registerGroup.Do(fmt.Sprintf("register_acme_user_%s_%s", user.CA, user.Email), func() (interface{}, error) { + return registerAcmeUser(client, user, userRegisterOptions) }) if err != nil { @@ -95,45 +91,81 @@ func registerAcmeUserWithSingleFlight(client *lego.Client, sslProviderConfig *ac return resp.(*registration.Resource), nil } -func registerAcmeUser(client *lego.Client, sslProviderConfig *acmeSSLProviderConfig, user *acmeUser) (*registration.Resource, error) { +func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) { var reg *registration.Resource var err error - switch sslProviderConfig.Provider { - case sslProviderZeroSSL: - reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: true, - Kid: sslProviderConfig.Config.ZeroSSL.EabKid, - HmacEncoded: sslProviderConfig.Config.ZeroSSL.EabHmacKey, - }) - case sslProviderGoogleTrustServices: - reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: true, - Kid: sslProviderConfig.Config.GoogleTrustServices.EabKid, - HmacEncoded: sslProviderConfig.Config.GoogleTrustServices.EabHmacKey, - }) + switch user.CA { case sslProviderLetsEncrypt, sslProviderLetsEncryptStaging: reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + + case sslProviderBuypass: + { + reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + } + + case sslProviderGoogleTrustServices: + { + access := domain.AccessConfigForGoogleTrustServices{} + if err := maputil.Populate(userRegisterOptions, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: true, + Kid: access.EabKid, + HmacEncoded: access.EabHmacKey, + }) + } + + case sslProviderSSLCom: + { + access := domain.AccessConfigForSSLCom{} + if err := maputil.Populate(userRegisterOptions, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: true, + Kid: access.EabKid, + HmacEncoded: access.EabHmacKey, + }) + } + + case sslProviderZeroSSL: + { + access := domain.AccessConfigForZeroSSL{} + if err := maputil.Populate(userRegisterOptions, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: true, + Kid: access.EabKid, + HmacEncoded: access.EabHmacKey, + }) + } + default: - err = fmt.Errorf("unsupported ssl provider: %s", sslProviderConfig.Provider) + err = fmt.Errorf("unsupported ca provider '%s'", user.CA) } if err != nil { return nil, err } repo := repository.NewAcmeAccountRepository() - resp, err := repo.GetByCAAndEmail(sslProviderConfig.Provider, user.GetEmail()) + resp, err := repo.GetByCAAndEmail(user.CA, user.Email) if err == nil { user.privkey = resp.Key return resp.Resource, nil } if _, err := repo.Save(context.Background(), &domain.AcmeAccount{ - CA: sslProviderConfig.Provider, - Email: user.GetEmail(), + CA: user.CA, + Email: user.Email, Key: user.getPrivateKeyPEM(), Resource: reg, }); err != nil { - return nil, fmt.Errorf("failed to save registration: %w", err) + return nil, fmt.Errorf("failed to save acme account registration: %w", err) } return reg, nil diff --git a/internal/applicant/applicant.go b/internal/applicant/applicant.go index 8cd8b6d0..e9ed4cb1 100644 --- a/internal/applicant/applicant.go +++ b/internal/applicant/applicant.go @@ -4,10 +4,12 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "os" "strconv" "strings" "sync" + "time" "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certificate" @@ -18,11 +20,11 @@ import ( "golang.org/x/time/rate" "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/utils/sliceutil" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" "github.com/usual2970/certimate/internal/repository" ) -type ApplyCertResult struct { +type ApplyResult struct { CertificateFullChain string IssuerCertificate string PrivateKey string @@ -33,51 +35,78 @@ type ApplyCertResult struct { } type Applicant interface { - Apply() (*ApplyCertResult, error) + Apply(ctx context.Context) (*ApplyResult, error) } -type applicantOptions struct { - Domains []string - ContactEmail string - Provider domain.ApplyDNSProviderType - ProviderAccessConfig map[string]any - ProviderApplyConfig map[string]any - KeyAlgorithm string - Nameservers []string - DnsPropagationTimeout int32 - DnsTTL int32 - DisableFollowCNAME bool - ReplacedARIAcctId string - ReplacedARICertId string +type ApplicantWithWorkflowNodeConfig struct { + Node *domain.WorkflowNode + Logger *slog.Logger } -func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) { - if node.Type != domain.WorkflowNodeTypeApply { - return nil, fmt.Errorf("node type is not apply") +func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, error) { + if config.Node == nil { + return nil, fmt.Errorf("node is nil") + } + if config.Node.Type != domain.WorkflowNodeTypeApply { + return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeApply)) } - nodeConfig := node.GetConfigForApply() - options := &applicantOptions{ - Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }), - ContactEmail: nodeConfig.ContactEmail, - Provider: domain.ApplyDNSProviderType(nodeConfig.Provider), - ProviderApplyConfig: nodeConfig.ProviderConfig, - KeyAlgorithm: nodeConfig.KeyAlgorithm, - Nameservers: sliceutil.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }), - DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout, - DnsTTL: nodeConfig.DnsTTL, - DisableFollowCNAME: nodeConfig.DisableFollowCNAME, + nodeConfig := config.Node.GetConfigForApply() + options := &applicantProviderOptions{ + Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }), + ContactEmail: nodeConfig.ContactEmail, + Provider: domain.ACMEDns01ProviderType(nodeConfig.Provider), + ProviderAccessConfig: make(map[string]any), + ProviderExtendedConfig: nodeConfig.ProviderConfig, + CAProvider: domain.CAProviderType(nodeConfig.CAProvider), + CAProviderAccessConfig: make(map[string]any), + CAProviderExtendedConfig: nodeConfig.CAProviderConfig, + KeyAlgorithm: nodeConfig.KeyAlgorithm, + Nameservers: sliceutil.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }), + DnsPropagationWait: nodeConfig.DnsPropagationWait, + DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout, + DnsTTL: nodeConfig.DnsTTL, + DisableFollowCNAME: nodeConfig.DisableFollowCNAME, } accessRepo := repository.NewAccessRepository() - if access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId); err != nil { - return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err) - } else { - options.ProviderAccessConfig = access.Config + if nodeConfig.ProviderAccessId != "" { + if access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId); err != nil { + return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err) + } else { + options.ProviderAccessConfig = access.Config + } + } + if nodeConfig.CAProviderAccessId != "" { + if access, err := accessRepo.GetById(context.Background(), nodeConfig.CAProviderAccessId); err != nil { + return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.CAProviderAccessId, err) + } else { + options.CAProviderAccessConfig = access.Config + } + } + + settingsRepo := repository.NewSettingsRepository() + if string(options.CAProvider) == "" { + settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider") + + sslProviderConfig := &acmeSSLProviderConfig{ + Config: make(map[domain.CAProviderType]map[string]any), + Provider: sslProviderDefault, + } + if settings != nil { + if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil { + return nil, err + } else if sslProviderConfig.Provider == "" { + sslProviderConfig.Provider = sslProviderDefault + } + } + + options.CAProvider = domain.CAProviderType(sslProviderConfig.Provider) + options.CAProviderAccessConfig = sslProviderConfig.Config[options.CAProvider] } certRepo := repository.NewCertificateRepository() - lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), node.Id) + lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), config.Node.Id) if lastCertificate != nil { newCertSan := slices.Clone(options.Domains) oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";") @@ -88,42 +117,53 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) { lastCertX509, _ := certcrypto.ParsePEMCertificate([]byte(lastCertificate.Certificate)) if lastCertX509 != nil { replacedARICertId, _ := certificate.MakeARICertID(lastCertX509) - options.ReplacedARIAcctId = lastCertificate.ACMEAccountUrl - options.ReplacedARICertId = replacedARICertId + options.ReplacedARIAcct = lastCertificate.ACMEAccountUrl + options.ReplacedARICert = replacedARICertId } } } - applicant, err := createApplicant(options) + applicant, err := createApplicantProvider(options) if err != nil { return nil, err } - return &proxyApplicant{ + return &applicantImpl{ applicant: applicant, options: options, }, nil } -func apply(challengeProvider challenge.Provider, options *applicantOptions) (*ApplyCertResult, error) { - settingsRepo := repository.NewSettingsRepository() - settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider") +type applicantImpl struct { + applicant challenge.Provider + options *applicantProviderOptions +} - sslProviderConfig := &acmeSSLProviderConfig{ - Config: acmeSSLProviderConfigContent{}, - Provider: defaultSSLProvider, - } - if settings != nil { - if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil { - return nil, err - } +var _ Applicant = (*applicantImpl)(nil) + +func (d *applicantImpl) Apply(ctx context.Context) (*ApplyResult, error) { + limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail)) + if err := limiter.Wait(ctx); err != nil { + return nil, err } - if sslProviderConfig.Provider == "" { - sslProviderConfig.Provider = defaultSSLProvider - } + return applyUseLego(d.applicant, d.options) +} - acmeUser, err := newAcmeUser(sslProviderConfig.Provider, options.ContactEmail) +const ( + limitBurst = 300 + limitRate float64 = float64(1) / float64(36) +) + +var limiters sync.Map + +func getLimiter(key string) *rate.Limiter { + limiter, _ := limiters.LoadOrStore(key, rate.NewLimiter(rate.Limit(limitRate), 300)) + return limiter.(*rate.Limiter) +} + +func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOptions) (*ApplyResult, error) { + user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail) if err != nil { return nil, err } @@ -133,9 +173,16 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", strconv.FormatBool(options.DisableFollowCNAME)) // Create an ACME client config - config := lego.NewConfig(acmeUser) - config.CADirURL = sslProviderUrls[sslProviderConfig.Provider] - config.Certificate.KeyType = parseKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm)) + config := lego.NewConfig(user) + config.Certificate.KeyType = parseLegoKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm)) + config.CADirURL = sslProviderUrls[user.CA] + if user.CA == sslProviderSSLCom { + if strings.HasPrefix(options.KeyAlgorithm, "RSA") { + config.CADirURL = sslProviderUrls[sslProviderSSLCom+"RSA"] + } else if strings.HasPrefix(options.KeyAlgorithm, "EC") { + config.CADirURL = sslProviderUrls[sslProviderSSLCom+"ECC"] + } + } // Create an ACME client client, err := lego.NewClient(config) @@ -144,20 +191,28 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap } // Set the DNS01 challenge provider - challengeOptions := make([]dns01.ChallengeOption, 0) - if len(options.Nameservers) > 0 { - challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers))) - challengeOptions = append(challengeOptions, dns01.DisableAuthoritativeNssPropagationRequirement()) - } - client.Challenge.SetDNS01Provider(challengeProvider, challengeOptions...) + client.Challenge.SetDNS01Provider(legoProvider, + dns01.CondOption( + len(options.Nameservers) > 0, + dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers)), + ), + dns01.CondOption( + options.DnsPropagationWait > 0, + dns01.PropagationWait(time.Duration(options.DnsPropagationWait)*time.Second, true), + ), + dns01.CondOption( + len(options.Nameservers) > 0 || options.DnsPropagationWait > 0, + dns01.DisableAuthoritativeNssPropagationRequirement(), + ), + ) // New users need to register first - if !acmeUser.hasRegistration() { - reg, err := registerAcmeUserWithSingleFlight(client, sslProviderConfig, acmeUser) + if !user.hasRegistration() { + reg, err := registerAcmeUserWithSingleFlight(client, user, options.CAProviderAccessConfig) if err != nil { - return nil, fmt.Errorf("failed to register: %w", err) + return nil, fmt.Errorf("failed to register acme user: %w", err) } - acmeUser.Registration = reg + user.Registration = reg } // Obtain a certificate @@ -165,64 +220,39 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap Domains: options.Domains, Bundle: true, } - if options.ReplacedARICertId != "" && options.ReplacedARIAcctId != acmeUser.Registration.URI { - certRequest.ReplacesCertID = options.ReplacedARICertId + if options.ReplacedARIAcct == user.Registration.URI { + certRequest.ReplacesCertID = options.ReplacedARICert } certResource, err := client.Certificate.Obtain(certRequest) if err != nil { return nil, err } - return &ApplyCertResult{ + return &ApplyResult{ CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)), IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)), PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)), - ACMEAccountUrl: acmeUser.Registration.URI, + ACMEAccountUrl: user.Registration.URI, ACMECertUrl: certResource.CertURL, ACMECertStableUrl: certResource.CertStableURL, CSR: strings.TrimSpace(string(certResource.CSR)), }, nil } -func parseKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType { - switch algo { - case domain.CertificateKeyAlgorithmTypeRSA2048: - return certcrypto.RSA2048 - case domain.CertificateKeyAlgorithmTypeRSA3072: - return certcrypto.RSA3072 - case domain.CertificateKeyAlgorithmTypeRSA4096: - return certcrypto.RSA4096 - case domain.CertificateKeyAlgorithmTypeRSA8192: - return certcrypto.RSA8192 - case domain.CertificateKeyAlgorithmTypeEC256: - return certcrypto.EC256 - case domain.CertificateKeyAlgorithmTypeEC384: - return certcrypto.EC384 +func parseLegoKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType { + alogMap := map[domain.CertificateKeyAlgorithmType]certcrypto.KeyType{ + domain.CertificateKeyAlgorithmTypeRSA2048: certcrypto.RSA2048, + domain.CertificateKeyAlgorithmTypeRSA3072: certcrypto.RSA3072, + domain.CertificateKeyAlgorithmTypeRSA4096: certcrypto.RSA4096, + domain.CertificateKeyAlgorithmTypeRSA8192: certcrypto.RSA8192, + domain.CertificateKeyAlgorithmTypeEC256: certcrypto.EC256, + domain.CertificateKeyAlgorithmTypeEC384: certcrypto.EC384, + domain.CertificateKeyAlgorithmTypeEC512: certcrypto.KeyType("P512"), + } + + if keyType, ok := alogMap[algo]; ok { + return keyType } return certcrypto.RSA2048 } - -// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑 -type proxyApplicant struct { - applicant challenge.Provider - options *applicantOptions -} - -var limiters sync.Map - -const ( - limitBurst = 300 - limitRate float64 = float64(1) / float64(36) -) - -func getLimiter(key string) *rate.Limiter { - limiter, _ := limiters.LoadOrStore(key, rate.NewLimiter(rate.Limit(limitRate), 300)) - return limiter.(*rate.Limiter) -} - -func (d *proxyApplicant) Apply() (*ApplyCertResult, error) { - limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail)) - limiter.Wait(context.Background()) - return apply(d.applicant, d.options) -} diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index 90e8a972..454f9376 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -8,9 +8,11 @@ import ( "github.com/usual2970/certimate/internal/domain" pACMEHttpReq "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq" pAliyun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun" + pAliyunESA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa" pAWSRoute53 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53" pAzureDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns" pBaiduCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud" + pBunny "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/bunny" pCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare" pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns" pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud" @@ -34,16 +36,35 @@ import ( pVercel "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/vercel" pVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine" pWestcn "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn" - "github.com/usual2970/certimate/internal/pkg/utils/maputil" + maputil "github.com/usual2970/certimate/internal/pkg/utils/map" ) -func createApplicant(options *applicantOptions) (challenge.Provider, error) { +type applicantProviderOptions struct { + Domains []string + ContactEmail string + Provider domain.ACMEDns01ProviderType + ProviderAccessConfig map[string]any + ProviderExtendedConfig map[string]any + CAProvider domain.CAProviderType + CAProviderAccessConfig map[string]any + CAProviderExtendedConfig map[string]any + KeyAlgorithm string + Nameservers []string + DnsPropagationWait int32 + DnsPropagationTimeout int32 + DnsTTL int32 + DisableFollowCNAME bool + ReplacedARIAcct string + ReplacedARICert string +} + +func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) { /* 注意:如果追加新的常量值,请保持以 ASCII 排序。 NOTICE: If you add new constant, please keep ASCII order. */ switch options.Provider { - case domain.ApplyDNSProviderTypeACMEHttpReq: + case domain.ACMEDns01ProviderTypeACMEHttpReq: { access := domain.AccessConfigForACMEHttpReq{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -60,23 +81,39 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeAliyun, domain.ApplyDNSProviderTypeAliyunDNS: + case domain.ACMEDns01ProviderTypeAliyun, domain.ACMEDns01ProviderTypeAliyunDNS, domain.ACMEDns01ProviderTypeAliyunESA: { access := domain.AccessConfigForAliyun{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := pAliyun.NewChallengeProvider(&pAliyun.ChallengeProviderConfig{ - AccessKeyId: access.AccessKeyId, - AccessKeySecret: access.AccessKeySecret, - DnsPropagationTimeout: options.DnsPropagationTimeout, - DnsTTL: options.DnsTTL, - }) - return applicant, err + switch options.Provider { + case domain.ACMEDns01ProviderTypeAliyun, domain.ACMEDns01ProviderTypeAliyunDNS: + applicant, err := pAliyun.NewChallengeProvider(&pAliyun.ChallengeProviderConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + + case domain.ACMEDns01ProviderTypeAliyunESA: + applicant, err := pAliyunESA.NewChallengeProvider(&pAliyunESA.ChallengeProviderConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + + default: + break + } } - case domain.ApplyDNSProviderTypeAWS, domain.ApplyDNSProviderTypeAWSRoute53: + case domain.ACMEDns01ProviderTypeAWS, domain.ACMEDns01ProviderTypeAWSRoute53: { access := domain.AccessConfigForAWS{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -86,15 +123,15 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderApplyConfig, "region"), - HostedZoneId: maputil.GetString(options.ProviderApplyConfig, "hostedZoneId"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + HostedZoneId: maputil.GetString(options.ProviderExtendedConfig, "hostedZoneId"), DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) return applicant, err } - case domain.ApplyDNSProviderTypeAzure, domain.ApplyDNSProviderTypeAzureDNS: + case domain.ACMEDns01ProviderTypeAzure, domain.ACMEDns01ProviderTypeAzureDNS: { access := domain.AccessConfigForAzure{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -112,7 +149,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeBaiduCloud, domain.ApplyDNSProviderTypeBaiduCloudDNS: + case domain.ACMEDns01ProviderTypeBaiduCloud, domain.ACMEDns01ProviderTypeBaiduCloudDNS: { access := domain.AccessConfigForBaiduCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -128,7 +165,22 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeCloudflare: + case domain.ACMEDns01ProviderTypeBunny: + { + access := domain.AccessConfigForBunny{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pBunny.NewChallengeProvider(&pBunny.ChallengeProviderConfig{ + ApiKey: access.ApiKey, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + + case domain.ACMEDns01ProviderTypeCloudflare: { access := domain.AccessConfigForCloudflare{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -137,13 +189,14 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { applicant, err := pCloudflare.NewChallengeProvider(&pCloudflare.ChallengeProviderConfig{ DnsApiToken: access.DnsApiToken, + ZoneApiToken: access.ZoneApiToken, DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) return applicant, err } - case domain.ApplyDNSProviderTypeClouDNS: + case domain.ACMEDns01ProviderTypeClouDNS: { access := domain.AccessConfigForClouDNS{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -159,7 +212,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeCMCCCloud: + case domain.ACMEDns01ProviderTypeCMCCCloud: { access := domain.AccessConfigForCMCCCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -175,7 +228,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeDeSEC: + case domain.ACMEDns01ProviderTypeDeSEC: { access := domain.AccessConfigForDeSEC{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -190,7 +243,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeDNSLA: + case domain.ACMEDns01ProviderTypeDNSLA: { access := domain.AccessConfigForDNSLA{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -206,7 +259,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeDynv6: + case domain.ACMEDns01ProviderTypeDynv6: { access := domain.AccessConfigForDynv6{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -221,7 +274,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeGcore: + case domain.ACMEDns01ProviderTypeGcore: { access := domain.AccessConfigForGcore{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -236,7 +289,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeGname: + case domain.ACMEDns01ProviderTypeGname: { access := domain.AccessConfigForGname{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -252,7 +305,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeGoDaddy: + case domain.ACMEDns01ProviderTypeGoDaddy: { access := domain.AccessConfigForGoDaddy{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -268,7 +321,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeHuaweiCloud, domain.ApplyDNSProviderTypeHuaweiCloudDNS: + case domain.ACMEDns01ProviderTypeHuaweiCloud, domain.ACMEDns01ProviderTypeHuaweiCloudDNS: { access := domain.AccessConfigForHuaweiCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -278,14 +331,14 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderApplyConfig, "region"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) return applicant, err } - case domain.ApplyDNSProviderTypeJDCloud, domain.ApplyDNSProviderTypeJDCloudDNS: + case domain.ACMEDns01ProviderTypeJDCloud, domain.ACMEDns01ProviderTypeJDCloudDNS: { access := domain.AccessConfigForJDCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -295,14 +348,14 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - RegionId: maputil.GetString(options.ProviderApplyConfig, "regionId"), + RegionId: maputil.GetString(options.ProviderExtendedConfig, "regionId"), DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) return applicant, err } - case domain.ApplyDNSProviderTypeNamecheap: + case domain.ACMEDns01ProviderTypeNamecheap: { access := domain.AccessConfigForNamecheap{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -318,7 +371,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeNameDotCom: + case domain.ACMEDns01ProviderTypeNameDotCom: { access := domain.AccessConfigForNameDotCom{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -334,7 +387,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeNameSilo: + case domain.ACMEDns01ProviderTypeNameSilo: { access := domain.AccessConfigForNameSilo{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -349,7 +402,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeNS1: + case domain.ACMEDns01ProviderTypeNS1: { access := domain.AccessConfigForNS1{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -364,7 +417,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypePorkbun: + case domain.ACMEDns01ProviderTypePorkbun: { access := domain.AccessConfigForPorkbun{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -380,7 +433,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypePowerDNS: + case domain.ACMEDns01ProviderTypePowerDNS: { access := domain.AccessConfigForPowerDNS{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -388,15 +441,16 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { } applicant, err := pPowerDNS.NewChallengeProvider(&pPowerDNS.ChallengeProviderConfig{ - ApiUrl: access.ApiUrl, - ApiKey: access.ApiKey, - DnsPropagationTimeout: options.DnsPropagationTimeout, - DnsTTL: options.DnsTTL, + ApiUrl: access.ApiUrl, + ApiKey: access.ApiKey, + AllowInsecureConnections: access.AllowInsecureConnections, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, }) return applicant, err } - case domain.ApplyDNSProviderTypeRainYun: + case domain.ACMEDns01ProviderTypeRainYun: { access := domain.AccessConfigForRainYun{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -411,7 +465,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS, domain.ApplyDNSProviderTypeTencentCloudEO: + case domain.ACMEDns01ProviderTypeTencentCloud, domain.ACMEDns01ProviderTypeTencentCloudDNS, domain.ACMEDns01ProviderTypeTencentCloudEO: { access := domain.AccessConfigForTencentCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -419,7 +473,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { } switch options.Provider { - case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS: + case domain.ACMEDns01ProviderTypeTencentCloud, domain.ACMEDns01ProviderTypeTencentCloudDNS: applicant, err := pTencentCloud.NewChallengeProvider(&pTencentCloud.ChallengeProviderConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, @@ -428,11 +482,11 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { }) return applicant, err - case domain.ApplyDNSProviderTypeTencentCloudEO: + case domain.ACMEDns01ProviderTypeTencentCloudEO: applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - ZoneId: maputil.GetString(options.ProviderApplyConfig, "zoneId"), + ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"), DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) @@ -443,7 +497,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { } } - case domain.ApplyDNSProviderTypeVercel: + case domain.ACMEDns01ProviderTypeVercel: { access := domain.AccessConfigForVercel{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -459,7 +513,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeVolcEngine, domain.ApplyDNSProviderTypeVolcEngineDNS: + case domain.ACMEDns01ProviderTypeVolcEngine, domain.ACMEDns01ProviderTypeVolcEngineDNS: { access := domain.AccessConfigForVolcEngine{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -475,7 +529,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeWestcn: + case domain.ACMEDns01ProviderTypeWestcn: { access := domain.AccessConfigForWestcn{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -492,5 +546,5 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { } } - return nil, fmt.Errorf("unsupported applicant provider: %s", string(options.Provider)) + return nil, fmt.Errorf("unsupported applicant provider '%s'", string(options.Provider)) } diff --git a/internal/certificate/service.go b/internal/certificate/service.go index f8874f2d..ffb30d0f 100644 --- a/internal/certificate/service.go +++ b/internal/certificate/service.go @@ -17,7 +17,7 @@ import ( "github.com/usual2970/certimate/internal/domain" "github.com/usual2970/certimate/internal/domain/dtos" "github.com/usual2970/certimate/internal/notify" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" "github.com/usual2970/certimate/internal/repository" ) diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index 6efc001c..bdacf08e 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -11,66 +11,62 @@ import ( ) type Deployer interface { - SetLogger(*slog.Logger) - Deploy(ctx context.Context) error } -type deployerOptions struct { - Provider domain.DeployProviderType - ProviderAccessConfig map[string]any - ProviderDeployConfig map[string]any +type DeployerWithWorkflowNodeConfig struct { + Node *domain.WorkflowNode + Logger *slog.Logger + CertificatePEM string + PrivateKeyPEM string } -func NewWithDeployNode(node *domain.WorkflowNode, certdata struct { - Certificate string - PrivateKey string -}, -) (Deployer, error) { - if node.Type != domain.WorkflowNodeTypeDeploy { - return nil, fmt.Errorf("node type is not deploy") +func NewWithWorkflowNode(config DeployerWithWorkflowNodeConfig) (Deployer, error) { + if config.Node == nil { + return nil, fmt.Errorf("node is nil") + } + if config.Node.Type != domain.WorkflowNodeTypeDeploy { + return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeDeploy)) } - nodeConfig := node.GetConfigForDeploy() + nodeConfig := config.Node.GetConfigForDeploy() + options := &deployerProviderOptions{ + Provider: domain.DeploymentProviderType(nodeConfig.Provider), + ProviderAccessConfig: make(map[string]any), + ProviderExtendedConfig: nodeConfig.ProviderConfig, + } accessRepo := repository.NewAccessRepository() - access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId) - if err != nil { - return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err) + if nodeConfig.ProviderAccessId != "" { + access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId) + if err != nil { + return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err) + } else { + options.ProviderAccessConfig = access.Config + } } - deployer, err := createDeployer(&deployerOptions{ - Provider: domain.DeployProviderType(nodeConfig.Provider), - ProviderAccessConfig: access.Config, - ProviderDeployConfig: nodeConfig.ProviderConfig, - }) + deployerProvider, err := createDeployerProvider(options) if err != nil { return nil, err } - return &proxyDeployer{ - deployer: deployer, - deployCertificate: certdata.Certificate, - deployPrivateKey: certdata.PrivateKey, + return &deployerImpl{ + provider: deployerProvider.WithLogger(config.Logger), + certPEM: config.CertificatePEM, + privkeyPEM: config.PrivateKeyPEM, }, nil } -// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑 -type proxyDeployer struct { - deployer deployer.Deployer - deployCertificate string - deployPrivateKey string +type deployerImpl struct { + provider deployer.Deployer + certPEM string + privkeyPEM string } -func (d *proxyDeployer) SetLogger(logger *slog.Logger) { - if logger == nil { - panic("logger is nil") - } +var _ Deployer = (*deployerImpl)(nil) - d.deployer.WithLogger(logger) -} - -func (d *proxyDeployer) Deploy(ctx context.Context) error { - _, err := d.deployer.Deploy(ctx, d.deployCertificate, d.deployPrivateKey) +func (d *deployerImpl) Deploy(ctx context.Context) error { + _, err := d.provider.Deploy(ctx, d.certPEM, d.privkeyPEM) return err } diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index 61d14613..77ed8acb 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -2,6 +2,7 @@ package deployer import ( "fmt" + "net/http" "strings" "github.com/usual2970/certimate/internal/domain" @@ -9,11 +10,13 @@ import ( p1PanelConsole "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/1panel-console" p1PanelSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/1panel-site" pAliyunALB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-alb" + pAliyunAPIGW "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-apigw" pAliyunCAS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cas" pAliyunCASDeploy "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cas-deploy" pAliyunCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cdn" pAliyunCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-clb" pAliyunDCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-dcdn" + pAliyunDDoS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ddos" pAliyunESA "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-esa" pAliyunFC "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-fc" pAliyunLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-live" @@ -31,12 +34,14 @@ import ( pBaishanCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baishan-cdn" pBaotaPanelConsole "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-console" pBaotaPanelSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-site" + pBunnyCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/bunny-cdn" pBytePlusCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/byteplus-cdn" pCacheFly "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cachefly" pCdnfly "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cdnfly" pDogeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/dogecloud-cdn" pEdgioApplications "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/edgio-applications" pGcoreCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/gcore-cdn" + pGoEdge "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/goedge" pHuaweiCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-cdn" pHuaweiCloudELB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-elb" pHuaweiCloudSCM "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-scm" @@ -47,8 +52,10 @@ import ( pJDCloudVOD "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-vod" pK8sSecret "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/k8s-secret" pLocal "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/local" + pProxmoxVE "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/proxmoxve" pQiniuCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/qiniu-cdn" pQiniuPili "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/qiniu-pili" + pRainYunRCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/rainyun-rcdn" pSafeLine "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/safeline" pSSH "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ssh" pTencentCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-cdn" @@ -73,18 +80,26 @@ import ( pVolcEngineImageX "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-imagex" pVolcEngineLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-live" pVolcEngineTOS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-tos" + pWangsuCDNPro "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-cdnpro" pWebhook "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/webhook" - "github.com/usual2970/certimate/internal/pkg/utils/maputil" - "github.com/usual2970/certimate/internal/pkg/utils/sliceutil" + httputil "github.com/usual2970/certimate/internal/pkg/utils/http" + maputil "github.com/usual2970/certimate/internal/pkg/utils/map" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" ) -func createDeployer(options *deployerOptions) (deployer.Deployer, error) { +type deployerProviderOptions struct { + Provider domain.DeploymentProviderType + ProviderAccessConfig map[string]any + ProviderExtendedConfig map[string]any +} + +func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer, error) { /* 注意:如果追加新的常量值,请保持以 ASCII 排序。 NOTICE: If you add new constant, please keep ASCII order. */ switch options.Provider { - case domain.DeployProviderType1PanelConsole, domain.DeployProviderType1PanelSite: + case domain.DeploymentProviderType1PanelConsole, domain.DeploymentProviderType1PanelSite: { access := domain.AccessConfigFor1Panel{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -92,21 +107,23 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderType1PanelConsole: + case domain.DeploymentProviderType1PanelConsole: deployer, err := p1PanelConsole.NewDeployer(&p1PanelConsole.DeployerConfig{ ApiUrl: access.ApiUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, - AutoRestart: maputil.GetBool(options.ProviderDeployConfig, "autoRestart"), + AutoRestart: maputil.GetBool(options.ProviderExtendedConfig, "autoRestart"), }) return deployer, err - case domain.DeployProviderType1PanelSite: + case domain.DeploymentProviderType1PanelSite: deployer, err := p1PanelSite.NewDeployer(&p1PanelSite.DeployerConfig{ ApiUrl: access.ApiUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, - WebsiteId: maputil.GetInt64(options.ProviderDeployConfig, "websiteId"), + ResourceType: p1PanelSite.ResourceType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "resourceType", string(p1PanelSite.RESOURCE_TYPE_WEBSITE))), + WebsiteId: maputil.GetInt64(options.ProviderExtendedConfig, "websiteId"), + CertificateId: maputil.GetInt64(options.ProviderExtendedConfig, "certificateId"), }) return deployer, err @@ -115,7 +132,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeAliyunALB, domain.DeployProviderTypeAliyunCAS, domain.DeployProviderTypeAliyunCASDeploy, domain.DeployProviderTypeAliyunCDN, domain.DeployProviderTypeAliyunCLB, domain.DeployProviderTypeAliyunDCDN, domain.DeployProviderTypeAliyunESA, domain.DeployProviderTypeAliyunFC, domain.DeployProviderTypeAliyunLive, domain.DeployProviderTypeAliyunNLB, domain.DeployProviderTypeAliyunOSS, domain.DeployProviderTypeAliyunVOD, domain.DeployProviderTypeAliyunWAF: + case domain.DeploymentProviderTypeAliyunALB, domain.DeploymentProviderTypeAliyunAPIGW, domain.DeploymentProviderTypeAliyunCAS, domain.DeploymentProviderTypeAliyunCASDeploy, domain.DeploymentProviderTypeAliyunCDN, domain.DeploymentProviderTypeAliyunCLB, domain.DeploymentProviderTypeAliyunDCDN, domain.DeploymentProviderTypeAliyunDDoS, domain.DeploymentProviderTypeAliyunESA, domain.DeploymentProviderTypeAliyunFC, domain.DeploymentProviderTypeAliyunLive, domain.DeploymentProviderTypeAliyunNLB, domain.DeploymentProviderTypeAliyunOSS, domain.DeploymentProviderTypeAliyunVOD, domain.DeploymentProviderTypeAliyunWAF: { access := domain.AccessConfigForAliyun{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -123,130 +140,151 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeAliyunALB: + case domain.DeploymentProviderTypeAliyunALB: deployer, err := pAliyunALB.NewDeployer(&pAliyunALB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pAliyunALB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderDeployConfig, "listenerId"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pAliyunALB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeAliyunCAS: + case domain.DeploymentProviderTypeAliyunAPIGW: + deployer, err := pAliyunAPIGW.NewDeployer(&pAliyunAPIGW.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ServiceType: pAliyunAPIGW.ServiceType(maputil.GetString(options.ProviderExtendedConfig, "serviceType")), + GatewayId: maputil.GetString(options.ProviderExtendedConfig, "gatewayId"), + GroupId: maputil.GetString(options.ProviderExtendedConfig, "groupId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + }) + return deployer, err + + case domain.DeploymentProviderTypeAliyunCAS: deployer, err := pAliyunCAS.NewDeployer(&pAliyunCAS.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), }) return deployer, err - case domain.DeployProviderTypeAliyunCASDeploy: + case domain.DeploymentProviderTypeAliyunCASDeploy: deployer, err := pAliyunCASDeploy.NewDeployer(&pAliyunCASDeploy.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderDeployConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), - ContactIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderDeployConfig, "contactIds"), ";"), func(s string) bool { return s != "" }), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderExtendedConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), + ContactIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderExtendedConfig, "contactIds"), ";"), func(s string) bool { return s != "" }), }) return deployer, err - case domain.DeployProviderTypeAliyunCDN: + case domain.DeploymentProviderTypeAliyunCDN: deployer, err := pAliyunCDN.NewDeployer(&pAliyunCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeAliyunCLB: + case domain.DeploymentProviderTypeAliyunCLB: deployer, err := pAliyunCLB.NewDeployer(&pAliyunCLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pAliyunCLB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerPort: maputil.GetOrDefaultInt32(options.ProviderDeployConfig, "listenerPort", 443), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pAliyunCLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerPort: maputil.GetOrDefaultInt32(options.ProviderExtendedConfig, "listenerPort", 443), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeAliyunDCDN: + case domain.DeploymentProviderTypeAliyunDCDN: deployer, err := pAliyunDCDN.NewDeployer(&pAliyunDCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeAliyunESA: + case domain.DeploymentProviderTypeAliyunDDoS: + deployer, err := pAliyunDDoS.NewDeployer(&pAliyunDDoS.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + }) + return deployer, err + + case domain.DeploymentProviderTypeAliyunESA: deployer, err := pAliyunESA.NewDeployer(&pAliyunESA.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - SiteId: maputil.GetInt64(options.ProviderDeployConfig, "siteId"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + SiteId: maputil.GetInt64(options.ProviderExtendedConfig, "siteId"), }) return deployer, err - case domain.DeployProviderTypeAliyunFC: + case domain.DeploymentProviderTypeAliyunFC: deployer, err := pAliyunFC.NewDeployer(&pAliyunFC.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ServiceVersion: maputil.GetOrDefaultString(options.ProviderDeployConfig, "serviceVersion", "3.0"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ServiceVersion: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "serviceVersion", "3.0"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeAliyunLive: + case domain.DeploymentProviderTypeAliyunLive: deployer, err := pAliyunLive.NewDeployer(&pAliyunLive.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeAliyunNLB: + case domain.DeploymentProviderTypeAliyunNLB: deployer, err := pAliyunNLB.NewDeployer(&pAliyunNLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pAliyunNLB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderDeployConfig, "listenerId"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pAliyunNLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), }) return deployer, err - case domain.DeployProviderTypeAliyunOSS: + case domain.DeploymentProviderTypeAliyunOSS: deployer, err := pAliyunOSS.NewDeployer(&pAliyunOSS.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - Bucket: maputil.GetString(options.ProviderDeployConfig, "bucket"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeAliyunVOD: + case domain.DeploymentProviderTypeAliyunVOD: deployer, err := pAliyunVOD.NewDeployer(&pAliyunVOD.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeAliyunWAF: + case domain.DeploymentProviderTypeAliyunWAF: deployer, err := pAliyunWAF.NewDeployer(&pAliyunWAF.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ServiceVersion: maputil.GetOrDefaultString(options.ProviderDeployConfig, "serviceVersion", "3.0"), - InstanceId: maputil.GetString(options.ProviderDeployConfig, "instanceId"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ServiceVersion: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "serviceVersion", "3.0"), + InstanceId: maputil.GetString(options.ProviderExtendedConfig, "instanceId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err @@ -255,7 +293,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeAWSACM, domain.DeployProviderTypeAWSCloudFront: + case domain.DeploymentProviderTypeAWSACM, domain.DeploymentProviderTypeAWSCloudFront: { access := domain.AccessConfigForAWS{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -263,20 +301,20 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeAWSACM: + case domain.DeploymentProviderTypeAWSACM: deployer, err := pAWSACM.NewDeployer(&pAWSACM.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), }) return deployer, err - case domain.DeployProviderTypeAWSCloudFront: + case domain.DeploymentProviderTypeAWSCloudFront: deployer, err := pAWSCloudFront.NewDeployer(&pAWSCloudFront.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - DistributionId: maputil.GetString(options.ProviderDeployConfig, "distributionId"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + DistributionId: maputil.GetString(options.ProviderExtendedConfig, "distributionId"), }) return deployer, err @@ -285,7 +323,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeAzureKeyVault: + case domain.DeploymentProviderTypeAzureKeyVault: { access := domain.AccessConfigForAzure{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -293,13 +331,14 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeAzureKeyVault: + case domain.DeploymentProviderTypeAzureKeyVault: deployer, err := pAzureKeyVault.NewDeployer(&pAzureKeyVault.DeployerConfig{ - TenantId: access.TenantId, - ClientId: access.ClientId, - ClientSecret: access.ClientSecret, - CloudName: access.CloudName, - KeyVaultName: maputil.GetString(options.ProviderDeployConfig, "keyvaultName"), + TenantId: access.TenantId, + ClientId: access.ClientId, + ClientSecret: access.ClientSecret, + CloudName: access.CloudName, + KeyVaultName: maputil.GetString(options.ProviderExtendedConfig, "keyvaultName"), + CertificateName: maputil.GetString(options.ProviderExtendedConfig, "certificateName"), }) return deployer, err @@ -308,7 +347,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeBaiduCloudAppBLB, domain.DeployProviderTypeBaiduCloudBLB, domain.DeployProviderTypeBaiduCloudCDN, domain.DeployProviderTypeBaiduCloudCert: + case domain.DeploymentProviderTypeBaiduCloudAppBLB, domain.DeploymentProviderTypeBaiduCloudBLB, domain.DeploymentProviderTypeBaiduCloudCDN, domain.DeploymentProviderTypeBaiduCloudCert: { access := domain.AccessConfigForBaiduCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -316,39 +355,39 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeBaiduCloudAppBLB: + case domain.DeploymentProviderTypeBaiduCloudAppBLB: deployer, err := pBaiduCloudAppBLB.NewDeployer(&pBaiduCloudAppBLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pBaiduCloudAppBLB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerPort: maputil.GetInt32(options.ProviderDeployConfig, "listenerPort"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pBaiduCloudAppBLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerPort: maputil.GetInt32(options.ProviderExtendedConfig, "listenerPort"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeBaiduCloudBLB: + case domain.DeploymentProviderTypeBaiduCloudBLB: deployer, err := pBaiduCloudBLB.NewDeployer(&pBaiduCloudBLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pBaiduCloudBLB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerPort: maputil.GetInt32(options.ProviderDeployConfig, "listenerPort"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pBaiduCloudBLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerPort: maputil.GetInt32(options.ProviderExtendedConfig, "listenerPort"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeBaiduCloudCDN: + case domain.DeploymentProviderTypeBaiduCloudCDN: deployer, err := pBaiduCloudCDN.NewDeployer(&pBaiduCloudCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeBaiduCloudCert: + case domain.DeploymentProviderTypeBaiduCloudCert: deployer, err := pBaiduCloudCert.NewDeployer(&pBaiduCloudCert.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, @@ -360,7 +399,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeBaishanCDN: + case domain.DeploymentProviderTypeBaishanCDN: { access := domain.AccessConfigForBaishan{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -368,10 +407,11 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeBaishanCDN: + case domain.DeploymentProviderTypeBaishanCDN: deployer, err := pBaishanCDN.NewDeployer(&pBaishanCDN.DeployerConfig{ - ApiToken: access.ApiToken, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + ApiToken: access.ApiToken, + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), }) return deployer, err @@ -380,7 +420,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeBaotaPanelConsole, domain.DeployProviderTypeBaotaPanelSite: + case domain.DeploymentProviderTypeBaotaPanelConsole, domain.DeploymentProviderTypeBaotaPanelSite: { access := domain.AccessConfigForBaotaPanel{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -388,23 +428,23 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeBaotaPanelConsole: + case domain.DeploymentProviderTypeBaotaPanelConsole: deployer, err := pBaotaPanelConsole.NewDeployer(&pBaotaPanelConsole.DeployerConfig{ ApiUrl: access.ApiUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, - AutoRestart: maputil.GetBool(options.ProviderDeployConfig, "autoRestart"), + AutoRestart: maputil.GetBool(options.ProviderExtendedConfig, "autoRestart"), }) return deployer, err - case domain.DeployProviderTypeBaotaPanelSite: + case domain.DeploymentProviderTypeBaotaPanelSite: deployer, err := pBaotaPanelSite.NewDeployer(&pBaotaPanelSite.DeployerConfig{ ApiUrl: access.ApiUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, - SiteType: maputil.GetOrDefaultString(options.ProviderDeployConfig, "siteType", "other"), - SiteName: maputil.GetString(options.ProviderDeployConfig, "siteName"), - SiteNames: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderDeployConfig, "siteNames"), ";"), func(s string) bool { return s != "" }), + SiteType: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "siteType", "other"), + SiteName: maputil.GetString(options.ProviderExtendedConfig, "siteName"), + SiteNames: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderExtendedConfig, "siteNames"), ";"), func(s string) bool { return s != "" }), }) return deployer, err @@ -413,7 +453,22 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeBytePlusCDN: + case domain.DeploymentProviderTypeBunnyCDN: + { + access := domain.AccessConfigForBunny{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pBunnyCDN.NewDeployer(&pBunnyCDN.DeployerConfig{ + ApiKey: access.ApiKey, + PullZoneId: maputil.GetString(options.ProviderExtendedConfig, "pullZoneId"), + Hostname: maputil.GetString(options.ProviderExtendedConfig, "hostname"), + }) + return deployer, err + } + + case domain.DeploymentProviderTypeBytePlusCDN: { access := domain.AccessConfigForBytePlus{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -421,11 +476,11 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeBytePlusCDN: + case domain.DeploymentProviderTypeBytePlusCDN: deployer, err := pBytePlusCDN.NewDeployer(&pBytePlusCDN.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err @@ -434,7 +489,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeCacheFly: + case domain.DeploymentProviderTypeCacheFly: { access := domain.AccessConfigForCacheFly{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -447,7 +502,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { return deployer, err } - case domain.DeployProviderTypeCdnfly: + case domain.DeploymentProviderTypeCdnfly: { access := domain.AccessConfigForCdnfly{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -455,17 +510,18 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } deployer, err := pCdnfly.NewDeployer(&pCdnfly.DeployerConfig{ - ApiUrl: access.ApiUrl, - ApiKey: access.ApiKey, - ApiSecret: access.ApiSecret, - ResourceType: pCdnfly.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - SiteId: maputil.GetString(options.ProviderDeployConfig, "siteId"), - CertificateId: maputil.GetString(options.ProviderDeployConfig, "certificateId"), + ApiUrl: access.ApiUrl, + ApiKey: access.ApiKey, + ApiSecret: access.ApiSecret, + AllowInsecureConnections: access.AllowInsecureConnections, + ResourceType: pCdnfly.ResourceType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "resourceType", string(pCdnfly.RESOURCE_TYPE_SITE))), + SiteId: maputil.GetString(options.ProviderExtendedConfig, "siteId"), + CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), }) return deployer, err } - case domain.DeployProviderTypeDogeCloudCDN: + case domain.DeploymentProviderTypeDogeCloudCDN: { access := domain.AccessConfigForDogeCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -475,12 +531,12 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { deployer, err := pDogeCDN.NewDeployer(&pDogeCDN.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err } - case domain.DeployProviderTypeEdgioApplications: + case domain.DeploymentProviderTypeEdgioApplications: { access := domain.AccessConfigForEdgio{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -490,12 +546,12 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { deployer, err := pEdgioApplications.NewDeployer(&pEdgioApplications.DeployerConfig{ ClientId: access.ClientId, ClientSecret: access.ClientSecret, - EnvironmentId: maputil.GetString(options.ProviderDeployConfig, "environmentId"), + EnvironmentId: maputil.GetString(options.ProviderExtendedConfig, "environmentId"), }) return deployer, err } - case domain.DeployProviderTypeGcoreCDN: + case domain.DeploymentProviderTypeGcoreCDN: { access := domain.AccessConfigForGcore{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -503,10 +559,10 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeGcoreCDN: + case domain.DeploymentProviderTypeGcoreCDN: deployer, err := pGcoreCDN.NewDeployer(&pGcoreCDN.DeployerConfig{ ApiToken: access.ApiToken, - ResourceId: maputil.GetInt64(options.ProviderDeployConfig, "resourceId"), + ResourceId: maputil.GetInt64(options.ProviderExtendedConfig, "resourceId"), }) return deployer, err @@ -515,7 +571,25 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeHuaweiCloudCDN, domain.DeployProviderTypeHuaweiCloudELB, domain.DeployProviderTypeHuaweiCloudSCM, domain.DeployProviderTypeHuaweiCloudWAF: + case domain.DeploymentProviderTypeGoEdge: + { + access := domain.AccessConfigForGoEdge{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pGoEdge.NewDeployer(&pGoEdge.DeployerConfig{ + ApiUrl: access.ApiUrl, + AccessKeyId: access.AccessKeyId, + AccessKey: access.AccessKey, + AllowInsecureConnections: access.AllowInsecureConnections, + ResourceType: pGoEdge.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + CertificateId: maputil.GetInt64(options.ProviderExtendedConfig, "certificateId"), + }) + return deployer, err + } + + case domain.DeploymentProviderTypeHuaweiCloudCDN, domain.DeploymentProviderTypeHuaweiCloudELB, domain.DeploymentProviderTypeHuaweiCloudSCM, domain.DeploymentProviderTypeHuaweiCloudWAF: { access := domain.AccessConfigForHuaweiCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -523,42 +597,42 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeHuaweiCloudCDN: + case domain.DeploymentProviderTypeHuaweiCloudCDN: deployer, err := pHuaweiCloudCDN.NewDeployer(&pHuaweiCloudCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeHuaweiCloudELB: + case domain.DeploymentProviderTypeHuaweiCloudELB: deployer, err := pHuaweiCloudELB.NewDeployer(&pHuaweiCloudELB.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pHuaweiCloudELB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - CertificateId: maputil.GetString(options.ProviderDeployConfig, "certificateId"), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderDeployConfig, "listenerId"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pHuaweiCloudELB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), }) return deployer, err - case domain.DeployProviderTypeHuaweiCloudSCM: + case domain.DeploymentProviderTypeHuaweiCloudSCM: deployer, err := pHuaweiCloudSCM.NewDeployer(&pHuaweiCloudSCM.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, }) return deployer, err - case domain.DeployProviderTypeHuaweiCloudWAF: + case domain.DeploymentProviderTypeHuaweiCloudWAF: deployer, err := pHuaweiCloudWAF.NewDeployer(&pHuaweiCloudWAF.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pHuaweiCloudWAF.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - CertificateId: maputil.GetString(options.ProviderDeployConfig, "certificateId"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pHuaweiCloudWAF.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err @@ -567,7 +641,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeJDCloudALB, domain.DeployProviderTypeJDCloudCDN, domain.DeployProviderTypeJDCloudLive, domain.DeployProviderTypeJDCloudVOD: + case domain.DeploymentProviderTypeJDCloudALB, domain.DeploymentProviderTypeJDCloudCDN, domain.DeploymentProviderTypeJDCloudLive, domain.DeploymentProviderTypeJDCloudVOD: { access := domain.AccessConfigForJDCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -575,38 +649,38 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeJDCloudALB: + case domain.DeploymentProviderTypeJDCloudALB: deployer, err := pJDCloudALB.NewDeployer(&pJDCloudALB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - RegionId: maputil.GetString(options.ProviderDeployConfig, "regionId"), - ResourceType: pJDCloudALB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderDeployConfig, "listenerId"), + RegionId: maputil.GetString(options.ProviderExtendedConfig, "regionId"), + ResourceType: pJDCloudALB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), }) return deployer, err - case domain.DeployProviderTypeJDCloudCDN: + case domain.DeploymentProviderTypeJDCloudCDN: deployer, err := pJDCloudCDN.NewDeployer(&pJDCloudCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeJDCloudLive: + case domain.DeploymentProviderTypeJDCloudLive: deployer, err := pJDCloudLive.NewDeployer(&pJDCloudLive.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeJDCloudVOD: + case domain.DeploymentProviderTypeJDCloudVOD: deployer, err := pJDCloudVOD.NewDeployer(&pJDCloudVOD.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err @@ -615,24 +689,24 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeLocal: + case domain.DeploymentProviderTypeLocal: { deployer, err := pLocal.NewDeployer(&pLocal.DeployerConfig{ - ShellEnv: pLocal.ShellEnvType(maputil.GetString(options.ProviderDeployConfig, "shellEnv")), - PreCommand: maputil.GetString(options.ProviderDeployConfig, "preCommand"), - PostCommand: maputil.GetString(options.ProviderDeployConfig, "postCommand"), - OutputFormat: pLocal.OutputFormatType(maputil.GetOrDefaultString(options.ProviderDeployConfig, "format", string(pLocal.OUTPUT_FORMAT_PEM))), - OutputCertPath: maputil.GetString(options.ProviderDeployConfig, "certPath"), - OutputKeyPath: maputil.GetString(options.ProviderDeployConfig, "keyPath"), - PfxPassword: maputil.GetString(options.ProviderDeployConfig, "pfxPassword"), - JksAlias: maputil.GetString(options.ProviderDeployConfig, "jksAlias"), - JksKeypass: maputil.GetString(options.ProviderDeployConfig, "jksKeypass"), - JksStorepass: maputil.GetString(options.ProviderDeployConfig, "jksStorepass"), + ShellEnv: pLocal.ShellEnvType(maputil.GetString(options.ProviderExtendedConfig, "shellEnv")), + PreCommand: maputil.GetString(options.ProviderExtendedConfig, "preCommand"), + PostCommand: maputil.GetString(options.ProviderExtendedConfig, "postCommand"), + OutputFormat: pLocal.OutputFormatType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "format", string(pLocal.OUTPUT_FORMAT_PEM))), + OutputCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPath"), + OutputKeyPath: maputil.GetString(options.ProviderExtendedConfig, "keyPath"), + PfxPassword: maputil.GetString(options.ProviderExtendedConfig, "pfxPassword"), + JksAlias: maputil.GetString(options.ProviderExtendedConfig, "jksAlias"), + JksKeypass: maputil.GetString(options.ProviderExtendedConfig, "jksKeypass"), + JksStorepass: maputil.GetString(options.ProviderExtendedConfig, "jksStorepass"), }) return deployer, err } - case domain.DeployProviderTypeKubernetesSecret: + case domain.DeploymentProviderTypeKubernetesSecret: { access := domain.AccessConfigForKubernetes{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -641,16 +715,34 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { deployer, err := pK8sSecret.NewDeployer(&pK8sSecret.DeployerConfig{ KubeConfig: access.KubeConfig, - Namespace: maputil.GetOrDefaultString(options.ProviderDeployConfig, "namespace", "default"), - SecretName: maputil.GetString(options.ProviderDeployConfig, "secretName"), - SecretType: maputil.GetOrDefaultString(options.ProviderDeployConfig, "secretType", "kubernetes.io/tls"), - SecretDataKeyForCrt: maputil.GetOrDefaultString(options.ProviderDeployConfig, "secretDataKeyForCrt", "tls.crt"), - SecretDataKeyForKey: maputil.GetOrDefaultString(options.ProviderDeployConfig, "secretDataKeyForKey", "tls.key"), + Namespace: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "namespace", "default"), + SecretName: maputil.GetString(options.ProviderExtendedConfig, "secretName"), + SecretType: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "secretType", "kubernetes.io/tls"), + SecretDataKeyForCrt: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "secretDataKeyForCrt", "tls.crt"), + SecretDataKeyForKey: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "secretDataKeyForKey", "tls.key"), }) return deployer, err } - case domain.DeployProviderTypeQiniuCDN, domain.DeployProviderTypeQiniuKodo, domain.DeployProviderTypeQiniuPili: + case domain.DeploymentProviderTypeProxmoxVE: + { + access := domain.AccessConfigForProxmoxVE{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pProxmoxVE.NewDeployer(&pProxmoxVE.DeployerConfig{ + ApiUrl: access.ApiUrl, + ApiToken: access.ApiToken, + ApiTokenSecret: access.ApiTokenSecret, + AllowInsecureConnections: access.AllowInsecureConnections, + NodeName: maputil.GetString(options.ProviderExtendedConfig, "nodeName"), + AutoRestart: maputil.GetBool(options.ProviderExtendedConfig, "autoRestart"), + }) + return deployer, err + } + + case domain.DeploymentProviderTypeQiniuCDN, domain.DeploymentProviderTypeQiniuKodo, domain.DeploymentProviderTypeQiniuPili: { access := domain.AccessConfigForQiniu{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -658,20 +750,20 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeQiniuCDN, domain.DeployProviderTypeQiniuKodo: + case domain.DeploymentProviderTypeQiniuCDN, domain.DeploymentProviderTypeQiniuKodo: deployer, err := pQiniuCDN.NewDeployer(&pQiniuCDN.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeQiniuPili: + case domain.DeploymentProviderTypeQiniuPili: deployer, err := pQiniuPili.NewDeployer(&pQiniuPili.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, - Hub: maputil.GetString(options.ProviderDeployConfig, "hub"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Hub: maputil.GetString(options.ProviderExtendedConfig, "hub"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err @@ -680,7 +772,28 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeSafeLine: + case domain.DeploymentProviderTypeRainYunRCDN: + { + access := domain.AccessConfigForRainYun{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + switch options.Provider { + case domain.DeploymentProviderTypeTencentCloudCDN: + deployer, err := pRainYunRCDN.NewDeployer(&pRainYunRCDN.DeployerConfig{ + ApiKey: access.ApiKey, + InstanceId: maputil.GetInt32(options.ProviderExtendedConfig, "instanceId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + }) + return deployer, err + + default: + break + } + } + + case domain.DeploymentProviderTypeSafeLine: { access := domain.AccessConfigForSafeLine{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -691,13 +804,13 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { ApiUrl: access.ApiUrl, ApiToken: access.ApiToken, AllowInsecureConnections: access.AllowInsecureConnections, - ResourceType: pSafeLine.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - CertificateId: maputil.GetInt32(options.ProviderDeployConfig, "certificateId"), + ResourceType: pSafeLine.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + CertificateId: maputil.GetInt32(options.ProviderExtendedConfig, "certificateId"), }) return deployer, err } - case domain.DeployProviderTypeSSH: + case domain.DeploymentProviderTypeSSH: { access := domain.AccessConfigForSSH{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -711,21 +824,21 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { SshPassword: access.Password, SshKey: access.Key, SshKeyPassphrase: access.KeyPassphrase, - UseSCP: maputil.GetBool(options.ProviderDeployConfig, "useSCP"), - PreCommand: maputil.GetString(options.ProviderDeployConfig, "preCommand"), - PostCommand: maputil.GetString(options.ProviderDeployConfig, "postCommand"), - OutputFormat: pSSH.OutputFormatType(maputil.GetOrDefaultString(options.ProviderDeployConfig, "format", string(pSSH.OUTPUT_FORMAT_PEM))), - OutputCertPath: maputil.GetString(options.ProviderDeployConfig, "certPath"), - OutputKeyPath: maputil.GetString(options.ProviderDeployConfig, "keyPath"), - PfxPassword: maputil.GetString(options.ProviderDeployConfig, "pfxPassword"), - JksAlias: maputil.GetString(options.ProviderDeployConfig, "jksAlias"), - JksKeypass: maputil.GetString(options.ProviderDeployConfig, "jksKeypass"), - JksStorepass: maputil.GetString(options.ProviderDeployConfig, "jksStorepass"), + UseSCP: maputil.GetBool(options.ProviderExtendedConfig, "useSCP"), + PreCommand: maputil.GetString(options.ProviderExtendedConfig, "preCommand"), + PostCommand: maputil.GetString(options.ProviderExtendedConfig, "postCommand"), + OutputFormat: pSSH.OutputFormatType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "format", string(pSSH.OUTPUT_FORMAT_PEM))), + OutputCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPath"), + OutputKeyPath: maputil.GetString(options.ProviderExtendedConfig, "keyPath"), + PfxPassword: maputil.GetString(options.ProviderExtendedConfig, "pfxPassword"), + JksAlias: maputil.GetString(options.ProviderExtendedConfig, "jksAlias"), + JksKeypass: maputil.GetString(options.ProviderExtendedConfig, "jksKeypass"), + JksStorepass: maputil.GetString(options.ProviderExtendedConfig, "jksStorepass"), }) return deployer, err } - case domain.DeployProviderTypeTencentCloudCDN, domain.DeployProviderTypeTencentCloudCLB, domain.DeployProviderTypeTencentCloudCOS, domain.DeployProviderTypeTencentCloudCSS, domain.DeployProviderTypeTencentCloudECDN, domain.DeployProviderTypeTencentCloudEO, domain.DeployProviderTypeTencentCloudSCF, domain.DeployProviderTypeTencentCloudSSL, domain.DeployProviderTypeTencentCloudSSLDeploy, domain.DeployProviderTypeTencentCloudVOD, domain.DeployProviderTypeTencentCloudWAF: + case domain.DeploymentProviderTypeTencentCloudCDN, domain.DeploymentProviderTypeTencentCloudCLB, domain.DeploymentProviderTypeTencentCloudCOS, domain.DeploymentProviderTypeTencentCloudCSS, domain.DeploymentProviderTypeTencentCloudECDN, domain.DeploymentProviderTypeTencentCloudEO, domain.DeploymentProviderTypeTencentCloudSCF, domain.DeploymentProviderTypeTencentCloudSSL, domain.DeploymentProviderTypeTencentCloudSSLDeploy, domain.DeploymentProviderTypeTencentCloudVOD, domain.DeploymentProviderTypeTencentCloudWAF: { access := domain.AccessConfigForTencentCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -733,103 +846,103 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeTencentCloudCDN: + case domain.DeploymentProviderTypeTencentCloudCDN: deployer, err := pTencentCloudCDN.NewDeployer(&pTencentCloudCDN.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeTencentCloudCLB: + case domain.DeploymentProviderTypeTencentCloudCLB: deployer, err := pTencentCloudCLB.NewDeployer(&pTencentCloudCLB.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pTencentCloudCLB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderDeployConfig, "listenerId"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pTencentCloudCLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeTencentCloudCOS: + case domain.DeploymentProviderTypeTencentCloudCOS: deployer, err := pTencentCloudCOS.NewDeployer(&pTencentCloudCOS.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - Bucket: maputil.GetString(options.ProviderDeployConfig, "bucket"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeTencentCloudCSS: + case domain.DeploymentProviderTypeTencentCloudCSS: deployer, err := pTencentCloudCSS.NewDeployer(&pTencentCloudCSS.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeTencentCloudECDN: + case domain.DeploymentProviderTypeTencentCloudECDN: deployer, err := pTencentCloudECDN.NewDeployer(&pTencentCloudECDN.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeTencentCloudEO: + case domain.DeploymentProviderTypeTencentCloudEO: deployer, err := pTencentCloudEO.NewDeployer(&pTencentCloudEO.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - ZoneId: maputil.GetString(options.ProviderDeployConfig, "zoneId"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeTencentCloudSCF: + case domain.DeploymentProviderTypeTencentCloudSCF: deployer, err := pTencentCloudSCF.NewDeployer(&pTencentCloudSCF.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeTencentCloudSSL: + case domain.DeploymentProviderTypeTencentCloudSSL: deployer, err := pTencentCloudSSL.NewDeployer(&pTencentCloudSSL.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, }) return deployer, err - case domain.DeployProviderTypeTencentCloudSSLDeploy: + case domain.DeploymentProviderTypeTencentCloudSSLDeploy: deployer, err := pTencentCloudSSLDeploy.NewDeployer(&pTencentCloudSSLDeploy.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: maputil.GetString(options.ProviderDeployConfig, "resourceType"), - ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderDeployConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: maputil.GetString(options.ProviderExtendedConfig, "resourceType"), + ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderExtendedConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), }) return deployer, err - case domain.DeployProviderTypeTencentCloudVOD: + case domain.DeploymentProviderTypeTencentCloudVOD: deployer, err := pTencentCloudVOD.NewDeployer(&pTencentCloudVOD.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - SubAppId: maputil.GetInt64(options.ProviderDeployConfig, "subAppId"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + SubAppId: maputil.GetInt64(options.ProviderExtendedConfig, "subAppId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeTencentCloudWAF: + case domain.DeploymentProviderTypeTencentCloudWAF: deployer, err := pTencentCloudWAF.NewDeployer(&pTencentCloudWAF.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), - DomainId: maputil.GetString(options.ProviderDeployConfig, "domainId"), - InstanceId: maputil.GetString(options.ProviderDeployConfig, "instanceId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + DomainId: maputil.GetString(options.ProviderExtendedConfig, "domainId"), + InstanceId: maputil.GetString(options.ProviderExtendedConfig, "instanceId"), }) return deployer, err @@ -838,7 +951,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeUCloudUCDN, domain.DeployProviderTypeUCloudUS3: + case domain.DeploymentProviderTypeUCloudUCDN, domain.DeploymentProviderTypeUCloudUS3: { access := domain.AccessConfigForUCloud{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -846,23 +959,23 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeUCloudUCDN: + case domain.DeploymentProviderTypeUCloudUCDN: deployer, err := pUCloudUCDN.NewDeployer(&pUCloudUCDN.DeployerConfig{ PrivateKey: access.PrivateKey, PublicKey: access.PublicKey, ProjectId: access.ProjectId, - DomainId: maputil.GetString(options.ProviderDeployConfig, "domainId"), + DomainId: maputil.GetString(options.ProviderExtendedConfig, "domainId"), }) return deployer, err - case domain.DeployProviderTypeUCloudUS3: + case domain.DeploymentProviderTypeUCloudUS3: deployer, err := pUCloudUS3.NewDeployer(&pUCloudUS3.DeployerConfig{ PrivateKey: access.PrivateKey, PublicKey: access.PublicKey, ProjectId: access.ProjectId, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - Bucket: maputil.GetString(options.ProviderDeployConfig, "bucket"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err @@ -871,7 +984,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeUpyunCDN, domain.DeployProviderTypeUpyunFile: + case domain.DeploymentProviderTypeUpyunCDN, domain.DeploymentProviderTypeUpyunFile: { access := domain.AccessConfigForUpyun{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -879,11 +992,11 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeUpyunCDN, domain.DeployProviderTypeUpyunFile: + case domain.DeploymentProviderTypeUpyunCDN, domain.DeploymentProviderTypeUpyunFile: deployer, err := pUpyunCDN.NewDeployer(&pUpyunCDN.DeployerConfig{ Username: access.Username, Password: access.Password, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err @@ -892,7 +1005,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeVolcEngineALB, domain.DeployProviderTypeVolcEngineCDN, domain.DeployProviderTypeVolcEngineCertCenter, domain.DeployProviderTypeVolcEngineCLB, domain.DeployProviderTypeVolcEngineDCDN, domain.DeployProviderTypeVolcEngineImageX, domain.DeployProviderTypeVolcEngineLive, domain.DeployProviderTypeVolcEngineTOS: + case domain.DeploymentProviderTypeVolcEngineALB, domain.DeploymentProviderTypeVolcEngineCDN, domain.DeploymentProviderTypeVolcEngineCertCenter, domain.DeploymentProviderTypeVolcEngineCLB, domain.DeploymentProviderTypeVolcEngineDCDN, domain.DeploymentProviderTypeVolcEngineImageX, domain.DeploymentProviderTypeVolcEngineLive, domain.DeploymentProviderTypeVolcEngineTOS: { access := domain.AccessConfigForVolcEngine{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -900,78 +1013,78 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { - case domain.DeployProviderTypeVolcEngineALB: + case domain.DeploymentProviderTypeVolcEngineALB: deployer, err := pVolcEngineALB.NewDeployer(&pVolcEngineALB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pVolcEngineALB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderDeployConfig, "listenerId"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pVolcEngineALB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeVolcEngineCDN: + case domain.DeploymentProviderTypeVolcEngineCDN: deployer, err := pVolcEngineCDN.NewDeployer(&pVolcEngineCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeVolcEngineCertCenter: + case domain.DeploymentProviderTypeVolcEngineCertCenter: deployer, err := pVolcEngineCertCenter.NewDeployer(&pVolcEngineCertCenter.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), }) return deployer, err - case domain.DeployProviderTypeVolcEngineCLB: + case domain.DeploymentProviderTypeVolcEngineCLB: deployer, err := pVolcEngineCLB.NewDeployer(&pVolcEngineCLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ResourceType: pVolcEngineCLB.ResourceType(maputil.GetString(options.ProviderDeployConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderDeployConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderDeployConfig, "listenerId"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ResourceType: pVolcEngineCLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), }) return deployer, err - case domain.DeployProviderTypeVolcEngineDCDN: + case domain.DeploymentProviderTypeVolcEngineDCDN: deployer, err := pVolcEngineDCDN.NewDeployer(&pVolcEngineDCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeVolcEngineImageX: + case domain.DeploymentProviderTypeVolcEngineImageX: deployer, err := pVolcEngineImageX.NewDeployer(&pVolcEngineImageX.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - ServiceId: maputil.GetString(options.ProviderDeployConfig, "serviceId"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + ServiceId: maputil.GetString(options.ProviderExtendedConfig, "serviceId"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeVolcEngineLive: + case domain.DeploymentProviderTypeVolcEngineLive: deployer, err := pVolcEngineLive.NewDeployer(&pVolcEngineLive.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err - case domain.DeployProviderTypeVolcEngineTOS: + case domain.DeploymentProviderTypeVolcEngineTOS: deployer, err := pVolcEngineTOS.NewDeployer(&pVolcEngineTOS.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderDeployConfig, "region"), - Bucket: maputil.GetString(options.ProviderDeployConfig, "bucket"), - Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), + Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), }) return deployer, err @@ -980,21 +1093,68 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeWebhook: + case domain.DeploymentProviderTypeWangsuCDNPro: + { + access := domain.AccessConfigForWangsu{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + switch options.Provider { + case domain.DeploymentProviderTypeWangsuCDNPro: + deployer, err := pWangsuCDNPro.NewDeployer(&pWangsuCDNPro.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + ApiKey: access.ApiKey, + Environment: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "environment", "production"), + Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), + WebhookId: maputil.GetString(options.ProviderExtendedConfig, "webhookId"), + }) + return deployer, err + + default: + break + } + } + + case domain.DeploymentProviderTypeWebhook: { access := domain.AccessConfigForWebhook{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } + mergedHeaders := make(map[string]string) + if defaultHeadersString := access.HeadersString; defaultHeadersString != "" { + h, err := httputil.ParseHeaders(defaultHeadersString) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook headers: %w", err) + } + for key := range h { + mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) + } + } + if extendedHeadersString := maputil.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" { + h, err := httputil.ParseHeaders(extendedHeadersString) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook headers: %w", err) + } + for key := range h { + mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) + } + } + deployer, err := pWebhook.NewDeployer(&pWebhook.DeployerConfig{ WebhookUrl: access.Url, - WebhookData: maputil.GetString(options.ProviderDeployConfig, "webhookData"), + WebhookData: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForDeployment), + Method: access.Method, + Headers: mergedHeaders, AllowInsecureConnections: access.AllowInsecureConnections, }) return deployer, err } } - return nil, fmt.Errorf("unsupported deployer provider: %s", string(options.Provider)) + return nil, fmt.Errorf("unsupported deployer provider '%s'", string(options.Provider)) } diff --git a/internal/domain/access.go b/internal/domain/access.go index dc796c83..84afd292 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -11,6 +11,7 @@ type Access struct { Name string `json:"name" db:"name"` Provider string `json:"provider" db:"provider"` Config map[string]any `json:"config" db:"config"` + Reserve string `json:"reserve,omitempty" db:"reserve"` DeletedAt *time.Time `json:"deleted" db:"deleted"` } @@ -64,18 +65,24 @@ type AccessConfigForBytePlus struct { SecretKey string `json:"secretKey"` } +type AccessConfigForBunny struct { + ApiKey string `json:"apiKey"` +} + type AccessConfigForCacheFly struct { ApiToken string `json:"apiToken"` } type AccessConfigForCdnfly struct { - ApiUrl string `json:"apiUrl"` - ApiKey string `json:"apiKey"` - ApiSecret string `json:"apiSecret"` + ApiUrl string `json:"apiUrl"` + ApiKey string `json:"apiKey"` + ApiSecret string `json:"apiSecret"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type AccessConfigForCloudflare struct { - DnsApiToken string `json:"dnsApiToken"` + DnsApiToken string `json:"dnsApiToken"` + ZoneApiToken string `json:"zoneApiToken,omitempty"` } type AccessConfigForClouDNS struct { @@ -92,6 +99,11 @@ type AccessConfigForDeSEC struct { Token string `json:"token"` } +type AccessConfigForDingTalkBot struct { + WebhookUrl string `json:"webhookUrl"` + Secret string `json:"secret"` +} + type AccessConfigForDNSLA struct { ApiId string `json:"apiId"` ApiSecret string `json:"apiSecret"` @@ -111,6 +123,16 @@ type AccessConfigForEdgio struct { ClientSecret string `json:"clientSecret"` } +type AccessConfigForEmail struct { + SmtpHost string `json:"smtpHost"` + SmtpPort int32 `json:"smtpPort"` + SmtpTls bool `json:"smtpTls"` + Username string `json:"username"` + Password string `json:"password"` + DefaultSenderAddress string `json:"defaultSenderAddress,omitempty"` + DefaultReceiverAddress string `json:"defaultReceiverAddress,omitempty"` +} + type AccessConfigForGcore struct { ApiToken string `json:"apiToken"` } @@ -125,6 +147,18 @@ type AccessConfigForGoDaddy struct { ApiSecret string `json:"apiSecret"` } +type AccessConfigForGoEdge struct { + ApiUrl string `json:"apiUrl"` + AccessKeyId string `json:"accessKeyId"` + AccessKey string `json:"accessKey"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` +} + +type AccessConfigForGoogleTrustServices struct { + EabKid string `json:"eabKid"` + EabHmacKey string `json:"eabHmacKey"` +} + type AccessConfigForHuaweiCloud struct { AccessKeyId string `json:"accessKeyId"` SecretAccessKey string `json:"secretAccessKey"` @@ -139,7 +173,16 @@ type AccessConfigForKubernetes struct { KubeConfig string `json:"kubeConfig,omitempty"` } -type AccessConfigForLocal struct{} +type AccessConfigForLarkBot struct { + WebhookUrl string `json:"webhookUrl"` +} + +type AccessConfigForMattermost struct { + ServerUrl string `json:"serverUrl"` + Username string `json:"username"` + Password string `json:"password"` + DefaultChannelId string `json:"defaultChannelId,omitempty"` +} type AccessConfigForNamecheap struct { Username string `json:"username"` @@ -165,8 +208,16 @@ type AccessConfigForPorkbun struct { } type AccessConfigForPowerDNS struct { - ApiUrl string `json:"apiUrl"` - ApiKey string `json:"apiKey"` + ApiUrl string `json:"apiUrl"` + ApiKey string `json:"apiKey"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` +} + +type AccessConfigForProxmoxVE struct { + ApiUrl string `json:"apiUrl"` + ApiToken string `json:"apiToken"` + ApiTokenSecret string `json:"apiTokenSecret,omitempty"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type AccessConfigForQiniu struct { @@ -193,6 +244,16 @@ type AccessConfigForSSH struct { KeyPassphrase string `json:"keyPassphrase,omitempty"` } +type AccessConfigForSSLCom struct { + EabKid string `json:"eabKid"` + EabHmacKey string `json:"eabHmacKey"` +} + +type AccessConfigForTelegram struct { + BotToken string `json:"botToken"` + DefaultChatId int64 `json:"defaultChatId,omitempty"` +} + type AccessConfigForTencentCloud struct { SecretId string `json:"secretId"` SecretKey string `json:"secretKey"` @@ -219,12 +280,31 @@ type AccessConfigForVolcEngine struct { SecretAccessKey string `json:"secretAccessKey"` } +type AccessConfigForWangsu struct { + AccessKeyId string `json:"accessKeyId"` + AccessKeySecret string `json:"accessKeySecret"` + ApiKey string `json:"apiKey"` +} + type AccessConfigForWebhook struct { - Url string `json:"url"` - AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + Url string `json:"url"` + Method string `json:"method,omitempty"` + HeadersString string `json:"headers,omitempty"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + DefaultDataForDeployment string `json:"defaultDataForDeployment,omitempty"` + DefaultDataForNotification string `json:"defaultDataForNotification,omitempty"` +} + +type AccessConfigForWeComBot struct { + WebhookUrl string `json:"webhookUrl"` } type AccessConfigForWestcn struct { Username string `json:"username"` ApiPassword string `json:"password"` } + +type AccessConfigForZeroSSL struct { + EabKid string `json:"eabKid"` + EabHmacKey string `json:"eabHmacKey"` +} diff --git a/internal/domain/certificate.go b/internal/domain/certificate.go index 489af550..d2a998da 100644 --- a/internal/domain/certificate.go +++ b/internal/domain/certificate.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) const CollectionNameCertificate = "certificate" diff --git a/internal/domain/dtos/certificate.go b/internal/domain/dtos/certificate.go index a1853df0..ce6b7d5e 100644 --- a/internal/domain/dtos/certificate.go +++ b/internal/domain/dtos/certificate.go @@ -1,4 +1,4 @@ -package dtos +package dtos type CertificateArchiveFileReq struct { CertificateId string `json:"-"` diff --git a/internal/domain/dtos/notify.go b/internal/domain/dtos/notify.go index 72c6cc79..ab72fff1 100644 --- a/internal/domain/dtos/notify.go +++ b/internal/domain/dtos/notify.go @@ -1,4 +1,4 @@ -package dtos +package dtos import "github.com/usual2970/certimate/internal/domain" diff --git a/internal/domain/dtos/workflow.go b/internal/domain/dtos/workflow.go index 3988220b..b0f75f58 100644 --- a/internal/domain/dtos/workflow.go +++ b/internal/domain/dtos/workflow.go @@ -1,4 +1,4 @@ -package dtos +package dtos import "github.com/usual2970/certimate/internal/domain" diff --git a/internal/domain/notify.go b/internal/domain/notify.go index 3d71a3a7..1a244e9b 100644 --- a/internal/domain/notify.go +++ b/internal/domain/notify.go @@ -8,11 +8,16 @@ type NotifyChannelType string 注意:如果追加新的常量值,请保持以 ASCII 排序。 NOTICE: If you add new constant, please keep ASCII order. */ +// Deprecated: v0.4.x 将废弃 const ( NotifyChannelTypeBark = NotifyChannelType("bark") NotifyChannelTypeDingTalk = NotifyChannelType("dingtalk") NotifyChannelTypeEmail = NotifyChannelType("email") + NotifyChannelTypeGotify = NotifyChannelType("gotify") NotifyChannelTypeLark = NotifyChannelType("lark") + NotifyChannelTypeMattermost = NotifyChannelType("mattermost") + NotifyChannelTypePushover = NotifyChannelType("pushover") + NotifyChannelTypePushPlus = NotifyChannelType("pushplus") NotifyChannelTypeServerChan = NotifyChannelType("serverchan") NotifyChannelTypeTelegram = NotifyChannelType("telegram") NotifyChannelTypeWebhook = NotifyChannelType("webhook") diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 0e4bcce3..728f89b6 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -1,4 +1,4 @@ -package domain +package domain type AccessProviderType string @@ -9,183 +9,244 @@ type AccessProviderType string NOTICE: If you add new constant, please keep ASCII order. */ const ( - AccessProviderType1Panel = AccessProviderType("1panel") - AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq") - AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留) - AccessProviderTypeAliyun = AccessProviderType("aliyun") - AccessProviderTypeAWS = AccessProviderType("aws") - AccessProviderTypeAzure = AccessProviderType("azure") - AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud") - AccessProviderTypeBaishan = AccessProviderType("baishan") - AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel") - AccessProviderTypeBytePlus = AccessProviderType("byteplus") - AccessProviderTypeCacheFly = AccessProviderType("cachefly") - AccessProviderTypeCdnfly = AccessProviderType("cdnfly") - AccessProviderTypeCloudflare = AccessProviderType("cloudflare") - AccessProviderTypeClouDNS = AccessProviderType("cloudns") - AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud") - AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留) - AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留) - AccessProviderTypeDeSEC = AccessProviderType("desec") - AccessProviderTypeDNSLA = AccessProviderType("dnsla") - AccessProviderTypeDogeCloud = AccessProviderType("dogecloud") - AccessProviderTypeDynv6 = AccessProviderType("dynv6") - AccessProviderTypeEdgio = AccessProviderType("edgio") - AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留) - AccessProviderTypeGname = AccessProviderType("gname") - AccessProviderTypeGcore = AccessProviderType("gcore") - AccessProviderTypeGoDaddy = AccessProviderType("godaddy") - AccessProviderTypeGoEdge = AccessProviderType("goedge") // GoEdge(预留) - AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") - AccessProviderTypeJDCloud = AccessProviderType("jdcloud") - AccessProviderTypeKubernetes = AccessProviderType("k8s") - AccessProviderTypeLocal = AccessProviderType("local") - AccessProviderTypeNamecheap = AccessProviderType("namecheap") - AccessProviderTypeNameDotCom = AccessProviderType("namedotcom") - AccessProviderTypeNameSilo = AccessProviderType("namesilo") - AccessProviderTypeNS1 = AccessProviderType("ns1") - AccessProviderTypePorkbun = AccessProviderType("porkbun") - AccessProviderTypePowerDNS = AccessProviderType("powerdns") - AccessProviderTypeQiniu = AccessProviderType("qiniu") - AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留) - AccessProviderTypeRainYun = AccessProviderType("rainyun") - AccessProviderTypeSafeLine = AccessProviderType("safeline") - AccessProviderTypeSSH = AccessProviderType("ssh") - AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud") - AccessProviderTypeUCloud = AccessProviderType("ucloud") - AccessProviderTypeUpyun = AccessProviderType("upyun") - AccessProviderTypeVercel = AccessProviderType("vercel") - AccessProviderTypeVolcEngine = AccessProviderType("volcengine") - AccessProviderTypeWebhook = AccessProviderType("webhook") - AccessProviderTypeWestcn = AccessProviderType("westcn") + AccessProviderType1Panel = AccessProviderType("1panel") + AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq") + AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留) + AccessProviderTypeAliyun = AccessProviderType("aliyun") + AccessProviderTypeAWS = AccessProviderType("aws") + AccessProviderTypeAzure = AccessProviderType("azure") + AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud") + AccessProviderTypeBaishan = AccessProviderType("baishan") + AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel") + AccessProviderTypeBytePlus = AccessProviderType("byteplus") + AccessProviderTypeBunny = AccessProviderType("bunny") + AccessProviderTypeBuypass = AccessProviderType("buypass") + AccessProviderTypeCacheFly = AccessProviderType("cachefly") + AccessProviderTypeCdnfly = AccessProviderType("cdnfly") + AccessProviderTypeCloudflare = AccessProviderType("cloudflare") + AccessProviderTypeClouDNS = AccessProviderType("cloudns") + AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud") + AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 天翼云(预留) + AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 联通云(预留) + AccessProviderTypeDeSEC = AccessProviderType("desec") + AccessProviderTypeDingTalkBot = AccessProviderType("dingtalkbot") + AccessProviderTypeDNSLA = AccessProviderType("dnsla") + AccessProviderTypeDogeCloud = AccessProviderType("dogecloud") + AccessProviderTypeDynv6 = AccessProviderType("dynv6") + AccessProviderTypeEdgio = AccessProviderType("edgio") + AccessProviderTypeEmail = AccessProviderType("email") + AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留) + AccessProviderTypeGname = AccessProviderType("gname") + AccessProviderTypeGcore = AccessProviderType("gcore") + AccessProviderTypeGoDaddy = AccessProviderType("godaddy") + AccessProviderTypeGoEdge = AccessProviderType("goedge") + AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices") + AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") + AccessProviderTypeJDCloud = AccessProviderType("jdcloud") + AccessProviderTypeKubernetes = AccessProviderType("k8s") + AccessProviderTypeLarkBot = AccessProviderType("larkbot") + AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt") + AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging") + AccessProviderTypeLocal = AccessProviderType("local") + AccessProviderTypeMattermost = AccessProviderType("mattermost") + AccessProviderTypeNamecheap = AccessProviderType("namecheap") + AccessProviderTypeNameDotCom = AccessProviderType("namedotcom") + AccessProviderTypeNameSilo = AccessProviderType("namesilo") + AccessProviderTypeNS1 = AccessProviderType("ns1") + AccessProviderTypePorkbun = AccessProviderType("porkbun") + AccessProviderTypePowerDNS = AccessProviderType("powerdns") + AccessProviderTypeProxmoxVE = AccessProviderType("proxmoxve") + AccessProviderTypeQiniu = AccessProviderType("qiniu") + AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留) + AccessProviderTypeRainYun = AccessProviderType("rainyun") + AccessProviderTypeSafeLine = AccessProviderType("safeline") + AccessProviderTypeSSH = AccessProviderType("ssh") + AccessProviderTypeSSLCOM = AccessProviderType("sslcom") + AccessProviderTypeTelegram = AccessProviderType("telegram") + AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud") + AccessProviderTypeUCloud = AccessProviderType("ucloud") + AccessProviderTypeUpyun = AccessProviderType("upyun") + AccessProviderTypeVercel = AccessProviderType("vercel") + AccessProviderTypeVolcEngine = AccessProviderType("volcengine") + AccessProviderTypeWangsu = AccessProviderType("wangsu") + AccessProviderTypeWebhook = AccessProviderType("webhook") + AccessProviderTypeWeComBot = AccessProviderType("wecombot") + AccessProviderTypeWestcn = AccessProviderType("westcn") + AccessProviderTypeZeroSSL = AccessProviderType("zerossl") ) -type ApplyDNSProviderType string +type CAProviderType string /* -申请证书 DNS 提供商常量值。 +证书颁发机构提供商常量值。 短横线前的部分始终等于授权提供商类型。 注意:如果追加新的常量值,请保持以 ASCII 排序。 NOTICE: If you add new constant, please keep ASCII order. */ const ( - ApplyDNSProviderTypeACMEHttpReq = ApplyDNSProviderType("acmehttpreq") - ApplyDNSProviderTypeAliyun = ApplyDNSProviderType("aliyun") // 兼容旧值,等同于 [ApplyDNSProviderTypeAliyunDNS] - ApplyDNSProviderTypeAliyunDNS = ApplyDNSProviderType("aliyun-dns") - ApplyDNSProviderTypeAWS = ApplyDNSProviderType("aws") // 兼容旧值,等同于 [ApplyDNSProviderTypeAWSRoute53] - ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType("aws-route53") - ApplyDNSProviderTypeAzure = ApplyDNSProviderType("azure") // 兼容旧值,等同于 [ApplyDNSProviderTypeAzure] - ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns") - ApplyDNSProviderTypeBaiduCloud = ApplyDNSProviderType("baiducloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeBaiduCloudDNS] - ApplyDNSProviderTypeBaiduCloudDNS = ApplyDNSProviderType("baiducloud-dns") - ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare") - ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns") - ApplyDNSProviderTypeCMCCCloud = ApplyDNSProviderType("cmcccloud") - ApplyDNSProviderTypeDeSEC = ApplyDNSProviderType("desec") - ApplyDNSProviderTypeDNSLA = ApplyDNSProviderType("dnsla") - ApplyDNSProviderTypeDynv6 = ApplyDNSProviderType("dynv6") - ApplyDNSProviderTypeGcore = ApplyDNSProviderType("gcore") - ApplyDNSProviderTypeGname = ApplyDNSProviderType("gname") - ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy") - ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS] - ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns") - ApplyDNSProviderTypeJDCloud = ApplyDNSProviderType("jdcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeJDCloudDNS] - ApplyDNSProviderTypeJDCloudDNS = ApplyDNSProviderType("jdcloud-dns") - ApplyDNSProviderTypeNamecheap = ApplyDNSProviderType("namecheap") - ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom") - ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo") - ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1") - ApplyDNSProviderTypePorkbun = ApplyDNSProviderType("porkbun") - ApplyDNSProviderTypePowerDNS = ApplyDNSProviderType("powerdns") - ApplyDNSProviderTypeRainYun = ApplyDNSProviderType("rainyun") - ApplyDNSProviderTypeTencentCloud = ApplyDNSProviderType("tencentcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeTencentCloudDNS] - ApplyDNSProviderTypeTencentCloudDNS = ApplyDNSProviderType("tencentcloud-dns") - ApplyDNSProviderTypeTencentCloudEO = ApplyDNSProviderType("tencentcloud-eo") - ApplyDNSProviderTypeVercel = ApplyDNSProviderType("vercel") - ApplyDNSProviderTypeVolcEngine = ApplyDNSProviderType("volcengine") // 兼容旧值,等同于 [ApplyDNSProviderTypeVolcEngineDNS] - ApplyDNSProviderTypeVolcEngineDNS = ApplyDNSProviderType("volcengine-dns") - ApplyDNSProviderTypeWestcn = ApplyDNSProviderType("westcn") + CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass) + CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices) + CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt) + CAProviderTypeLetsEncryptStaging = CAProviderType(AccessProviderTypeLetsEncryptStaging) + CAProviderTypeSSLCom = CAProviderType(AccessProviderTypeSSLCOM) + CAProviderTypeZeroSSL = CAProviderType(AccessProviderTypeZeroSSL) ) -type DeployProviderType string +type ACMEDns01ProviderType string /* -部署目标提供商常量值。 +ACME DNS-01 提供商常量值。 短横线前的部分始终等于授权提供商类型。 注意:如果追加新的常量值,请保持以 ASCII 排序。 NOTICE: If you add new constant, please keep ASCII order. */ const ( - DeployProviderType1PanelConsole = DeployProviderType("1panel-console") - DeployProviderType1PanelSite = DeployProviderType("1panel-site") - DeployProviderTypeAliyunALB = DeployProviderType("aliyun-alb") - DeployProviderTypeAliyunCAS = DeployProviderType("aliyun-cas") - DeployProviderTypeAliyunCASDeploy = DeployProviderType("aliyun-casdeploy") - DeployProviderTypeAliyunCDN = DeployProviderType("aliyun-cdn") - DeployProviderTypeAliyunCLB = DeployProviderType("aliyun-clb") - DeployProviderTypeAliyunDCDN = DeployProviderType("aliyun-dcdn") - DeployProviderTypeAliyunESA = DeployProviderType("aliyun-esa") - DeployProviderTypeAliyunFC = DeployProviderType("aliyun-fc") - DeployProviderTypeAliyunLive = DeployProviderType("aliyun-live") - DeployProviderTypeAliyunNLB = DeployProviderType("aliyun-nlb") - DeployProviderTypeAliyunOSS = DeployProviderType("aliyun-oss") - DeployProviderTypeAliyunVOD = DeployProviderType("aliyun-vod") - DeployProviderTypeAliyunWAF = DeployProviderType("aliyun-waf") - DeployProviderTypeAWSACM = DeployProviderType("aws-acm") - DeployProviderTypeAWSCloudFront = DeployProviderType("aws-cloudfront") - DeployProviderTypeAzureKeyVault = DeployProviderType("azure-keyvault") - DeployProviderTypeBaiduCloudAppBLB = DeployProviderType("baiducloud-appblb") - DeployProviderTypeBaiduCloudBLB = DeployProviderType("baiducloud-blb") - DeployProviderTypeBaiduCloudCDN = DeployProviderType("baiducloud-cdn") - DeployProviderTypeBaiduCloudCert = DeployProviderType("baiducloud-cert") - DeployProviderTypeBaishanCDN = DeployProviderType("baishan-cdn") - DeployProviderTypeBaotaPanelConsole = DeployProviderType("baotapanel-console") - DeployProviderTypeBaotaPanelSite = DeployProviderType("baotapanel-site") - DeployProviderTypeBytePlusCDN = DeployProviderType("byteplus-cdn") - DeployProviderTypeCacheFly = DeployProviderType("cachefly") - DeployProviderTypeCdnfly = DeployProviderType("cdnfly") - DeployProviderTypeDogeCloudCDN = DeployProviderType("dogecloud-cdn") - DeployProviderTypeEdgioApplications = DeployProviderType("edgio-applications") - DeployProviderTypeGcoreCDN = DeployProviderType("gcore-cdn") - DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn") - DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb") - DeployProviderTypeHuaweiCloudSCM = DeployProviderType("huaweicloud-scm") - DeployProviderTypeHuaweiCloudWAF = DeployProviderType("huaweicloud-waf") - DeployProviderTypeJDCloudALB = DeployProviderType("jdcloud-alb") - DeployProviderTypeJDCloudCDN = DeployProviderType("jdcloud-cdn") - DeployProviderTypeJDCloudLive = DeployProviderType("jdcloud-live") - DeployProviderTypeJDCloudVOD = DeployProviderType("jdcloud-vod") - DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret") - DeployProviderTypeLocal = DeployProviderType("local") - DeployProviderTypeQiniuCDN = DeployProviderType("qiniu-cdn") - DeployProviderTypeQiniuKodo = DeployProviderType("qiniu-kodo") - DeployProviderTypeQiniuPili = DeployProviderType("qiniu-pili") - DeployProviderTypeSafeLine = DeployProviderType("safeline") - DeployProviderTypeSSH = DeployProviderType("ssh") - DeployProviderTypeTencentCloudCDN = DeployProviderType("tencentcloud-cdn") - DeployProviderTypeTencentCloudCLB = DeployProviderType("tencentcloud-clb") - DeployProviderTypeTencentCloudCOS = DeployProviderType("tencentcloud-cos") - DeployProviderTypeTencentCloudCSS = DeployProviderType("tencentcloud-css") - DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn") - DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo") - DeployProviderTypeTencentCloudSCF = DeployProviderType("tencentcloud-scf") - DeployProviderTypeTencentCloudSSL = DeployProviderType("tencentcloud-ssl") - DeployProviderTypeTencentCloudSSLDeploy = DeployProviderType("tencentcloud-ssldeploy") - DeployProviderTypeTencentCloudVOD = DeployProviderType("tencentcloud-vod") - DeployProviderTypeTencentCloudWAF = DeployProviderType("tencentcloud-waf") - DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn") - DeployProviderTypeUCloudUS3 = DeployProviderType("ucloud-us3") - DeployProviderTypeUpyunCDN = DeployProviderType("upyun-cdn") - DeployProviderTypeUpyunFile = DeployProviderType("upyun-file") - DeployProviderTypeVolcEngineALB = DeployProviderType("volcengine-alb") - DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn") - DeployProviderTypeVolcEngineCertCenter = DeployProviderType("volcengine-certcenter") - DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb") - DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn") - DeployProviderTypeVolcEngineImageX = DeployProviderType("volcengine-imagex") - DeployProviderTypeVolcEngineLive = DeployProviderType("volcengine-live") - DeployProviderTypeVolcEngineTOS = DeployProviderType("volcengine-tos") - DeployProviderTypeWebhook = DeployProviderType("webhook") + ACMEDns01ProviderTypeACMEHttpReq = ACMEDns01ProviderType(AccessProviderTypeACMEHttpReq) + ACMEDns01ProviderTypeAliyun = ACMEDns01ProviderType(AccessProviderTypeAliyun) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAliyunDNS] + ACMEDns01ProviderTypeAliyunDNS = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-dns") + ACMEDns01ProviderTypeAliyunESA = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-esa") + ACMEDns01ProviderTypeAWS = ACMEDns01ProviderType(AccessProviderTypeAWS) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAWSRoute53] + ACMEDns01ProviderTypeAWSRoute53 = ACMEDns01ProviderType(AccessProviderTypeAWS + "-route53") + ACMEDns01ProviderTypeAzure = ACMEDns01ProviderType(AccessProviderTypeAzure) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAzure] + ACMEDns01ProviderTypeAzureDNS = ACMEDns01ProviderType(AccessProviderTypeAzure + "-dns") + ACMEDns01ProviderTypeBaiduCloud = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeBaiduCloudDNS] + ACMEDns01ProviderTypeBaiduCloudDNS = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud + "-dns") + ACMEDns01ProviderTypeBunny = ACMEDns01ProviderType(AccessProviderTypeBunny) + ACMEDns01ProviderTypeCloudflare = ACMEDns01ProviderType(AccessProviderTypeCloudflare) + ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS) + ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud) + ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC) + ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA) + ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6) + ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore) + ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname) + ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy) + ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS] + ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns") + ACMEDns01ProviderTypeJDCloud = ACMEDns01ProviderType(AccessProviderTypeJDCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeJDCloudDNS] + ACMEDns01ProviderTypeJDCloudDNS = ACMEDns01ProviderType(AccessProviderTypeJDCloud + "-dns") + ACMEDns01ProviderTypeNamecheap = ACMEDns01ProviderType(AccessProviderTypeNamecheap) + ACMEDns01ProviderTypeNameDotCom = ACMEDns01ProviderType(AccessProviderTypeNameDotCom) + ACMEDns01ProviderTypeNameSilo = ACMEDns01ProviderType(AccessProviderTypeNameSilo) + ACMEDns01ProviderTypeNS1 = ACMEDns01ProviderType(AccessProviderTypeNS1) + ACMEDns01ProviderTypePorkbun = ACMEDns01ProviderType(AccessProviderTypePorkbun) + ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS) + ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun) + ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS] + ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns") + ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo") + ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel) + ACMEDns01ProviderTypeVolcEngine = ACMEDns01ProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ACMEDns01ProviderTypeVolcEngineDNS] + ACMEDns01ProviderTypeVolcEngineDNS = ACMEDns01ProviderType(AccessProviderTypeVolcEngine + "-dns") + ACMEDns01ProviderTypeWestcn = ACMEDns01ProviderType(AccessProviderTypeWestcn) +) + +type DeploymentProviderType string + +/* +部署证书主机提供商常量值。 +短横线前的部分始终等于授权提供商类型。 + + 注意:如果追加新的常量值,请保持以 ASCII 排序。 + NOTICE: If you add new constant, please keep ASCII order. +*/ +const ( + DeploymentProviderType1PanelConsole = DeploymentProviderType(AccessProviderType1Panel + "-console") + DeploymentProviderType1PanelSite = DeploymentProviderType(AccessProviderType1Panel + "-site") + DeploymentProviderTypeAliyunALB = DeploymentProviderType(AccessProviderTypeAliyun + "-alb") + DeploymentProviderTypeAliyunAPIGW = DeploymentProviderType(AccessProviderTypeAliyun + "-apigw") + DeploymentProviderTypeAliyunCAS = DeploymentProviderType(AccessProviderTypeAliyun + "-cas") + DeploymentProviderTypeAliyunCASDeploy = DeploymentProviderType(AccessProviderTypeAliyun + "-casdeploy") + DeploymentProviderTypeAliyunCDN = DeploymentProviderType(AccessProviderTypeAliyun + "-cdn") + DeploymentProviderTypeAliyunCLB = DeploymentProviderType(AccessProviderTypeAliyun + "-clb") + DeploymentProviderTypeAliyunDCDN = DeploymentProviderType(AccessProviderTypeAliyun + "-dcdn") + DeploymentProviderTypeAliyunDDoS = DeploymentProviderType(AccessProviderTypeAliyun + "-ddos") + DeploymentProviderTypeAliyunESA = DeploymentProviderType(AccessProviderTypeAliyun + "-esa") + DeploymentProviderTypeAliyunFC = DeploymentProviderType(AccessProviderTypeAliyun + "-fc") + DeploymentProviderTypeAliyunLive = DeploymentProviderType(AccessProviderTypeAliyun + "-live") + DeploymentProviderTypeAliyunNLB = DeploymentProviderType(AccessProviderTypeAliyun + "-nlb") + DeploymentProviderTypeAliyunOSS = DeploymentProviderType(AccessProviderTypeAliyun + "-oss") + DeploymentProviderTypeAliyunVOD = DeploymentProviderType(AccessProviderTypeAliyun + "-vod") + DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf") + DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm") + DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront") + DeploymentProviderTypeAzureKeyVault = DeploymentProviderType(AccessProviderTypeAzure + "-keyvault") + DeploymentProviderTypeBaiduCloudAppBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-appblb") + DeploymentProviderTypeBaiduCloudBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-blb") + DeploymentProviderTypeBaiduCloudCDN = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-cdn") + DeploymentProviderTypeBaiduCloudCert = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-cert") + DeploymentProviderTypeBaishanCDN = DeploymentProviderType(AccessProviderTypeBaishan + "-cdn") + DeploymentProviderTypeBaotaPanelConsole = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-console") + DeploymentProviderTypeBaotaPanelSite = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-site") + DeploymentProviderTypeBunnyCDN = DeploymentProviderType(AccessProviderTypeBunny + "-cdn") + DeploymentProviderTypeBytePlusCDN = DeploymentProviderType(AccessProviderTypeBytePlus + "-cdn") + DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly) + DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly) + DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn") + DeploymentProviderTypeEdgioApplications = DeploymentProviderType(AccessProviderTypeEdgio + "-applications") + DeploymentProviderTypeGcoreCDN = DeploymentProviderType(AccessProviderTypeGcore + "-cdn") + DeploymentProviderTypeGoEdge = DeploymentProviderType(AccessProviderTypeGoEdge) + DeploymentProviderTypeHuaweiCloudCDN = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-cdn") + DeploymentProviderTypeHuaweiCloudELB = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-elb") + DeploymentProviderTypeHuaweiCloudSCM = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-scm") + DeploymentProviderTypeHuaweiCloudWAF = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-waf") + DeploymentProviderTypeJDCloudALB = DeploymentProviderType(AccessProviderTypeJDCloud + "-alb") + DeploymentProviderTypeJDCloudCDN = DeploymentProviderType(AccessProviderTypeJDCloud + "-cdn") + DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live") + DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod") + DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret") + DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal) + DeploymentProviderTypeProxmoxVE = DeploymentProviderType(AccessProviderTypeProxmoxVE) + DeploymentProviderTypeQiniuCDN = DeploymentProviderType(AccessProviderTypeQiniu + "-cdn") + DeploymentProviderTypeQiniuKodo = DeploymentProviderType(AccessProviderTypeQiniu + "-kodo") + DeploymentProviderTypeQiniuPili = DeploymentProviderType(AccessProviderTypeQiniu + "-pili") + DeploymentProviderTypeRainYunRCDN = DeploymentProviderType(AccessProviderTypeRainYun + "-rcdn") + DeploymentProviderTypeSafeLine = DeploymentProviderType(AccessProviderTypeSafeLine) + DeploymentProviderTypeSSH = DeploymentProviderType(AccessProviderTypeSSH) + DeploymentProviderTypeTencentCloudCDN = DeploymentProviderType(AccessProviderTypeTencentCloud + "-cdn") + DeploymentProviderTypeTencentCloudCLB = DeploymentProviderType(AccessProviderTypeTencentCloud + "-clb") + DeploymentProviderTypeTencentCloudCOS = DeploymentProviderType(AccessProviderTypeTencentCloud + "-cos") + DeploymentProviderTypeTencentCloudCSS = DeploymentProviderType(AccessProviderTypeTencentCloud + "-css") + DeploymentProviderTypeTencentCloudECDN = DeploymentProviderType(AccessProviderTypeTencentCloud + "-ecdn") + DeploymentProviderTypeTencentCloudEO = DeploymentProviderType(AccessProviderTypeTencentCloud + "-eo") + DeploymentProviderTypeTencentCloudSCF = DeploymentProviderType(AccessProviderTypeTencentCloud + "-scf") + DeploymentProviderTypeTencentCloudSSL = DeploymentProviderType(AccessProviderTypeTencentCloud + "-ssl") + DeploymentProviderTypeTencentCloudSSLDeploy = DeploymentProviderType(AccessProviderTypeTencentCloud + "-ssldeploy") + DeploymentProviderTypeTencentCloudVOD = DeploymentProviderType(AccessProviderTypeTencentCloud + "-vod") + DeploymentProviderTypeTencentCloudWAF = DeploymentProviderType(AccessProviderTypeTencentCloud + "-waf") + DeploymentProviderTypeUCloudUCDN = DeploymentProviderType(AccessProviderTypeUCloud + "-ucdn") + DeploymentProviderTypeUCloudUS3 = DeploymentProviderType(AccessProviderTypeUCloud + "-us3") + DeploymentProviderTypeUpyunCDN = DeploymentProviderType(AccessProviderTypeUpyun + "-cdn") + DeploymentProviderTypeUpyunFile = DeploymentProviderType(AccessProviderTypeUpyun + "-file") + DeploymentProviderTypeVolcEngineALB = DeploymentProviderType(AccessProviderTypeVolcEngine + "-alb") + DeploymentProviderTypeVolcEngineCDN = DeploymentProviderType(AccessProviderTypeVolcEngine + "-cdn") + DeploymentProviderTypeVolcEngineCertCenter = DeploymentProviderType(AccessProviderTypeVolcEngine + "-certcenter") + DeploymentProviderTypeVolcEngineCLB = DeploymentProviderType(AccessProviderTypeVolcEngine + "-clb") + DeploymentProviderTypeVolcEngineDCDN = DeploymentProviderType(AccessProviderTypeVolcEngine + "-dcdn") + DeploymentProviderTypeVolcEngineImageX = DeploymentProviderType(AccessProviderTypeVolcEngine + "-imagex") + DeploymentProviderTypeVolcEngineLive = DeploymentProviderType(AccessProviderTypeVolcEngine + "-live") + DeploymentProviderTypeVolcEngineTOS = DeploymentProviderType(AccessProviderTypeVolcEngine + "-tos") + DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro") + DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook) +) + +type NotificationProviderType string + +/* +消息通知提供商常量值。 +短横线前的部分始终等于授权提供商类型。 + + 注意:如果追加新的常量值,请保持以 ASCII 排序。 + NOTICE: If you add new constant, please keep ASCII order. +*/ +const ( + NotificationProviderTypeDingTalkBot = NotificationProviderType(AccessProviderTypeDingTalkBot) + NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail) + NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot) + NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost) + NotificationProviderTypeTelegram = NotificationProviderType(AccessProviderTypeTelegram) + NotificationProviderTypeWebhook = NotificationProviderType(AccessProviderTypeWebhook) + NotificationProviderTypeWeComBot = NotificationProviderType(AccessProviderTypeWeComBot) ) diff --git a/internal/domain/settings.go b/internal/domain/settings.go index ebe6b9d7..7063ed83 100644 --- a/internal/domain/settings.go +++ b/internal/domain/settings.go @@ -13,6 +13,7 @@ type Settings struct { Content string `json:"content" db:"content"` } +// Deprecated: v0.4.x 将废弃 type NotifyTemplatesSettingsContent struct { NotifyTemplates []struct { Subject string `json:"subject"` @@ -20,8 +21,10 @@ type NotifyTemplatesSettingsContent struct { } `json:"notifyTemplates"` } +// Deprecated: v0.4.x 将废弃 type NotifyChannelsSettingsContent map[string]map[string]any +// Deprecated: v0.4.x 将废弃 func (s *Settings) GetNotifyChannelConfig(channel string) (map[string]any, error) { conf := &NotifyChannelsSettingsContent{} if err := json.Unmarshal([]byte(s.Content), conf); err != nil { diff --git a/internal/domain/workflow.go b/internal/domain/workflow.go index 50069865..6f3cccea 100644 --- a/internal/domain/workflow.go +++ b/internal/domain/workflow.go @@ -3,7 +3,7 @@ package domain import ( "time" - "github.com/usual2970/certimate/internal/pkg/utils/maputil" + maputil "github.com/usual2970/certimate/internal/pkg/utils/map" ) const CollectionNameWorkflow = "workflow" @@ -62,19 +62,23 @@ type WorkflowNode struct { } type WorkflowNodeConfigForApply struct { - Domains string `json:"domains"` // 域名列表,以半角分号分隔 - ContactEmail string `json:"contactEmail"` // 联系邮箱 - ChallengeType string `json:"challengeType"` // TODO: 验证方式。目前仅支持 dns-01 - Provider string `json:"provider"` // DNS 提供商 - ProviderAccessId string `json:"providerAccessId"` // DNS 提供商授权记录 ID - ProviderConfig map[string]any `json:"providerConfig"` // DNS 提供商额外配置 - KeyAlgorithm string `json:"keyAlgorithm"` // 密钥算法 - Nameservers string `json:"nameservers"` // DNS 服务器列表,以半角分号分隔 - DnsPropagationTimeout int32 `json:"dnsPropagationTimeout"` // DNS 传播超时时间(零值取决于提供商的默认值) - DnsTTL int32 `json:"dnsTTL"` // DNS TTL(零值取决于提供商的默认值) - DisableFollowCNAME bool `json:"disableFollowCNAME"` // 是否关闭 CNAME 跟随 - DisableARI bool `json:"disableARI"` // 是否关闭 ARI - SkipBeforeExpiryDays int32 `json:"skipBeforeExpiryDays"` // 证书到期前多少天前跳过续期(零值将使用默认值 30) + Domains string `json:"domains"` // 域名列表,以半角分号分隔 + ContactEmail string `json:"contactEmail"` // 联系邮箱 + ChallengeType string `json:"challengeType"` // TODO: 验证方式。目前仅支持 dns-01 + Provider string `json:"provider"` // DNS 提供商 + ProviderAccessId string `json:"providerAccessId"` // DNS 提供商授权记录 ID + ProviderConfig map[string]any `json:"providerConfig"` // DNS 提供商额外配置 + CAProvider string `json:"caProvider,omitempty"` // CA 提供商(零值将使用全局配置) + CAProviderAccessId string `json:"caProviderAccessId,omitempty"` // CA 提供商授权记录 ID + CAProviderConfig map[string]any `json:"caProviderConfig,omitempty"` // CA 提供商额外配置 + KeyAlgorithm string `json:"keyAlgorithm"` // 证书算法 + Nameservers string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔 + DnsPropagationWait int32 `json:"dnsPropagationWait,omitempty"` // DNS 传播等待时间,等同于 lego 的 `--dns-propagation-wait` 参数 + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` // DNS 传播检查超时时间(零值取决于提供商的默认值) + DnsTTL int32 `json:"dnsTTL,omitempty"` // DNS 解析记录 TTL(零值取决于提供商的默认值) + DisableFollowCNAME bool `json:"disableFollowCNAME,omitempty"` // 是否关闭 CNAME 跟随 + DisableARI bool `json:"disableARI,omitempty"` // 是否关闭 ARI + SkipBeforeExpiryDays int32 `json:"skipBeforeExpiryDays,omitempty"` // 证书到期前多少天前跳过续期(零值将使用默认值 30) } type WorkflowNodeConfigForUpload struct { @@ -84,86 +88,74 @@ type WorkflowNodeConfigForUpload struct { } type WorkflowNodeConfigForDeploy struct { - Certificate string `json:"certificate"` // 前序节点输出的证书,形如“${NodeId}#certificate” - Provider string `json:"provider"` // 主机提供商 - ProviderAccessId string `json:"providerAccessId"` // 主机提供商授权记录 ID - ProviderConfig map[string]any `json:"providerConfig"` // 主机提供商额外配置 - SkipOnLastSucceeded bool `json:"skipOnLastSucceeded"` // 上次部署成功时是否跳过 + Certificate string `json:"certificate"` // 前序节点输出的证书,形如“${NodeId}#certificate” + Provider string `json:"provider"` // 主机提供商 + ProviderAccessId string `json:"providerAccessId,omitempty"` // 主机提供商授权记录 ID + ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 主机提供商额外配置 + SkipOnLastSucceeded bool `json:"skipOnLastSucceeded"` // 上次部署成功时是否跳过 } type WorkflowNodeConfigForNotify struct { - Channel string `json:"channel"` // 通知渠道 - Subject string `json:"subject"` // 通知主题 - Message string `json:"message"` // 通知内容 -} - -func (n *WorkflowNode) getConfigString(key string) string { - return maputil.GetString(n.Config, key) -} - -func (n *WorkflowNode) getConfigBool(key string) bool { - return maputil.GetBool(n.Config, key) -} - -func (n *WorkflowNode) getConfigInt32(key string) int32 { - return maputil.GetInt32(n.Config, key) -} - -func (n *WorkflowNode) getConfigMap(key string) map[string]any { - if val, ok := n.Config[key]; ok { - if result, ok := val.(map[string]any); ok { - return result - } - } - - return make(map[string]any) + Channel string `json:"channel,omitempty"` // Deprecated: v0.4.x 将废弃 + Provider string `json:"provider"` // 通知提供商 + ProviderAccessId string `json:"providerAccessId"` // 通知提供商授权记录 ID + ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 通知提供商额外配置 + Subject string `json:"subject"` // 通知主题 + Message string `json:"message"` // 通知内容 } func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply { - skipBeforeExpiryDays := n.getConfigInt32("skipBeforeExpiryDays") + skipBeforeExpiryDays := maputil.GetInt32(n.Config, "skipBeforeExpiryDays") if skipBeforeExpiryDays == 0 { skipBeforeExpiryDays = 30 } return WorkflowNodeConfigForApply{ - Domains: n.getConfigString("domains"), - ContactEmail: n.getConfigString("contactEmail"), - Provider: n.getConfigString("provider"), - ProviderAccessId: n.getConfigString("providerAccessId"), - ProviderConfig: n.getConfigMap("providerConfig"), - KeyAlgorithm: n.getConfigString("keyAlgorithm"), - Nameservers: n.getConfigString("nameservers"), - DnsPropagationTimeout: n.getConfigInt32("dnsPropagationTimeout"), - DnsTTL: n.getConfigInt32("dnsTTL"), - DisableFollowCNAME: n.getConfigBool("disableFollowCNAME"), - DisableARI: n.getConfigBool("disableARI"), + Domains: maputil.GetString(n.Config, "domains"), + ContactEmail: maputil.GetString(n.Config, "contactEmail"), + Provider: maputil.GetString(n.Config, "provider"), + ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"), + ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"), + CAProvider: maputil.GetString(n.Config, "caProvider"), + CAProviderAccessId: maputil.GetString(n.Config, "caProviderAccessId"), + CAProviderConfig: maputil.GetKVMapAny(n.Config, "caProviderConfig"), + KeyAlgorithm: maputil.GetString(n.Config, "keyAlgorithm"), + Nameservers: maputil.GetString(n.Config, "nameservers"), + DnsPropagationWait: maputil.GetInt32(n.Config, "dnsPropagationWait"), + DnsPropagationTimeout: maputil.GetInt32(n.Config, "dnsPropagationTimeout"), + DnsTTL: maputil.GetInt32(n.Config, "dnsTTL"), + DisableFollowCNAME: maputil.GetBool(n.Config, "disableFollowCNAME"), + DisableARI: maputil.GetBool(n.Config, "disableARI"), SkipBeforeExpiryDays: skipBeforeExpiryDays, } } func (n *WorkflowNode) GetConfigForUpload() WorkflowNodeConfigForUpload { return WorkflowNodeConfigForUpload{ - Certificate: n.getConfigString("certificate"), - PrivateKey: n.getConfigString("privateKey"), - Domains: n.getConfigString("domains"), + Certificate: maputil.GetString(n.Config, "certificate"), + PrivateKey: maputil.GetString(n.Config, "privateKey"), + Domains: maputil.GetString(n.Config, "domains"), } } func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy { return WorkflowNodeConfigForDeploy{ - Certificate: n.getConfigString("certificate"), - Provider: n.getConfigString("provider"), - ProviderAccessId: n.getConfigString("providerAccessId"), - ProviderConfig: n.getConfigMap("providerConfig"), - SkipOnLastSucceeded: n.getConfigBool("skipOnLastSucceeded"), + Certificate: maputil.GetString(n.Config, "certificate"), + Provider: maputil.GetString(n.Config, "provider"), + ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"), + ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"), + SkipOnLastSucceeded: maputil.GetBool(n.Config, "skipOnLastSucceeded"), } } func (n *WorkflowNode) GetConfigForNotify() WorkflowNodeConfigForNotify { return WorkflowNodeConfigForNotify{ - Channel: n.getConfigString("channel"), - Subject: n.getConfigString("subject"), - Message: n.getConfigString("message"), + Channel: maputil.GetString(n.Config, "channel"), + Provider: maputil.GetString(n.Config, "provider"), + ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"), + ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"), + Subject: maputil.GetString(n.Config, "subject"), + Message: maputil.GetString(n.Config, "message"), } } diff --git a/internal/notify/notifier.go b/internal/notify/notifier.go new file mode 100644 index 00000000..955e88c3 --- /dev/null +++ b/internal/notify/notifier.go @@ -0,0 +1,72 @@ +package notify + +import ( + "context" + "fmt" + "log/slog" + + "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/pkg/core/notifier" + "github.com/usual2970/certimate/internal/repository" +) + +type Notifier interface { + Notify(ctx context.Context) error +} + +type NotifierWithWorkflowNodeConfig struct { + Node *domain.WorkflowNode + Logger *slog.Logger + Subject string + Message string +} + +func NewWithWorkflowNode(config NotifierWithWorkflowNodeConfig) (Notifier, error) { + if config.Node == nil { + return nil, fmt.Errorf("node is nil") + } + if config.Node.Type != domain.WorkflowNodeTypeNotify { + return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeNotify)) + } + + nodeConfig := config.Node.GetConfigForNotify() + options := ¬ifierProviderOptions{ + Provider: domain.NotificationProviderType(nodeConfig.Provider), + ProviderAccessConfig: make(map[string]any), + ProviderExtendedConfig: nodeConfig.ProviderConfig, + } + + accessRepo := repository.NewAccessRepository() + if nodeConfig.ProviderAccessId != "" { + access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId) + if err != nil { + return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err) + } else { + options.ProviderAccessConfig = access.Config + } + } + + notifierProvider, err := createNotifierProvider(options) + if err != nil { + return nil, err + } + + return ¬ifierImpl{ + provider: notifierProvider.WithLogger(config.Logger), + subject: config.Subject, + message: config.Message, + }, nil +} + +type notifierImpl struct { + provider notifier.Notifier + subject string + message string +} + +var _ Notifier = (*notifierImpl)(nil) + +func (n *notifierImpl) Notify(ctx context.Context) error { + _, err := n.provider.Notify(ctx, n.subject, n.message) + return err +} diff --git a/internal/notify/notify.go b/internal/notify/notify.go index d4b42ec9..92970341 100644 --- a/internal/notify/notify.go +++ b/internal/notify/notify.go @@ -9,10 +9,11 @@ import ( "github.com/usual2970/certimate/internal/domain" "github.com/usual2970/certimate/internal/pkg/core/notifier" - "github.com/usual2970/certimate/internal/pkg/utils/maputil" + maputil "github.com/usual2970/certimate/internal/pkg/utils/map" "github.com/usual2970/certimate/internal/repository" ) +// Deprecated: v0.4.x 将废弃 func SendToAllChannels(subject, message string) error { notifiers, err := getEnabledNotifiers() if err != nil { @@ -38,8 +39,9 @@ func SendToAllChannels(subject, message string) error { return err } +// Deprecated: v0.4.x 将废弃 func SendToChannel(subject, message string, channel string, channelConfig map[string]any) error { - notifier, err := createNotifier(domain.NotifyChannelType(channel), channelConfig) + notifier, err := createNotifierProviderUseGlobalSettings(domain.NotifyChannelType(channel), channelConfig) if err != nil { return err } @@ -48,6 +50,7 @@ func SendToChannel(subject, message string, channel string, channelConfig map[st return err } +// Deprecated: v0.4.x 将废弃 func getEnabledNotifiers() ([]notifier.Notifier, error) { settingsRepo := repository.NewSettingsRepository() settings, err := settingsRepo.GetByName(context.Background(), "notifyChannels") @@ -66,7 +69,7 @@ func getEnabledNotifiers() ([]notifier.Notifier, error) { continue } - notifier, err := createNotifier(domain.NotifyChannelType(k), v) + notifier, err := createNotifierProviderUseGlobalSettings(domain.NotifyChannelType(k), v) if err != nil { continue } diff --git a/internal/notify/providers.go b/internal/notify/providers.go index 66927390..c57b9c82 100644 --- a/internal/notify/providers.go +++ b/internal/notify/providers.go @@ -2,76 +2,152 @@ package notify import ( "fmt" + "net/http" "github.com/usual2970/certimate/internal/domain" "github.com/usual2970/certimate/internal/pkg/core/notifier" - pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark" pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk" pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email" pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark" - pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan" + pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost" pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram" pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook" pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom" - "github.com/usual2970/certimate/internal/pkg/utils/maputil" + httputil "github.com/usual2970/certimate/internal/pkg/utils/http" + maputil "github.com/usual2970/certimate/internal/pkg/utils/map" ) -func createNotifier(channel domain.NotifyChannelType, channelConfig map[string]any) (notifier.Notifier, error) { +type notifierProviderOptions struct { + Provider domain.NotificationProviderType + ProviderAccessConfig map[string]any + ProviderExtendedConfig map[string]any +} + +func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier, error) { /* 注意:如果追加新的常量值,请保持以 ASCII 排序。 NOTICE: If you add new constant, please keep ASCII order. */ - switch channel { - case domain.NotifyChannelTypeBark: - return pBark.NewNotifier(&pBark.NotifierConfig{ - DeviceKey: maputil.GetString(channelConfig, "deviceKey"), - ServerUrl: maputil.GetString(channelConfig, "serverUrl"), - }) + switch options.Provider { + case domain.NotificationProviderTypeDingTalkBot: + { + access := domain.AccessConfigForDingTalkBot{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } - case domain.NotifyChannelTypeDingTalk: - return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{ - AccessToken: maputil.GetString(channelConfig, "accessToken"), - Secret: maputil.GetString(channelConfig, "secret"), - }) + return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{ + WebhookUrl: access.WebhookUrl, + Secret: access.Secret, + }) + } - case domain.NotifyChannelTypeEmail: - return pEmail.NewNotifier(&pEmail.NotifierConfig{ - SmtpHost: maputil.GetString(channelConfig, "smtpHost"), - SmtpPort: maputil.GetInt32(channelConfig, "smtpPort"), - SmtpTLS: maputil.GetOrDefaultBool(channelConfig, "smtpTLS", true), - Username: maputil.GetOrDefaultString(channelConfig, "username", maputil.GetString(channelConfig, "senderAddress")), - Password: maputil.GetString(channelConfig, "password"), - SenderAddress: maputil.GetString(channelConfig, "senderAddress"), - ReceiverAddress: maputil.GetString(channelConfig, "receiverAddress"), - }) + case domain.NotificationProviderTypeEmail: + { + access := domain.AccessConfigForEmail{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } - case domain.NotifyChannelTypeLark: - return pLark.NewNotifier(&pLark.NotifierConfig{ - WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"), - }) + return pEmail.NewNotifier(&pEmail.NotifierConfig{ + SmtpHost: access.SmtpHost, + SmtpPort: access.SmtpPort, + SmtpTls: access.SmtpTls, + Username: access.Username, + Password: access.Password, + SenderAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "senderAddress", access.DefaultSenderAddress), + ReceiverAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "receiverAddress", access.DefaultReceiverAddress), + }) + } - case domain.NotifyChannelTypeServerChan: - return pServerChan.NewNotifier(&pServerChan.NotifierConfig{ - Url: maputil.GetString(channelConfig, "url"), - }) + case domain.NotificationProviderTypeLarkBot: + { + access := domain.AccessConfigForLarkBot{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } - case domain.NotifyChannelTypeTelegram: - return pTelegram.NewNotifier(&pTelegram.NotifierConfig{ - ApiToken: maputil.GetString(channelConfig, "apiToken"), - ChatId: maputil.GetInt64(channelConfig, "chatId"), - }) + return pLark.NewNotifier(&pLark.NotifierConfig{ + WebhookUrl: access.WebhookUrl, + }) + } - case domain.NotifyChannelTypeWebhook: - return pWebhook.NewNotifier(&pWebhook.NotifierConfig{ - Url: maputil.GetString(channelConfig, "url"), - AllowInsecureConnections: maputil.GetBool(channelConfig, "allowInsecureConnections"), - }) + case domain.NotificationProviderTypeMattermost: + { + access := domain.AccessConfigForMattermost{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } - case domain.NotifyChannelTypeWeCom: - return pWeCom.NewNotifier(&pWeCom.NotifierConfig{ - WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"), - }) + return pMattermost.NewNotifier(&pMattermost.NotifierConfig{ + ServerUrl: access.ServerUrl, + Username: access.Username, + Password: access.Password, + ChannelId: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "channelId", access.DefaultChannelId), + }) + } + + case domain.NotificationProviderTypeTelegram: + { + access := domain.AccessConfigForTelegram{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + return pTelegram.NewNotifier(&pTelegram.NotifierConfig{ + BotToken: access.BotToken, + ChatId: maputil.GetOrDefaultInt64(options.ProviderExtendedConfig, "chatId", access.DefaultChatId), + }) + } + + case domain.NotificationProviderTypeWebhook: + { + access := domain.AccessConfigForWebhook{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + mergedHeaders := make(map[string]string) + if defaultHeadersString := access.HeadersString; defaultHeadersString != "" { + h, err := httputil.ParseHeaders(defaultHeadersString) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook headers: %w", err) + } + for key := range h { + mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) + } + } + if extendedHeadersString := maputil.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" { + h, err := httputil.ParseHeaders(extendedHeadersString) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook headers: %w", err) + } + for key := range h { + mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) + } + } + + return pWebhook.NewNotifier(&pWebhook.NotifierConfig{ + WebhookUrl: access.Url, + WebhookData: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForNotification), + Method: access.Method, + Headers: mergedHeaders, + AllowInsecureConnections: access.AllowInsecureConnections, + }) + } + + case domain.NotificationProviderTypeWeComBot: + { + access := domain.AccessConfigForWeComBot{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + return pWeCom.NewNotifier(&pWeCom.NotifierConfig{ + WebhookUrl: access.WebhookUrl, + }) + } } - return nil, fmt.Errorf("unsupported notifier channel: %s", channelConfig) + return nil, fmt.Errorf("unsupported notifier provider '%s'", options.Provider) } diff --git a/internal/notify/providers_deprecated.go b/internal/notify/providers_deprecated.go new file mode 100644 index 00000000..1e862866 --- /dev/null +++ b/internal/notify/providers_deprecated.go @@ -0,0 +1,108 @@ +package notify + +import ( + "fmt" + + "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/pkg/core/notifier" + pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark" + pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk" + pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email" + pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify" + pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark" + pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost" + pPushover "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover" + pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus" + pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan" + pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram" + pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook" + pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom" + maputil "github.com/usual2970/certimate/internal/pkg/utils/map" +) + +// Deprecated: v0.4.x 将废弃 +func createNotifierProviderUseGlobalSettings(channel domain.NotifyChannelType, channelConfig map[string]any) (notifier.Notifier, error) { + /* + 注意:如果追加新的常量值,请保持以 ASCII 排序。 + NOTICE: If you add new constant, please keep ASCII order. + */ + switch channel { + case domain.NotifyChannelTypeBark: + return pBark.NewNotifier(&pBark.NotifierConfig{ + DeviceKey: maputil.GetString(channelConfig, "deviceKey"), + ServerUrl: maputil.GetString(channelConfig, "serverUrl"), + }) + + case domain.NotifyChannelTypeDingTalk: + return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{ + WebhookUrl: "https://oapi.dingtalk.com/robot/send?access_token=" + maputil.GetString(channelConfig, "accessToken"), + Secret: maputil.GetString(channelConfig, "secret"), + }) + + case domain.NotifyChannelTypeEmail: + return pEmail.NewNotifier(&pEmail.NotifierConfig{ + SmtpHost: maputil.GetString(channelConfig, "smtpHost"), + SmtpPort: maputil.GetInt32(channelConfig, "smtpPort"), + SmtpTls: maputil.GetOrDefaultBool(channelConfig, "smtpTLS", true), + Username: maputil.GetOrDefaultString(channelConfig, "username", maputil.GetString(channelConfig, "senderAddress")), + Password: maputil.GetString(channelConfig, "password"), + SenderAddress: maputil.GetString(channelConfig, "senderAddress"), + ReceiverAddress: maputil.GetString(channelConfig, "receiverAddress"), + }) + + case domain.NotifyChannelTypeGotify: + return pGotify.NewNotifier(&pGotify.NotifierConfig{ + Url: maputil.GetString(channelConfig, "url"), + Token: maputil.GetString(channelConfig, "token"), + Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1), + }) + + case domain.NotifyChannelTypeLark: + return pLark.NewNotifier(&pLark.NotifierConfig{ + WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"), + }) + + case domain.NotifyChannelTypeMattermost: + return pMattermost.NewNotifier(&pMattermost.NotifierConfig{ + ServerUrl: maputil.GetString(channelConfig, "serverUrl"), + ChannelId: maputil.GetString(channelConfig, "channelId"), + Username: maputil.GetString(channelConfig, "username"), + Password: maputil.GetString(channelConfig, "password"), + }) + + case domain.NotifyChannelTypePushover: + return pPushover.NewNotifier(&pPushover.NotifierConfig{ + Token: maputil.GetString(channelConfig, "token"), + User: maputil.GetString(channelConfig, "user"), + }) + + case domain.NotifyChannelTypePushPlus: + return pPushPlus.NewNotifier(&pPushPlus.NotifierConfig{ + Token: maputil.GetString(channelConfig, "token"), + }) + + case domain.NotifyChannelTypeServerChan: + return pServerChan.NewNotifier(&pServerChan.NotifierConfig{ + Url: maputil.GetString(channelConfig, "url"), + }) + + case domain.NotifyChannelTypeTelegram: + return pTelegram.NewNotifier(&pTelegram.NotifierConfig{ + BotToken: maputil.GetString(channelConfig, "apiToken"), + ChatId: maputil.GetInt64(channelConfig, "chatId"), + }) + + case domain.NotifyChannelTypeWebhook: + return pWebhook.NewNotifier(&pWebhook.NotifierConfig{ + WebhookUrl: maputil.GetString(channelConfig, "url"), + AllowInsecureConnections: maputil.GetBool(channelConfig, "allowInsecureConnections"), + }) + + case domain.NotifyChannelTypeWeCom: + return pWeCom.NewNotifier(&pWeCom.NotifierConfig{ + WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"), + }) + } + + return nil, fmt.Errorf("unsupported notifier channel '%s'", channelConfig) +} diff --git a/internal/notify/service.go b/internal/notify/service.go index 9b7cc416..1d1f6c25 100644 --- a/internal/notify/service.go +++ b/internal/notify/service.go @@ -8,25 +8,30 @@ import ( "github.com/usual2970/certimate/internal/domain/dtos" ) +// Deprecated: v0.4.x 将废弃 const ( notifyTestTitle = "测试通知" notifyTestBody = "欢迎使用 Certimate ,这是一条测试通知。" ) +// Deprecated: v0.4.x 将废弃 type settingsRepository interface { GetByName(ctx context.Context, name string) (*domain.Settings, error) } +// Deprecated: v0.4.x 将废弃 type NotifyService struct { settingsRepo settingsRepository } +// Deprecated: v0.4.x 将废弃 func NewNotifyService(settingsRepo settingsRepository) *NotifyService { return &NotifyService{ settingsRepo: settingsRepo, } } +// Deprecated: v0.4.x 将废弃 func (n *NotifyService) Test(ctx context.Context, req *dtos.NotifyTestPushReq) error { settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels") if err != nil { diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq/acmehttpreq.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq/acmehttpreq.go index ab2b11a6..bdd16234 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq/acmehttpreq.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq/acmehttpreq.go @@ -1,4 +1,4 @@ -package acmehttpreq +package acmehttpreq import ( "net/url" diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/aliyun_esa.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/aliyun_esa.go new file mode 100644 index 00000000..bf7026da --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/aliyun_esa.go @@ -0,0 +1,40 @@ +package aliyunesa + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + + internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/internal" +) + +type ChallengeProviderConfig struct { + AccessKeyId string `json:"accessKeyId"` + AccessKeySecret string `json:"accessKeySecret"` + Region string `json:"region"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := internal.NewDefaultConfig() + providerConfig.SecretID = config.AccessKeyId + providerConfig.SecretKey = config.AccessKeySecret + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = config.DnsTTL + } + + provider, err := internal.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/internal/lego.go new file mode 100644 index 00000000..79df4083 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/internal/lego.go @@ -0,0 +1,266 @@ +package lego_aliyunesa + +import ( + "errors" + "fmt" + "strings" + "sync" + "time" + + aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" + aliesa "github.com/alibabacloud-go/esa-20240910/v2/client" + "github.com/alibabacloud-go/tea/tea" + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/config/env" +) + +const ( + envNamespace = "ALICLOUDESA_" + + EnvAccessKey = envNamespace + "ACCESS_KEY" + EnvSecretKey = envNamespace + "SECRET_KEY" + EnvRegionID = envNamespace + "REGION_ID" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" + EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" +) + +var _ challenge.ProviderTimeout = (*DNSProvider)(nil) + +type Config struct { + SecretID string + SecretKey string + RegionID string + + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int32 + HTTPTimeout time.Duration +} + +type DNSProvider struct { + client *aliesa.Client + config *Config + + siteIDs map[string]int64 + siteIDsMtx sync.Mutex +} + +func NewDefaultConfig() *Config { + return &Config{ + TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), + } +} + +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAccessKey, EnvSecretKey, EnvRegionID) + if err != nil { + return nil, fmt.Errorf("alicloud-esa: %w", err) + } + + config := NewDefaultConfig() + config.SecretID = values[EnvAccessKey] + config.SecretKey = values[EnvSecretKey] + config.RegionID = values[EnvRegionID] + + return NewDNSProviderConfig(config) +} + +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("alicloud-esa: the configuration of the DNS provider is nil") + } + + client, err := aliesa.NewClient(&aliopen.Config{ + AccessKeyId: tea.String(config.SecretID), + AccessKeySecret: tea.String(config.SecretKey), + Endpoint: tea.String(fmt.Sprintf("esa.%s.aliyuncs.com", config.RegionID)), + }) + if err != nil { + return nil, fmt.Errorf("alicloud-esa: %w", err) + } + + return &DNSProvider{ + client: client, + config: config, + }, nil +} + +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err) + } + + siteId, err := d.getSiteId(authZone) + if err != nil { + return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err) + } + + if err := d.addOrUpdateDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, "."), info.Value); err != nil { + return fmt.Errorf("alicloud-esa: %w", err) + } + + return nil +} + +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err) + } + + siteId, err := d.getSiteId(authZone) + if err != nil { + return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err) + } + + if err := d.removeDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, ".")); err != nil { + return fmt.Errorf("alicloud-esa: %w", err) + } + + return nil +} + +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +func (d *DNSProvider) getSiteId(siteName string) (int64, error) { + d.siteIDsMtx.Lock() + siteID, ok := d.siteIDs[siteName] + d.siteIDsMtx.Unlock() + if ok { + return siteID, nil + } + + pageNumber := 1 + pageSize := 500 + for { + request := &aliesa.ListSitesRequest{ + SiteName: tea.String(siteName), + PageNumber: tea.Int32(int32(pageNumber)), + PageSize: tea.Int32(int32(pageNumber)), + AccessType: tea.String("NS"), + } + response, err := d.client.ListSites(request) + if err != nil { + return 0, err + } + + if response.Body == nil { + break + } else { + for _, record := range response.Body.Sites { + if tea.StringValue(record.SiteName) == siteName { + d.siteIDsMtx.Lock() + d.siteIDs[siteName] = *record.SiteId + d.siteIDsMtx.Unlock() + return *record.SiteId, nil + } + } + + if len(response.Body.Sites) < pageSize { + break + } + + pageNumber++ + } + } + + return 0, errors.New("failed to get site id") +} + +func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN string) (*aliesa.ListRecordsResponseBodyRecords, error) { + pageNumber := 1 + pageSize := 500 + for { + request := &aliesa.ListRecordsRequest{ + SiteId: tea.Int64(siteId), + Type: tea.String("TXT"), + RecordName: tea.String(effectiveFQDN), + PageNumber: tea.Int32(int32(pageNumber)), + PageSize: tea.Int32(int32(pageNumber)), + } + response, err := d.client.ListRecords(request) + if err != nil { + return nil, err + } + + if response.Body == nil { + break + } else { + for _, record := range response.Body.Records { + if tea.StringValue(record.RecordName) == effectiveFQDN { + return record, nil + } + } + + if len(response.Body.Records) < pageSize { + break + } + + pageNumber++ + } + } + + return nil, nil +} + +func (d *DNSProvider) addOrUpdateDNSRecord(siteId int64, effectiveFQDN, value string) error { + record, err := d.findDNSRecord(siteId, effectiveFQDN) + if err != nil { + return err + } + + if record == nil { + request := &aliesa.CreateRecordRequest{ + SiteId: tea.Int64(siteId), + Type: tea.String("TXT"), + RecordName: tea.String(effectiveFQDN), + Data: &aliesa.CreateRecordRequestData{ + Value: tea.String(value), + }, + Ttl: tea.Int32(d.config.TTL), + } + _, err := d.client.CreateRecord(request) + return err + } else { + request := &aliesa.UpdateRecordRequest{ + RecordId: record.RecordId, + Ttl: tea.Int32(d.config.TTL), + Data: &aliesa.UpdateRecordRequestData{ + Value: tea.String(value), + }, + } + _, err := d.client.UpdateRecord(request) + return err + } +} + +func (d *DNSProvider) removeDNSRecord(siteId int64, effectiveFQDN string) error { + record, err := d.findDNSRecord(siteId, effectiveFQDN) + if err != nil { + return err + } + + if record == nil { + return nil + } else { + request := &aliesa.DeleteRecordRequest{ + RecordId: record.RecordId, + } + _, err = d.client.DeleteRecord(request) + return err + } +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns/azure-dns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns/azure-dns.go index eaf46bce..d8d54c90 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns/azure-dns.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns/azure-dns.go @@ -6,7 +6,7 @@ import ( "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/azuredns" - azcommon "github.com/usual2970/certimate/internal/pkg/vendors/azure-sdk/common" + azcommon "github.com/usual2970/certimate/internal/pkg/sdk3rd/azure/common" ) type ChallengeProviderConfig struct { diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go index da157bd0..f67662b5 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go @@ -1,4 +1,4 @@ -package lego_baiducloud +package lego_baiducloud import ( "errors" @@ -89,7 +89,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("baiducloud: %w", err) + return fmt.Errorf("baiducloud: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) @@ -109,7 +109,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("baiducloud: %w", err) + return fmt.Errorf("baiducloud: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) @@ -128,7 +128,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } -func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*bcedns.Record, error) { +func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*bcedns.Record, error) { pageMarker := "" pageSize := 1000 for { @@ -159,7 +159,7 @@ func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*bcedns.Record, } func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error { - record, err := d.getDNSRecord(zoneName, subDomain) + record, err := d.findDNSRecord(zoneName, subDomain) if err != nil { return err } @@ -186,7 +186,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er } func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error { - record, err := d.getDNSRecord(zoneName, subDomain) + record, err := d.findDNSRecord(zoneName, subDomain) if err != nil { return err } diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/bunny/bunny.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/bunny/bunny.go new file mode 100644 index 00000000..1f4fdffe --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/bunny/bunny.go @@ -0,0 +1,36 @@ +package bunny + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/bunny" +) + +type ChallengeProviderConfig struct { + ApiKey string `json:"apiKey"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := bunny.NewDefaultConfig() + providerConfig.APIKey = config.ApiKey + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := bunny.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare/cloudflare.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare/cloudflare.go index 9782b39b..cdfc1313 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare/cloudflare.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare/cloudflare.go @@ -9,6 +9,7 @@ import ( type ChallengeProviderConfig struct { DnsApiToken string `json:"dnsApiToken"` + ZoneApiToken string `json:"zoneApiToken,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } @@ -20,6 +21,7 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, providerConfig := cloudflare.NewDefaultConfig() providerConfig.AuthToken = config.DnsApiToken + providerConfig.ZoneToken = config.ZoneApiToken if config.DnsPropagationTimeout != 0 { providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second } diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/internal/lego.go index 0329d18d..6bccb1dc 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/internal/lego.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/internal/lego.go @@ -93,7 +93,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("cmccecloud: %w", err) + return fmt.Errorf("cmccecloud: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName) @@ -108,33 +108,33 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } if record == nil { - // add new record resp, err := d.client.CreateRecordOpenapi(&model.CreateRecordOpenapiRequest{ CreateRecordOpenapiBody: &model.CreateRecordOpenapiBody{ LineId: "0", // 默认线路 Rr: subDomain, DomainName: readDomain, - Description: "from certimate", + Description: "certimate acme", Type: model.CreateRecordOpenapiBodyTypeEnumTxt, Value: info.Value, Ttl: &d.config.TTL, }, }) if err != nil { - return fmt.Errorf("lego: %w", err) + return fmt.Errorf("cmccecloud: %w", err) } + if resp.State != model.CreateRecordOpenapiResponseStateEnumOk { - return fmt.Errorf("lego: create record failed, response state: %s, message: %s, code: %s", resp.State, resp.ErrorMessage, resp.ErrorCode) + return fmt.Errorf("cmccecloud: create record failed, response state: %s, message: %s, code: %s", resp.State, resp.ErrorMessage, resp.ErrorCode) } + return nil } else { - // update record resp, err := d.client.ModifyRecordOpenapi(&model.ModifyRecordOpenapiRequest{ ModifyRecordOpenapiBody: &model.ModifyRecordOpenapiBody{ RecordId: record.RecordId, Rr: subDomain, DomainName: readDomain, - Description: "from certmate", + Description: "certmate acme", LineId: "0", Type: model.ModifyRecordOpenapiBodyTypeEnumTxt, Value: info.Value, @@ -142,44 +142,52 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { }, }) if err != nil { - return fmt.Errorf("lego: %w", err) + return fmt.Errorf("cmccecloud: %w", err) } + if resp.State != model.ModifyRecordOpenapiResponseStateEnumOk { - return fmt.Errorf("lego: create record failed, response state: %s", resp.State) + return fmt.Errorf("cmccecloud: create record failed, response state: %s", resp.State) } + return nil } } func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { challengeInfo := dns01.GetChallengeInfo(domain, keyAuth) + zoneName, err := dns01.FindZoneByFqdn(challengeInfo.FQDN) if err != nil { - return fmt.Errorf("cmccecloud: %w", err) + return fmt.Errorf("cmccecloud: could not find zone for domain %q: %w", domain, err) } + subDomain, err := dns01.ExtractSubDomain(challengeInfo.FQDN, zoneName) if err != nil { return fmt.Errorf("cmccecloud: %w", err) } + readDomain := strings.Trim(zoneName, ".") record, err := d.getDomainRecord(readDomain, subDomain) if err != nil { return err } + if record == nil { return nil + } else { + resp, err := d.client.DeleteRecordOpenapi(&model.DeleteRecordOpenapiRequest{ + DeleteRecordOpenapiBody: &model.DeleteRecordOpenapiBody{ + RecordIdList: []string{record.RecordId}, + }, + }) + if err != nil { + return fmt.Errorf("cmccecloud: %w", err) + } + if resp.State != model.DeleteRecordOpenapiResponseStateEnumOk { + return fmt.Errorf("cmccecloud: delete record failed, unexpected response state: %s", resp.State) + } } - resp, err := d.client.DeleteRecordOpenapi(&model.DeleteRecordOpenapiRequest{ - DeleteRecordOpenapiBody: &model.DeleteRecordOpenapiBody{ - RecordIdList: []string{record.RecordId}, - }, - }) - if err != nil { - return fmt.Errorf("lego: %w", err) - } - if resp.State != model.DeleteRecordOpenapiResponseStateEnumOk { - return fmt.Errorf("lego: delete record failed, response state: %s", resp.State) - } + return nil } @@ -205,8 +213,9 @@ func (d *DNSProvider) getDomainRecord(domain string, rr string) (*model.ListReco } if resp.State != model.ListRecordOpenapiResponseStateEnumOk { respStr, _ := json.Marshal(resp) - return nil, fmt.Errorf("request error. %s", string(respStr)) + return nil, fmt.Errorf("cmccecloud: request error: %s", string(respStr)) } + if resp.Body.Data != nil { for _, item := range *resp.Body.Data { if item.Rr == rr { @@ -214,9 +223,11 @@ func (d *DNSProvider) getDomainRecord(domain string, rr string) (*model.ListReco } } } + if resp.Body.TotalPages == nil || page >= *resp.Body.TotalPages { return nil, nil } + page++ } } diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/internal/lego.go index 1b9603bd..87cb6cd9 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/internal/lego.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/internal/lego.go @@ -1,4 +1,4 @@ -package lego_dnsla +package lego_dnsla import ( "errors" @@ -10,7 +10,7 @@ import ( "github.com/go-acme/lego/v4/challenge/dns01" "github.com/go-acme/lego/v4/platform/config/env" - dnslasdk "github.com/usual2970/certimate/internal/pkg/vendors/dnsla-sdk" + dnslasdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/dnsla" ) const ( @@ -83,7 +83,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("dnsla: %w", err) + return fmt.Errorf("dnsla: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) @@ -103,7 +103,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("dnsla: %w", err) + return fmt.Errorf("dnsla: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6/internal/lego.go index f83949a2..8b33cf9e 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6/internal/lego.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6/internal/lego.go @@ -1,4 +1,4 @@ -package lego_dynv6 +package lego_dynv6 import ( "context" @@ -76,7 +76,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("dynv6: %w", err) + return fmt.Errorf("dynv6: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) @@ -96,7 +96,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("dynv6: %w", err) + return fmt.Errorf("dynv6: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) @@ -115,7 +115,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } -func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*libdns.Record, error) { +func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*libdns.Record, error) { records, err := d.client.GetRecords(context.Background(), zoneName) if err != nil { return nil, err @@ -131,7 +131,7 @@ func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*libdns.Record, } func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error { - record, err := d.getDNSRecord(zoneName, subDomain) + record, err := d.findDNSRecord(zoneName, subDomain) if err != nil { return err } @@ -153,7 +153,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er } func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error { - record, err := d.getDNSRecord(zoneName, subDomain) + record, err := d.findDNSRecord(zoneName, subDomain) if err != nil { return err } diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go index 3c9b1b13..7f1f5670 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go @@ -1,4 +1,4 @@ -package lego_gname +package lego_gname import ( "errors" @@ -9,7 +9,7 @@ import ( "github.com/go-acme/lego/v4/challenge/dns01" "github.com/go-acme/lego/v4/platform/config/env" - gnamesdk "github.com/usual2970/certimate/internal/pkg/vendors/gname-sdk" + gnamesdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/gname" ) const ( @@ -82,7 +82,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("gname: %w", err) + return fmt.Errorf("gname: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) @@ -102,7 +102,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("gname: %w", err) + return fmt.Errorf("gname: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) @@ -121,7 +121,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } -func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*gnamesdk.ResolutionRecord, error) { +func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*gnamesdk.ResolutionRecord, error) { page := int32(1) pageSize := int32(20) for { @@ -155,7 +155,7 @@ func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*gnamesdk.Resolu } func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error { - record, err := d.getDNSRecord(zoneName, subDomain) + record, err := d.findDNSRecord(zoneName, subDomain) if err != nil { return err } @@ -186,7 +186,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er } func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error { - record, err := d.getDNSRecord(zoneName, subDomain) + record, err := d.findDNSRecord(zoneName, subDomain) if err != nil { return err } diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go index d5b5277b..a1851a11 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go @@ -1,4 +1,4 @@ -package lego_jdcloud +package lego_jdcloud import ( "errors" @@ -91,7 +91,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("jdcloud: %w", err) + return fmt.Errorf("jdcloud: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) @@ -111,7 +111,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { - return fmt.Errorf("jdcloud: %w", err) + return fmt.Errorf("jdcloud: could not find zone for domain %q: %w", domain, err) } subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go index e5275efe..7630633c 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go @@ -1,6 +1,8 @@ package powerdns import ( + "crypto/tls" + "net/http" "net/url" "time" @@ -9,10 +11,11 @@ import ( ) type ChallengeProviderConfig struct { - ApiUrl string `json:"apiUrl"` - ApiKey string `json:"apiKey"` - DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` - DnsTTL int32 `json:"dnsTTL,omitempty"` + ApiUrl string `json:"apiUrl"` + ApiKey string `json:"apiKey"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` } func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { @@ -24,6 +27,13 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, providerConfig := pdns.NewDefaultConfig() providerConfig.Host = host providerConfig.APIKey = config.ApiKey + if config.AllowInsecureConnections { + providerConfig.HTTPClient.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + } if config.DnsPropagationTimeout != 0 { providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second } diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/internal/lego.go index 57f74193..692c42d3 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/internal/lego.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/internal/lego.go @@ -1,4 +1,4 @@ -package lego_tencentcloudeo +package lego_tencentcloudeo import ( "errors" @@ -20,7 +20,7 @@ const ( EnvSecretID = envNamespace + "SECRET_ID" EnvSecretKey = envNamespace + "SECRET_KEY" - EnvZoneId = envNamespace + "ZONE_ID" + EnvZoneID = envNamespace + "ZONE_ID" EnvTTL = envNamespace + "TTL" EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" @@ -33,7 +33,7 @@ var _ challenge.ProviderTimeout = (*DNSProvider)(nil) type Config struct { SecretID string SecretKey string - ZoneId string + ZoneID string PropagationTimeout time.Duration PollingInterval time.Duration @@ -56,7 +56,7 @@ func NewDefaultConfig() *Config { } func NewDNSProvider() (*DNSProvider, error) { - values, err := env.Get(EnvSecretID, EnvSecretKey, EnvZoneId) + values, err := env.Get(EnvSecretID, EnvSecretKey, EnvZoneID) if err != nil { return nil, fmt.Errorf("tencentcloud-eo: %w", err) } @@ -64,7 +64,7 @@ func NewDNSProvider() (*DNSProvider, error) { config := NewDefaultConfig() config.SecretID = values[EnvSecretID] config.SecretKey = values[EnvSecretKey] - config.ZoneId = values[EnvSecretKey] + config.ZoneID = values[EnvSecretKey] return NewDNSProviderConfig(config) } @@ -112,12 +112,12 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } -func (d *DNSProvider) getDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error) { +func (d *DNSProvider) findDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error) { pageOffset := 0 pageLimit := 1000 for { request := teo.NewDescribeDnsRecordsRequest() - request.ZoneId = common.StringPtr(d.config.ZoneId) + request.ZoneId = common.StringPtr(d.config.ZoneID) request.Offset = common.Int64Ptr(int64(pageOffset)) request.Limit = common.Int64Ptr(int64(pageLimit)) request.Filters = []*teo.AdvancedFilter{ @@ -141,7 +141,7 @@ func (d *DNSProvider) getDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error) } } - if len(response.Response.DnsRecords) < int(pageLimit) { + if len(response.Response.DnsRecords) < pageLimit { break } @@ -153,14 +153,14 @@ func (d *DNSProvider) getDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error) } func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error { - record, err := d.getDNSRecord(effectiveFQDN) + record, err := d.findDNSRecord(effectiveFQDN) if err != nil { return err } if record == nil { request := teo.NewCreateDnsRecordRequest() - request.ZoneId = common.StringPtr(d.config.ZoneId) + request.ZoneId = common.StringPtr(d.config.ZoneID) request.Name = common.StringPtr(effectiveFQDN) request.Type = common.StringPtr("TXT") request.Content = common.StringPtr(value) @@ -169,8 +169,9 @@ func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error { return err } else { record.Content = common.StringPtr(value) + record.TTL = common.Int64Ptr(int64(d.config.TTL)) request := teo.NewModifyDnsRecordsRequest() - request.ZoneId = common.StringPtr(d.config.ZoneId) + request.ZoneId = common.StringPtr(d.config.ZoneID) request.DnsRecords = []*teo.DnsRecord{record} if _, err := d.client.ModifyDnsRecords(request); err != nil { return err @@ -178,7 +179,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error { if *record.Status == "disable" { request := teo.NewModifyDnsRecordsStatusRequest() - request.ZoneId = common.StringPtr(d.config.ZoneId) + request.ZoneId = common.StringPtr(d.config.ZoneID) request.RecordsToEnable = []*string{record.RecordId} if _, err = d.client.ModifyDnsRecordsStatus(request); err != nil { return err @@ -190,7 +191,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error { } func (d *DNSProvider) removeDNSRecord(effectiveFQDN string) error { - record, err := d.getDNSRecord(effectiveFQDN) + record, err := d.findDNSRecord(effectiveFQDN) if err != nil { return err } @@ -199,7 +200,7 @@ func (d *DNSProvider) removeDNSRecord(effectiveFQDN string) error { return nil } else { request := teo.NewDeleteDnsRecordsRequest() - request.ZoneId = common.StringPtr(d.config.ZoneId) + request.ZoneId = common.StringPtr(d.config.ZoneID) request.RecordIds = []*string{record.RecordId} _, err = d.client.DeleteDnsRecords(request) return err diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/tencentcloud_eo.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/tencentcloud_eo.go index 33552ecf..427c79ea 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/tencentcloud_eo.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/tencentcloud_eo.go @@ -24,7 +24,7 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, providerConfig := internal.NewDefaultConfig() providerConfig.SecretID = config.SecretId providerConfig.SecretKey = config.SecretKey - providerConfig.ZoneId = config.ZoneId + providerConfig.ZoneID = config.ZoneId if config.DnsPropagationTimeout != 0 { providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second } diff --git a/internal/pkg/core/deployer/deployer.go b/internal/pkg/core/deployer/deployer.go index 54b206bd..67ce7ef7 100644 --- a/internal/pkg/core/deployer/deployer.go +++ b/internal/pkg/core/deployer/deployer.go @@ -1,4 +1,4 @@ -package deployer +package deployer import ( "context" @@ -14,13 +14,13 @@ type Deployer interface { // // 入参: // - ctx:上下文。 - // - certPem:证书 PEM 内容。 - // - privkeyPem:私钥 PEM 内容。 + // - certPEM:证书 PEM 内容。 + // - privkeyPEM:私钥 PEM 内容。 // // 出参: // - res:部署结果。 // - err: 错误。 - Deploy(ctx context.Context, certPem string, privkeyPem string) (res *DeployResult, err error) + Deploy(ctx context.Context, certPEM string, privkeyPEM string) (res *DeployResult, err error) } // 表示证书部署结果的数据结构。 diff --git a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go index 91143aa6..069e01f9 100644 --- a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go +++ b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go @@ -1,16 +1,15 @@ -package onepanelconsole +package onepanelconsole import ( "context" "crypto/tls" "errors" + "fmt" "log/slog" "net/url" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" - opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk" + opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel" ) type DeployerConfig struct { @@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -58,11 +57,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 设置面板 SSL 证书 updateSystemSSLReq := &opsdk.UpdateSystemSSLRequest{ - Cert: certPem, - Key: privkeyPem, + Cert: certPEM, + Key: privkeyPEM, SSL: "enable", SSLType: "import-paste", } @@ -74,13 +73,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe updateSystemSSLResp, err := d.sdkClient.UpdateSystemSSL(updateSystemSSLReq) d.logger.Debug("sdk request '1panel.UpdateSystemSSL'", slog.Any("request", updateSystemSSLReq), slog.Any("response", updateSystemSSLResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.UpdateSystemSSL'") + return nil, fmt.Errorf("failed to execute sdk request '1panel.UpdateSystemSSL': %w", err) } return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client, error) { +func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid 1panel api url") } @@ -90,7 +89,7 @@ func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client, } client := opsdk.NewClient(apiUrl, apiKey) - if allowInsecure { + if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go b/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go index d4b7cfa9..abec586c 100644 --- a/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go +++ b/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go @@ -1,4 +1,4 @@ -package onepanelconsole_test +package onepanelconsole_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go index 6aa34607..7d360c77 100644 --- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go +++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go @@ -1,19 +1,18 @@ -package onepanelsite +package onepanelsite import ( "context" "crypto/tls" "errors" + "fmt" "log/slog" "net/url" "strconv" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/1panel-ssl" - opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk" + opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel" ) type DeployerConfig struct { @@ -23,8 +22,14 @@ type DeployerConfig struct { ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` // 网站 ID。 - WebsiteId int64 `json:"websiteId"` + // 部署资源类型为 [RESOURCE_TYPE_WEBSITE] 时必填。 + WebsiteId int64 `json:"websiteId,omitempty"` + // 证书 ID。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。 + CertificateId int64 `json:"certificateId,omitempty"` } type DeployerProvider struct { @@ -43,7 +48,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -51,7 +56,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { ApiKey: config.ApiKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -72,7 +77,31 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_WEBSITE: + if err := d.deployToWebsite(ctx, certPEM, privkeyPEM); err != nil { + return nil, err + } + + case RESOURCE_TYPE_CERTIFICATE: + if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string, privkeyPEM string) error { + if d.config.WebsiteId == 0 { + return errors.New("config `websiteId` is required") + } + // 获取网站 HTTPS 配置 getHttpsConfReq := &opsdk.GetHttpsConfRequest{ WebsiteID: d.config.WebsiteId, @@ -80,13 +109,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq) d.logger.Debug("sdk request '1panel.GetHttpsConf'", slog.Any("request", getHttpsConfReq), slog.Any("response", getHttpsConfResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.GetHttpsConf'") + return fmt.Errorf("failed to execute sdk request '1panel.GetHttpsConf': %w", err) } // 上传证书到面板 - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -106,13 +135,45 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe updateHttpsConfResp, err := d.sdkClient.UpdateHttpsConf(updateHttpsConfReq) d.logger.Debug("sdk request '1panel.UpdateHttpsConf'", slog.Any("request", updateHttpsConfReq), slog.Any("response", updateHttpsConfResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.UpdateHttpsConf'") + return fmt.Errorf("failed to execute sdk request '1panel.UpdateHttpsConf': %w", err) } - return &deployer.DeployResult{}, nil + return nil } -func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client, error) { +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error { + if d.config.CertificateId == 0 { + return errors.New("config `certificateId` is required") + } + + // 获取证书详情 + getWebsiteSSLReq := &opsdk.GetWebsiteSSLRequest{ + SSLID: d.config.CertificateId, + } + getWebsiteSSLResp, err := d.sdkClient.GetWebsiteSSL(getWebsiteSSLReq) + d.logger.Debug("sdk request '1panel.GetWebsiteSSL'", slog.Any("request", getWebsiteSSLReq), slog.Any("response", getWebsiteSSLResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request '1panel.GetWebsiteSSL': %w", err) + } + + // 更新证书 + uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{ + Type: "paste", + SSLID: d.config.CertificateId, + Description: getWebsiteSSLResp.Data.Description, + Certificate: certPEM, + PrivateKey: privkeyPEM, + } + uploadWebsiteSSLResp, err := d.sdkClient.UploadWebsiteSSL(uploadWebsiteSSLReq) + d.logger.Debug("sdk request '1panel.UploadWebsiteSSL'", slog.Any("request", uploadWebsiteSSLReq), slog.Any("response", uploadWebsiteSSLResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request '1panel.UploadWebsiteSSL': %w", err) + } + + return nil +} + +func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid 1panel api url") } @@ -122,7 +183,7 @@ func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client, } client := opsdk.NewClient(apiUrl, apiKey) - if allowInsecure { + if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go b/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go index 1be2444d..702584e3 100644 --- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go +++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go @@ -1,4 +1,4 @@ -package onepanelsite_test +package onepanelsite_test import ( "context" @@ -20,7 +20,7 @@ var ( ) func init() { - argsPrefix := "CERTIMATE_DEPLOYER_1PANELCONSOLE_" + argsPrefix := "CERTIMATE_DEPLOYER_1PANELSITE_" flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") @@ -32,12 +32,12 @@ func init() { /* Shell command to run this test: - go test -v ./1panel_console_test.go -args \ - --CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ - --CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIURL="http://127.0.0.1:20410" \ - --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key" \ - --CERTIMATE_DEPLOYER_1PANELCONSOLE_WEBSITEID="your-website-id" + go test -v ./1panel_site_test.go -args \ + --CERTIMATE_DEPLOYER_1PANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_1PANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_1PANELSITE_APIURL="http://127.0.0.1:20410" \ + --CERTIMATE_DEPLOYER_1PANELSITE_APIKEY="your-api-key" \ + --CERTIMATE_DEPLOYER_1PANELSITE_WEBSITEID="your-website-id" */ func TestDeploy(t *testing.T) { flag.Parse() @@ -55,8 +55,9 @@ func TestDeploy(t *testing.T) { deployer, err := provider.NewDeployer(&provider.DeployerConfig{ ApiUrl: fApiUrl, ApiKey: fApiKey, - WebsiteId: fWebsiteId, AllowInsecureConnections: true, + ResourceType: provider.RESOURCE_TYPE_WEBSITE, + WebsiteId: fWebsiteId, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/internal/pkg/core/deployer/providers/1panel-site/consts.go b/internal/pkg/core/deployer/providers/1panel-site/consts.go new file mode 100644 index 00000000..85f85a0d --- /dev/null +++ b/internal/pkg/core/deployer/providers/1panel-site/consts.go @@ -0,0 +1,10 @@ +package onepanelsite + +type ResourceType string + +const ( + // 资源类型:替换指定网站的证书。 + RESOURCE_TYPE_WEBSITE = ResourceType("website") + // 资源类型:替换指定证书。 + RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate") +) diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go index a019917a..3dca4a9d 100644 --- a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go +++ b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go @@ -1,4 +1,4 @@ -package aliyunalb +package aliyunalb import ( "context" @@ -13,7 +13,6 @@ import ( alicas "github.com/alibabacloud-go/cas-20200407/v3/client" aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "golang.org/x/exp/slices" "github.com/usual2970/certimate/internal/pkg/core/deployer" @@ -62,12 +61,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") + return nil, fmt.Errorf("failed to create sdk clients: %w", err) } uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -88,11 +87,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CAS - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -110,7 +109,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil @@ -129,7 +128,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId getLoadBalancerAttributeResp, err := d.sdkClients.ALB.GetLoadBalancerAttribute(getLoadBalancerAttributeReq) d.logger.Debug("sdk request 'alb.GetLoadBalancerAttribute'", slog.Any("request", getLoadBalancerAttributeReq), slog.Any("response", getLoadBalancerAttributeResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetLoadBalancerAttribute'") + return fmt.Errorf("failed to execute sdk request 'alb.GetLoadBalancerAttribute': %w", err) } // 查询 HTTPS 监听列表 @@ -138,6 +137,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId listListenersLimit := int32(100) var listListenersToken *string = nil for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + listListenersReq := &alialb.ListListenersRequest{ MaxResults: tea.Int32(listListenersLimit), NextToken: listListenersToken, @@ -147,7 +152,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId listListenersResp, err := d.sdkClients.ALB.ListListeners(listListenersReq) d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'") + return fmt.Errorf("failed to execute sdk request 'alb.ListListeners': %w", err) } if listListenersResp.Body.Listeners != nil { @@ -167,6 +172,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId // REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners listListenersToken = nil for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + listListenersReq := &alialb.ListListenersRequest{ MaxResults: tea.Int32(listListenersLimit), NextToken: listListenersToken, @@ -176,7 +187,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId listListenersResp, err := d.sdkClients.ALB.ListListeners(listListenersReq) d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'") + return fmt.Errorf("failed to execute sdk request 'alb.ListListeners': %w", err) } if listListenersResp.Body.Listeners != nil { @@ -235,7 +246,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL getListenerAttributeResp, err := d.sdkClients.ALB.GetListenerAttribute(getListenerAttributeReq) d.logger.Debug("sdk request 'alb.GetListenerAttribute'", slog.Any("request", getListenerAttributeReq), slog.Any("response", getListenerAttributeResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetListenerAttribute'") + return fmt.Errorf("failed to execute sdk request 'alb.GetListenerAttribute': %w", err) } if d.config.Domain == "" { @@ -252,7 +263,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL updateListenerAttributeResp, err := d.sdkClients.ALB.UpdateListenerAttribute(updateListenerAttributeReq) d.logger.Debug("sdk request 'alb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerAttributeResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.UpdateListenerAttribute'") + return fmt.Errorf("failed to execute sdk request 'alb.UpdateListenerAttribute': %w", err) } } else { // 指定 SNI,需部署到扩展域名 @@ -263,6 +274,12 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL listListenerCertificatesLimit := int32(100) var listListenerCertificatesToken *string = nil for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + listListenerCertificatesReq := &alialb.ListListenerCertificatesRequest{ NextToken: listListenerCertificatesToken, MaxResults: tea.Int32(listListenerCertificatesLimit), @@ -272,7 +289,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL listListenerCertificatesResp, err := d.sdkClients.ALB.ListListenerCertificates(listListenerCertificatesReq) d.logger.Debug("sdk request 'alb.ListListenerCertificates'", slog.Any("request", listListenerCertificatesReq), slog.Any("response", listListenerCertificatesResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListenerCertificates'") + return fmt.Errorf("failed to execute sdk request 'alb.ListListenerCertificates': %w", err) } if listListenerCertificatesResp.Body.Certificates != nil { @@ -331,7 +348,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL } } - errs = append(errs, xerrors.Wrap(err, "failed to execute sdk request 'cas.GetUserCertificateDetail'")) + errs = append(errs, fmt.Errorf("failed to execute sdk request 'cas.GetUserCertificateDetail': %w", err)) continue } else { certCNMatched := tea.StringValue(getUserCertificateDetailResp.Body.Common) == d.config.Domain @@ -368,7 +385,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL associateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesFromListenerReq) d.logger.Debug("sdk request 'alb.AssociateAdditionalCertificatesWithListener'", slog.Any("request", associateAdditionalCertificatesFromListenerReq), slog.Any("response", associateAdditionalCertificatesFromListenerResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.AssociateAdditionalCertificatesWithListener'") + return fmt.Errorf("failed to execute sdk request 'alb.AssociateAdditionalCertificatesWithListener': %w", err) } } @@ -389,7 +406,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL dissociateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.DissociateAdditionalCertificatesFromListener(dissociateAdditionalCertificatesFromListenerReq) d.logger.Debug("sdk request 'alb.DissociateAdditionalCertificatesFromListener'", slog.Any("request", dissociateAdditionalCertificatesFromListenerReq), slog.Any("response", dissociateAdditionalCertificatesFromListenerResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.DissociateAdditionalCertificatesFromListener'") + return fmt.Errorf("failed to execute sdk request 'alb.DissociateAdditionalCertificatesFromListener': %w", err) } } } @@ -447,7 +464,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up // 阿里云 CAS 服务接入点是独立于 ALB 服务的 // 国内版固定接入点:华东一杭州 // 国际版固定接入点:亚太东南一新加坡 - if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") { + if !strings.HasPrefix(casRegion, "cn-") { casRegion = "ap-southeast-1" } else { casRegion = "cn-hangzhou" diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go index b5ae776a..c75119e9 100644 --- a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go @@ -1,4 +1,4 @@ -package aliyunalb_test +package aliyunalb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/consts.go b/internal/pkg/core/deployer/providers/aliyun-alb/consts.go index a2d1aacc..286aef00 100644 --- a/internal/pkg/core/deployer/providers/aliyun-alb/consts.go +++ b/internal/pkg/core/deployer/providers/aliyun-alb/consts.go @@ -1,4 +1,4 @@ -package aliyunalb +package aliyunalb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/aliyun-apigw/aliyun_apigw.go b/internal/pkg/core/deployer/providers/aliyun-apigw/aliyun_apigw.go new file mode 100644 index 00000000..d74c7c27 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-apigw/aliyun_apigw.go @@ -0,0 +1,274 @@ +package aliyunapigw + +import ( + "context" + "errors" + "fmt" + "log/slog" + "strings" + "time" + + aliapig "github.com/alibabacloud-go/apig-20240327/v3/client" + alicloudapi "github.com/alibabacloud-go/cloudapi-20160714/v5/client" + aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" + "github.com/alibabacloud-go/tea/tea" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" +) + +type DeployerConfig struct { + // 阿里云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 阿里云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 阿里云地域。 + Region string `json:"region"` + // 服务类型。 + ServiceType ServiceType `json:"serviceType"` + // API 网关 ID。 + // 服务类型为 [SERVICE_TYPE_CLOUDNATIVE] 时必填。 + GatewayId string `json:"gatewayId,omitempty"` + // API 分组 ID。 + // 服务类型为 [SERVICE_TYPE_TRADITIONAL] 时必填。 + GroupId string `json:"groupId,omitempty"` + // 自定义域名(支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClients *wSdkClients + sslUploader uploader.Uploader +} + +type wSdkClients struct { + CloudNativeAPIGateway *aliapig.Client + TraditionalAPIGateway *alicloudapi.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region) + if err != nil { + return nil, fmt.Errorf("failed to create sdk clients: %w", err) + } + + uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region) + if err != nil { + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClients: clients, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + switch d.config.ServiceType { + case SERVICE_TYPE_TRADITIONAL: + if err := d.deployToTraditional(ctx, certPEM, privkeyPEM); err != nil { + return nil, err + } + + case SERVICE_TYPE_CLOUDNATIVE: + if err := d.deployToCloudNative(ctx, certPEM, privkeyPEM); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported service type '%s'", string(d.config.ServiceType)) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToTraditional(ctx context.Context, certPEM string, privkeyPEM string) error { + if d.config.GroupId == "" { + return errors.New("config `groupId` is required") + } + if d.config.Domain == "" { + return errors.New("config `domain` is required") + } + + // 为自定义域名添加 SSL 证书 + // REF: https://help.aliyun.com/zh/api-gateway/traditional-api-gateway/developer-reference/api-cloudapi-2016-07-14-setdomaincertificate + setDomainCertificateReq := &alicloudapi.SetDomainCertificateRequest{ + GroupId: tea.String(d.config.GroupId), + DomainName: tea.String(d.config.Domain), + CertificateName: tea.String(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())), + CertificateBody: tea.String(certPEM), + CertificatePrivateKey: tea.String(privkeyPEM), + } + setDomainCertificateResp, err := d.sdkClients.TraditionalAPIGateway.SetDomainCertificate(setDomainCertificateReq) + d.logger.Debug("sdk request 'apigateway.SetDomainCertificate'", slog.Any("request", setDomainCertificateReq), slog.Any("response", setDomainCertificateResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'apigateway.SetDomainCertificate': %w", err) + } + + return nil +} + +func (d *DeployerProvider) deployToCloudNative(ctx context.Context, certPEM string, privkeyPEM string) error { + if d.config.GatewayId == "" { + return errors.New("config `gatewayId` is required") + } + if d.config.Domain == "" { + return errors.New("config `domain` is required") + } + + // 遍历查询域名列表,获取域名 ID + // REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-listdomains + var domainId string + listDomainsPageNumber := int32(1) + listDomainsPageSize := int32(10) + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + listDomainsReq := &aliapig.ListDomainsRequest{ + GatewayId: tea.String(d.config.GatewayId), + NameLike: tea.String(d.config.Domain), + PageNumber: tea.Int32(listDomainsPageNumber), + PageSize: tea.Int32(listDomainsPageSize), + } + listDomainsResp, err := d.sdkClients.CloudNativeAPIGateway.ListDomains(listDomainsReq) + d.logger.Debug("sdk request 'apig.ListDomains'", slog.Any("request", listDomainsReq), slog.Any("response", listDomainsResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'apig.ListDomains': %w", err) + } + + if listDomainsResp.Body.Data.Items != nil { + for _, domainInfo := range listDomainsResp.Body.Data.Items { + if strings.EqualFold(tea.StringValue(domainInfo.Name), d.config.Domain) { + domainId = tea.StringValue(domainInfo.DomainId) + break + } + } + + if domainId != "" { + break + } + } + + if listDomainsResp.Body.Data.Items == nil || len(listDomainsResp.Body.Data.Items) < int(listDomainsPageSize) { + break + } else { + listDomainsPageNumber++ + } + } + if domainId == "" { + return errors.New("domain not found") + } + + // 查询域名 + // REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-getdomain + getDomainReq := &aliapig.GetDomainRequest{} + getDomainResp, err := d.sdkClients.CloudNativeAPIGateway.GetDomain(tea.String(domainId), getDomainReq) + d.logger.Debug("sdk request 'apig.GetDomain'", slog.Any("domainId", domainId), slog.Any("request", getDomainReq), slog.Any("response", getDomainResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'apig.GetDomain': %w", err) + } + + // 上传证书到 CAS + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return fmt.Errorf("failed to upload certificate file: %w", err) + } else { + d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + } + + // 更新域名 + // REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-updatedomain + updateDomainReq := &aliapig.UpdateDomainRequest{ + Protocol: tea.String("HTTPS"), + ForceHttps: getDomainResp.Body.Data.ForceHttps, + MTLSEnabled: getDomainResp.Body.Data.MTLSEnabled, + Http2Option: getDomainResp.Body.Data.Http2Option, + TlsMin: getDomainResp.Body.Data.TlsMin, + TlsMax: getDomainResp.Body.Data.TlsMax, + TlsCipherSuitesConfig: getDomainResp.Body.Data.TlsCipherSuitesConfig, + CertIdentifier: tea.String(upres.ExtendedData["certIdentifier"].(string)), + } + updateDomainResp, err := d.sdkClients.CloudNativeAPIGateway.UpdateDomain(tea.String(domainId), updateDomainReq) + d.logger.Debug("sdk request 'apig.UpdateDomain'", slog.Any("domainId", domainId), slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'apig.UpdateDomain': %w", err) + } + + return nil +} + +func createSdkClients(accessKeyId, accessKeySecret, region string) (*wSdkClients, error) { + // 接入点一览 https://api.aliyun.com/product/APIG + cloudNativeAPIGEndpoint := fmt.Sprintf("apig.%s.aliyuncs.com", region) + cloudNativeAPIGConfig := &aliopen.Config{ + AccessKeyId: tea.String(accessKeyId), + AccessKeySecret: tea.String(accessKeySecret), + Endpoint: tea.String(cloudNativeAPIGEndpoint), + } + cloudNativeAPIGClient, err := aliapig.NewClient(cloudNativeAPIGConfig) + if err != nil { + return nil, err + } + + // 接入点一览 https://api.aliyun.com/product/CloudAPI + traditionalAPIGEndpoint := fmt.Sprintf("apigateway.%s.aliyuncs.com", region) + traditionalAPIGConfig := &aliopen.Config{ + AccessKeyId: tea.String(accessKeyId), + AccessKeySecret: tea.String(accessKeySecret), + Endpoint: tea.String(traditionalAPIGEndpoint), + } + traditionalAPIGClient, err := alicloudapi.NewClient(traditionalAPIGConfig) + if err != nil { + return nil, err + } + + return &wSdkClients{ + CloudNativeAPIGateway: cloudNativeAPIGClient, + TraditionalAPIGateway: traditionalAPIGClient, + }, nil +} + +func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Uploader, error) { + casRegion := region + if casRegion != "" { + // 阿里云 CAS 服务接入点是独立于 APIGateway 服务的 + // 国内版固定接入点:华东一杭州 + // 国际版固定接入点:亚太东南一新加坡 + if !strings.HasPrefix(casRegion, "cn-") { + casRegion = "ap-southeast-1" + } else { + casRegion = "cn-hangzhou" + } + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + Region: casRegion, + }) + return uploader, err +} diff --git a/internal/pkg/core/deployer/providers/aliyun-apigw/aliyun_apigw_test.go b/internal/pkg/core/deployer/providers/aliyun-apigw/aliyun_apigw_test.go new file mode 100644 index 00000000..7807a927 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-apigw/aliyun_apigw_test.go @@ -0,0 +1,95 @@ +package aliyunapigw_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-apigw" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fRegion string + fServiceType string + fGatewayId string + fGroupId string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNAPIGW_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fRegion, argsPrefix+"REGION", "", "") + flag.StringVar(&fGatewayId, argsPrefix+"GATEWARYID", "", "") + flag.StringVar(&fGroupId, argsPrefix+"GROUPID", "", "") + flag.StringVar(&fServiceType, argsPrefix+"SERVICETYPE", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./aliyun_apigw_test.go -args \ + --CERTIMATE_DEPLOYER_ALIYUNAPIGW_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_ALIYUNAPIGW_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_ALIYUNAPIGW_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_ALIYUNAPIGW_ACCESSKEYSECRET="your-access-key-secret" \ + --CERTIMATE_DEPLOYER_ALIYUNAPIGW_REGION="cn-hangzhou" \ + --CERTIMATE_DEPLOYER_ALIYUNAPIGW_GATEWAYID="your-api-gateway-id" \ + --CERTIMATE_DEPLOYER_ALIYUNAPIGW_GROUPID="your-api-group-id" \ + --CERTIMATE_DEPLOYER_ALIYUNAPIGW_SERVICETYPE="cloudnative" \ + --CERTIMATE_DEPLOYER_ALIYUNAPIGW_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("REGION: %v", fRegion), + fmt.Sprintf("GATEWAYID: %v", fGatewayId), + fmt.Sprintf("GROUPID: %v", fGroupId), + fmt.Sprintf("SERVICETYPE: %v", fServiceType), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Region: fRegion, + ServiceType: provider.ServiceType(fServiceType), + GatewayId: fGatewayId, + GroupId: fGroupId, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/aliyun-apigw/consts.go b/internal/pkg/core/deployer/providers/aliyun-apigw/consts.go new file mode 100644 index 00000000..1abe97e3 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-apigw/consts.go @@ -0,0 +1,10 @@ +package aliyunapigw + +type ServiceType string + +const ( + // 服务类型:原 API 网关。 + SERVICE_TYPE_TRADITIONAL = ServiceType("traditional") + // 服务类型:云原生 API 网关。 + SERVICE_TYPE_CLOUDNATIVE = ServiceType("cloudnative") +) diff --git a/internal/pkg/core/deployer/providers/aliyun-cas-deploy/aliyun_cas_deploy.go b/internal/pkg/core/deployer/providers/aliyun-cas-deploy/aliyun_cas_deploy.go index b50e8c5d..077dea5c 100644 --- a/internal/pkg/core/deployer/providers/aliyun-cas-deploy/aliyun_cas_deploy.go +++ b/internal/pkg/core/deployer/providers/aliyun-cas-deploy/aliyun_cas_deploy.go @@ -1,4 +1,4 @@ -package aliyuncasdeploy +package aliyuncasdeploy import ( "context" @@ -11,7 +11,6 @@ import ( alicas "github.com/alibabacloud-go/cas-20200407/v3/client" aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" @@ -48,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -57,7 +56,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -78,15 +77,15 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if len(d.config.ResourceIds) == 0 { return nil, errors.New("config `resourceIds` is required") } // 上传证书到 CAS - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -101,7 +100,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe listContactResp, err := d.sdkClient.ListContact(listContactReq) d.logger.Debug("sdk request 'cas.ListContact'", slog.Any("request", listContactReq), slog.Any("response", listContactResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cas.ListContact'") + return nil, fmt.Errorf("failed to execute sdk request 'cas.ListContact': %w", err) } if len(listContactResp.Body.ContactList) > 0 { @@ -121,14 +120,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe createDeploymentJobResp, err := d.sdkClient.CreateDeploymentJob(createDeploymentJobReq) d.logger.Debug("sdk request 'cas.CreateDeploymentJob'", slog.Any("request", createDeploymentJobReq), slog.Any("response", createDeploymentJobResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cas.CreateDeploymentJob'") + return nil, fmt.Errorf("failed to execute sdk request 'cas.CreateDeploymentJob': %w", err) } // 循环获取部署任务详情,等待任务状态变更 // REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-describedeploymentjob for { - if ctx.Err() != nil { + select { + case <-ctx.Done(): return nil, ctx.Err() + default: } describeDeploymentJobReq := &alicas.DescribeDeploymentJobRequest{ @@ -137,7 +138,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe describeDeploymentJobResp, err := d.sdkClient.DescribeDeploymentJob(describeDeploymentJobReq) d.logger.Debug("sdk request 'cas.DescribeDeploymentJob'", slog.Any("request", describeDeploymentJobReq), slog.Any("response", describeDeploymentJobResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cas.DescribeDeploymentJob'") + return nil, fmt.Errorf("failed to execute sdk request 'cas.DescribeDeploymentJob': %w", err) } if describeDeploymentJobResp.Body.Status == nil || *describeDeploymentJobResp.Body.Status == "editing" { diff --git a/internal/pkg/core/deployer/providers/aliyun-cas/aliyun_cas.go b/internal/pkg/core/deployer/providers/aliyun-cas/aliyun_cas.go index e00d3788..56681e57 100644 --- a/internal/pkg/core/deployer/providers/aliyun-cas/aliyun_cas.go +++ b/internal/pkg/core/deployer/providers/aliyun-cas/aliyun_cas.go @@ -1,11 +1,10 @@ -package aliyuncas +package aliyuncas import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" @@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -59,11 +58,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CAS - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } diff --git a/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn.go b/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn.go index 2a9be884..ce5f9fd8 100644 --- a/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn.go +++ b/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn.go @@ -1,4 +1,4 @@ -package aliyuncdn +package aliyuncdn import ( "context" @@ -10,7 +10,6 @@ import ( alicdn "github.com/alibabacloud-go/cdn-20180510/v5/client" aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" ) @@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -58,7 +57,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // "*.example.com" → ".example.com",适配阿里云 CDN 要求的泛域名格式 domain := strings.TrimPrefix(d.config.Domain, "*") @@ -69,13 +68,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), CertType: tea.String("upload"), SSLProtocol: tea.String("on"), - SSLPub: tea.String(certPem), - SSLPri: tea.String(privkeyPem), + SSLPub: tea.String(certPEM), + SSLPri: tea.String(privkeyPEM), } setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificate(setCdnDomainSSLCertificateReq) d.logger.Debug("sdk request 'cdn.SetCdnDomainSSLCertificate'", slog.Any("request", setCdnDomainSSLCertificateReq), slog.Any("response", setCdnDomainSSLCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.SetCdnDomainSSLCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.SetCdnDomainSSLCertificate': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn_test.go b/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn_test.go index 1f92947f..b07611da 100644 --- a/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn_test.go @@ -1,4 +1,4 @@ -package aliyuncdn_test +package aliyuncdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go index 2fcdd0f0..41a78968 100644 --- a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go +++ b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go @@ -1,15 +1,15 @@ -package aliyunclb +package aliyunclb import ( "context" "errors" "fmt" "log/slog" + "strings" aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" alislb "github.com/alibabacloud-go/slb-20140515/v4/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" @@ -52,16 +52,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } - uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ - AccessKeyId: config.AccessKeyId, - AccessKeySecret: config.AccessKeySecret, - Region: config.Region, - }) + uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -82,11 +78,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SLB - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -104,7 +100,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil @@ -124,7 +120,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeLoadBalancerAttributeResp, err := d.sdkClient.DescribeLoadBalancerAttribute(describeLoadBalancerAttributeReq) d.logger.Debug("sdk request 'slb.DescribeLoadBalancerAttribute'", slog.Any("request", describeLoadBalancerAttributeReq), slog.Any("response", describeLoadBalancerAttributeResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerAttribute'") + return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerAttribute': %w", err) } // 查询 HTTPS 监听列表 @@ -133,6 +129,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeLoadBalancerListenersLimit := int32(100) var describeLoadBalancerListenersToken *string = nil for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + describeLoadBalancerListenersReq := &alislb.DescribeLoadBalancerListenersRequest{ RegionId: tea.String(d.config.Region), MaxResults: tea.Int32(describeLoadBalancerListenersLimit), @@ -143,7 +145,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeLoadBalancerListenersResp, err := d.sdkClient.DescribeLoadBalancerListeners(describeLoadBalancerListenersReq) d.logger.Debug("sdk request 'slb.DescribeLoadBalancerListeners'", slog.Any("request", describeLoadBalancerListenersReq), slog.Any("response", describeLoadBalancerListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerListeners'") + return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerListeners': %w", err) } if describeLoadBalancerListenersResp.Body.Listeners != nil { @@ -167,8 +169,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId var errs []error for _, listenerPort := range listenerPorts { - if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + + default: + if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -206,7 +214,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL describeLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.DescribeLoadBalancerHTTPSListenerAttribute(describeLoadBalancerHTTPSListenerAttributeReq) d.logger.Debug("sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute'", slog.Any("request", describeLoadBalancerHTTPSListenerAttributeReq), slog.Any("response", describeLoadBalancerHTTPSListenerAttributeResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute'") + return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute': %w", err) } if d.config.Domain == "" { @@ -223,7 +231,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.SetLoadBalancerHTTPSListenerAttribute(setLoadBalancerHTTPSListenerAttributeReq) d.logger.Debug("sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute'", slog.Any("request", setLoadBalancerHTTPSListenerAttributeReq), slog.Any("response", setLoadBalancerHTTPSListenerAttributeResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute'") + return fmt.Errorf("failed to execute sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute': %w", err) } } else { // 指定 SNI,需部署到扩展域名 @@ -238,7 +246,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL describeDomainExtensionsResp, err := d.sdkClient.DescribeDomainExtensions(describeDomainExtensionsReq) d.logger.Debug("sdk request 'slb.DescribeDomainExtensions'", slog.Any("request", describeDomainExtensionsReq), slog.Any("response", describeDomainExtensionsResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeDomainExtensions'") + return fmt.Errorf("failed to execute sdk request 'slb.DescribeDomainExtensions': %w", err) } // 遍历修改扩展域名 @@ -259,7 +267,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL setDomainExtensionAttributeResp, err := d.sdkClient.SetDomainExtensionAttribute(setDomainExtensionAttributeReq) d.logger.Debug("sdk request 'slb.SetDomainExtensionAttribute'", slog.Any("request", setDomainExtensionAttributeReq), slog.Any("response", setDomainExtensionAttributeResp)) if err != nil { - errs = append(errs, xerrors.Wrap(err, "failed to execute sdk request 'slb.SetDomainExtensionAttribute'")) + errs = append(errs, fmt.Errorf("failed to execute sdk request 'slb.SetDomainExtensionAttribute': %w", err)) continue } } @@ -300,3 +308,24 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*alislb.Clien return client, nil } + +func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Uploader, error) { + casRegion := region + if casRegion != "" { + // 阿里云 CAS 服务接入点是独立于 CLB 服务的 + // 国内版固定接入点:华东一杭州 + // 国际版固定接入点:亚太东南一新加坡 + if !strings.HasPrefix(casRegion, "cn-") { + casRegion = "ap-southeast-1" + } else { + casRegion = "cn-hangzhou" + } + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + Region: casRegion, + }) + return uploader, err +} diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go index f688edf9..3b8ce12d 100644 --- a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go @@ -1,4 +1,4 @@ -package aliyunclb_test +package aliyunclb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/consts.go b/internal/pkg/core/deployer/providers/aliyun-clb/consts.go index ad4fb070..9d901095 100644 --- a/internal/pkg/core/deployer/providers/aliyun-clb/consts.go +++ b/internal/pkg/core/deployer/providers/aliyun-clb/consts.go @@ -1,4 +1,4 @@ -package aliyunclb +package aliyunclb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn.go b/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn.go index b9ba9f23..4eb411fd 100644 --- a/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn.go +++ b/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn.go @@ -1,4 +1,4 @@ -package aliyundcdn +package aliyundcdn import ( "context" @@ -10,7 +10,6 @@ import ( aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" alidcdn "github.com/alibabacloud-go/dcdn-20180115/v3/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" ) @@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -58,7 +57,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // "*.example.com" → ".example.com",适配阿里云 DCDN 要求的泛域名格式 domain := strings.TrimPrefix(d.config.Domain, "*") @@ -69,13 +68,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), CertType: tea.String("upload"), SSLProtocol: tea.String("on"), - SSLPub: tea.String(certPem), - SSLPri: tea.String(privkeyPem), + SSLPub: tea.String(certPEM), + SSLPri: tea.String(privkeyPEM), } setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificate(setDcdnDomainSSLCertificateReq) d.logger.Debug("sdk request 'dcdn.SetDcdnDomainSSLCertificate'", slog.Any("request", setDcdnDomainSSLCertificateReq), slog.Any("response", setDcdnDomainSSLCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'dcdn.SetDcdnDomainSSLCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'dcdn.SetDcdnDomainSSLCertificate': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn_test.go b/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn_test.go index 04ca4c48..deb489c6 100644 --- a/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn_test.go @@ -1,4 +1,4 @@ -package aliyundcdn_test +package aliyundcdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-ddos/aliyun_ddos.go b/internal/pkg/core/deployer/providers/aliyun-ddos/aliyun_ddos.go new file mode 100644 index 00000000..d1cb5b61 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-ddos/aliyun_ddos.go @@ -0,0 +1,137 @@ +package aliyunddos + +import ( + "context" + "errors" + "fmt" + "log/slog" + "strconv" + "strings" + + aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" + aliddos "github.com/alibabacloud-go/ddoscoo-20200101/v4/client" + "github.com/alibabacloud-go/tea/tea" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb" +) + +type DeployerConfig struct { + // 阿里云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 阿里云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 阿里云地域。 + Region string `json:"region"` + // 网站域名(支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *aliddos.Client + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region) + if err != nil { + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + d.sslUploader.WithLogger(logger) + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + if d.config.Domain == "" { + return nil, errors.New("config `domain` is required") + } + + // 上传证书到 CAS + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return nil, fmt.Errorf("failed to upload certificate file: %w", err) + } else { + d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + } + + // 为网站业务转发规则关联 SSL 证书 + // REF: https://help.aliyun.com/zh/anti-ddos/anti-ddos-pro-and-premium/developer-reference/api-ddoscoo-2020-01-01-associatewebcert + certId, _ := strconv.Atoi(upres.CertId) + associateWebCertReq := &aliddos.AssociateWebCertRequest{ + Domain: tea.String(d.config.Domain), + CertId: tea.Int32(int32(certId)), + } + associateWebCertResp, err := d.sdkClient.AssociateWebCert(associateWebCertReq) + d.logger.Debug("sdk request 'dcdn.AssociateWebCert'", slog.Any("request", associateWebCertReq), slog.Any("response", associateWebCertResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'dcdn.AssociateWebCert': %w", err) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliddos.Client, error) { + // 接入点一览 https://api.aliyun.com/product/ddoscoo + config := &aliopen.Config{ + AccessKeyId: tea.String(accessKeyId), + AccessKeySecret: tea.String(accessKeySecret), + Endpoint: tea.String(fmt.Sprintf("ddoscoo.%s.aliyuncs.com", region)), + } + + client, err := aliddos.NewClient(config) + if err != nil { + return nil, err + } + + return client, nil +} + +func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Uploader, error) { + casRegion := region + if casRegion != "" { + // 阿里云 CAS 服务接入点是独立于 Anti-DDoS 服务的 + // 国内版固定接入点:华东一杭州 + // 国际版固定接入点:亚太东南一新加坡 + if !strings.HasPrefix(casRegion, "cn-") { + casRegion = "ap-southeast-1" + } else { + casRegion = "cn-hangzhou" + } + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + Region: casRegion, + }) + return uploader, err +} diff --git a/internal/pkg/core/deployer/providers/aliyun-ddos/aliyun_ddos_test.go b/internal/pkg/core/deployer/providers/aliyun-ddos/aliyun_ddos_test.go new file mode 100644 index 00000000..b7f5ad34 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-ddos/aliyun_ddos_test.go @@ -0,0 +1,80 @@ +package aliyunddos_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ddos" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fRegion string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNDDOS_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fRegion, argsPrefix+"REGION", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./aliyun_ddos_test.go -args \ + --CERTIMATE_DEPLOYER_ALIYUNDDOS_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_ALIYUNDDOS_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_ALIYUNDDOS_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_ALIYUNDDOS_ACCESSKEYSECRET="your-access-key-secret" \ + --CERTIMATE_DEPLOYER_ALIYUNDDOS_REGION="cn-hangzhou" \ + --CERTIMATE_DEPLOYER_ALIYUNDDOS_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("REGION: %v", fRegion), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Region: fRegion, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa.go b/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa.go index 918ec095..1f29756f 100644 --- a/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa.go +++ b/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa.go @@ -1,4 +1,4 @@ -package aliyunesa +package aliyunesa import ( "context" @@ -11,7 +11,6 @@ import ( aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" aliesa "github.com/alibabacloud-go/esa-20240910/v2/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" @@ -45,12 +44,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -71,15 +70,15 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.SiteId == 0 { return nil, errors.New("config `siteId` is required") } // 上传证书到 CAS - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -95,7 +94,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe setCertificateResp, err := d.sdkClient.SetCertificate(setCertificateReq) d.logger.Debug("sdk request 'esa.SetCertificate'", slog.Any("request", setCertificateReq), slog.Any("response", setCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'esa.SetCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'esa.SetCertificate': %w", err) } return &deployer.DeployResult{}, nil @@ -123,7 +122,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up // 阿里云 CAS 服务接入点是独立于 ESA 服务的 // 国内版固定接入点:华东一杭州 // 国际版固定接入点:亚太东南一新加坡 - if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") { + if !strings.HasPrefix(casRegion, "cn-") { casRegion = "ap-southeast-1" } else { casRegion = "cn-hangzhou" diff --git a/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa_test.go b/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa_test.go index 0877d561..0b66d2fb 100644 --- a/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa_test.go @@ -1,4 +1,4 @@ -package aliyunesa_test +package aliyunesa_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go b/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go index 87f51130..8557068c 100644 --- a/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go +++ b/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go @@ -1,4 +1,4 @@ -package aliyunfc +package aliyunfc import ( "context" @@ -10,7 +10,6 @@ import ( alifc3 "github.com/alibabacloud-go/fc-20230330/v4/client" alifc2 "github.com/alibabacloud-go/fc-open-20210406/v2/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" ) @@ -48,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") + return nil, fmt.Errorf("failed to create sdk clients: %w", err) } return &DeployerProvider{ @@ -67,32 +66,32 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { switch d.config.ServiceVersion { case "3", "3.0": - if err := d.deployToFC3(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToFC3(ctx, certPEM, privkeyPEM); err != nil { return nil, err } case "2", "2.0": - if err := d.deployToFC2(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToFC2(ctx, certPEM, privkeyPEM); err != nil { return nil, err } default: - return nil, xerrors.Errorf("unsupported service version: %s", d.config.ServiceVersion) + return nil, fmt.Errorf("unsupported service version '%s'", d.config.ServiceVersion) } return &deployer.DeployResult{}, nil } -func (d *DeployerProvider) deployToFC3(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToFC3(ctx context.Context, certPEM string, privkeyPEM string) error { // 获取自定义域名 // REF: https://help.aliyun.com/zh/functioncompute/fc-3-0/developer-reference/api-fc-2023-03-30-getcustomdomain getCustomDomainResp, err := d.sdkClients.FC3.GetCustomDomain(tea.String(d.config.Domain)) d.logger.Debug("sdk request 'fc.GetCustomDomain'", slog.Any("response", getCustomDomainResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'fc.GetCustomDomain'") + return fmt.Errorf("failed to execute sdk request 'fc.GetCustomDomain': %w", err) } // 更新自定义域名 @@ -101,8 +100,8 @@ func (d *DeployerProvider) deployToFC3(ctx context.Context, certPem string, priv Body: &alifc3.UpdateCustomDomainInput{ CertConfig: &alifc3.CertConfig{ CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), - Certificate: tea.String(certPem), - PrivateKey: tea.String(privkeyPem), + Certificate: tea.String(certPEM), + PrivateKey: tea.String(privkeyPEM), }, Protocol: getCustomDomainResp.Body.Protocol, TlsConfig: getCustomDomainResp.Body.TlsConfig, @@ -111,19 +110,19 @@ func (d *DeployerProvider) deployToFC3(ctx context.Context, certPem string, priv updateCustomDomainResp, err := d.sdkClients.FC3.UpdateCustomDomain(tea.String(d.config.Domain), updateCustomDomainReq) d.logger.Debug("sdk request 'fc.UpdateCustomDomain'", slog.Any("request", updateCustomDomainReq), slog.Any("response", updateCustomDomainResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'fc.UpdateCustomDomain'") + return fmt.Errorf("failed to execute sdk request 'fc.UpdateCustomDomain': %w", err) } return nil } -func (d *DeployerProvider) deployToFC2(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToFC2(ctx context.Context, certPEM string, privkeyPEM string) error { // 获取自定义域名 // REF: https://help.aliyun.com/zh/functioncompute/fc-2-0/developer-reference/api-fc-open-2021-04-06-getcustomdomain getCustomDomainResp, err := d.sdkClients.FC2.GetCustomDomain(tea.String(d.config.Domain)) d.logger.Debug("sdk request 'fc.GetCustomDomain'", slog.Any("response", getCustomDomainResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'fc.GetCustomDomain'") + return fmt.Errorf("failed to execute sdk request 'fc.GetCustomDomain': %w", err) } // 更新自定义域名 @@ -131,8 +130,8 @@ func (d *DeployerProvider) deployToFC2(ctx context.Context, certPem string, priv updateCustomDomainReq := &alifc2.UpdateCustomDomainRequest{ CertConfig: &alifc2.CertConfig{ CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), - Certificate: tea.String(certPem), - PrivateKey: tea.String(privkeyPem), + Certificate: tea.String(certPEM), + PrivateKey: tea.String(privkeyPEM), }, Protocol: getCustomDomainResp.Body.Protocol, TlsConfig: getCustomDomainResp.Body.TlsConfig, @@ -140,7 +139,7 @@ func (d *DeployerProvider) deployToFC2(ctx context.Context, certPem string, priv updateCustomDomainResp, err := d.sdkClients.FC2.UpdateCustomDomain(tea.String(d.config.Domain), updateCustomDomainReq) d.logger.Debug("sdk request 'fc.UpdateCustomDomain'", slog.Any("request", updateCustomDomainReq), slog.Any("response", updateCustomDomainResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'fc.UpdateCustomDomain'") + return fmt.Errorf("failed to execute sdk request 'fc.UpdateCustomDomain': %w", err) } return nil diff --git a/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc_test.go b/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc_test.go index d83f2591..215ec93b 100644 --- a/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc_test.go @@ -1,4 +1,4 @@ -package aliyunfc_test +package aliyunfc_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live.go b/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live.go index e3f6a435..354c9601 100644 --- a/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live.go +++ b/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live.go @@ -1,4 +1,4 @@ -package aliyunlive +package aliyunlive import ( "context" @@ -10,7 +10,6 @@ import ( aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" alilive "github.com/alibabacloud-go/live-20161101/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" ) @@ -41,7 +40,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -60,7 +59,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // "*.example.com" → ".example.com",适配阿里云 Live 要求的泛域名格式 domain := strings.TrimPrefix(d.config.Domain, "*") @@ -71,13 +70,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), CertType: tea.String("upload"), SSLProtocol: tea.String("on"), - SSLPub: tea.String(certPem), - SSLPri: tea.String(privkeyPem), + SSLPub: tea.String(certPEM), + SSLPri: tea.String(privkeyPEM), } setLiveDomainSSLCertificateResp, err := d.sdkClient.SetLiveDomainCertificate(setLiveDomainSSLCertificateReq) d.logger.Debug("sdk request 'live.SetLiveDomainCertificate'", slog.Any("request", setLiveDomainSSLCertificateReq), slog.Any("response", setLiveDomainSSLCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.SetLiveDomainCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'live.SetLiveDomainCertificate': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live_test.go b/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live_test.go index fcf01147..46608e38 100644 --- a/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live_test.go @@ -1,4 +1,4 @@ -package aliyunlive_test +package aliyunlive_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go index f851fee8..b8391144 100644 --- a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go +++ b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go @@ -1,4 +1,4 @@ -package aliyunnlb +package aliyunnlb import ( "context" @@ -10,7 +10,6 @@ import ( aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" alinlb "github.com/alibabacloud-go/nlb-20220430/v2/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" @@ -50,12 +49,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -76,11 +75,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CAS - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -98,7 +97,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil @@ -117,7 +116,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq) d.logger.Debug("sdk request 'nlb.GetLoadBalancerAttribute'", slog.Any("request", getLoadBalancerAttributeReq), slog.Any("response", getLoadBalancerAttributeResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetLoadBalancerAttribute'") + return fmt.Errorf("failed to execute sdk request 'nlb.GetLoadBalancerAttribute': %w", err) } // 查询 TCPSSL 监听列表 @@ -126,6 +125,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId listListenersLimit := int32(100) var listListenersToken *string = nil for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + listListenersReq := &alinlb.ListListenersRequest{ MaxResults: tea.Int32(listListenersLimit), NextToken: listListenersToken, @@ -135,7 +140,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId listListenersResp, err := d.sdkClient.ListListeners(listListenersReq) d.logger.Debug("sdk request 'nlb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'nlb.ListListeners'") + return fmt.Errorf("failed to execute sdk request 'nlb.ListListeners': %w", err) } if listListenersResp.Body.Listeners != nil { @@ -159,8 +164,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId var errs []error for _, listenerId := range listenerIds { - if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + + default: + if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -194,7 +205,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq) d.logger.Debug("sdk request 'nlb.GetListenerAttribute'", slog.Any("request", getListenerAttributeReq), slog.Any("response", getListenerAttributeResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetListenerAttribute'") + return fmt.Errorf("failed to execute sdk request 'nlb.GetListenerAttribute': %w", err) } // 修改监听的属性 @@ -206,7 +217,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq) d.logger.Debug("sdk request 'nlb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerAttributeResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'nlb.UpdateListenerAttribute'") + return fmt.Errorf("failed to execute sdk request 'nlb.UpdateListenerAttribute': %w", err) } return nil @@ -240,7 +251,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up // 阿里云 CAS 服务接入点是独立于 NLB 服务的 // 国内版固定接入点:华东一杭州 // 国际版固定接入点:亚太东南一新加坡 - if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") { + if !strings.HasPrefix(casRegion, "cn-") { casRegion = "ap-southeast-1" } else { casRegion = "cn-hangzhou" diff --git a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb_test.go b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb_test.go index dfaf0916..f4d64219 100644 --- a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb_test.go @@ -1,4 +1,4 @@ -package aliyunnlb_test +package aliyunnlb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-nlb/consts.go b/internal/pkg/core/deployer/providers/aliyun-nlb/consts.go index 1fa071df..40724727 100644 --- a/internal/pkg/core/deployer/providers/aliyun-nlb/consts.go +++ b/internal/pkg/core/deployer/providers/aliyun-nlb/consts.go @@ -1,4 +1,4 @@ -package aliyunnlb +package aliyunnlb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go b/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go index 2f16b09d..474fe5b3 100644 --- a/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go +++ b/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go @@ -1,4 +1,4 @@ -package aliyunoss +package aliyunoss import ( "context" @@ -7,7 +7,6 @@ import ( "log/slog" "github.com/aliyun/aliyun-oss-go-sdk/oss" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" ) @@ -40,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -59,7 +58,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.Bucket == "" { return nil, errors.New("config `bucket` is required") } @@ -72,15 +71,15 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe putBucketCnameWithCertificateReq := oss.PutBucketCname{ Cname: d.config.Domain, CertificateConfiguration: &oss.CertificateConfiguration{ - Certificate: certPem, - PrivateKey: privkeyPem, + Certificate: certPEM, + PrivateKey: privkeyPEM, Force: true, }, } err := d.sdkClient.PutBucketCnameWithCertificate(d.config.Bucket, putBucketCnameWithCertificateReq) d.logger.Debug("sdk request 'oss.PutBucketCnameWithCertificate'", slog.Any("bucket", d.config.Bucket), slog.Any("request", putBucketCnameWithCertificateReq)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'oss.PutBucketCnameWithCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'oss.PutBucketCnameWithCertificate': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss_test.go b/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss_test.go index 7613a003..412f7d16 100644 --- a/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss_test.go @@ -1,4 +1,4 @@ -package aliyunoss_test +package aliyunoss_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod.go b/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod.go index 95d4f631..48e52c26 100644 --- a/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod.go +++ b/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod.go @@ -1,4 +1,4 @@ -package aliyunvod +package aliyunvod import ( "context" @@ -9,7 +9,6 @@ import ( aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" "github.com/alibabacloud-go/tea/tea" alivod "github.com/alibabacloud-go/vod-20170321/v4/client" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" ) @@ -40,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -59,7 +58,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 设置域名证书 // REF: https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-setvoddomainsslcertificate setVodDomainSSLCertificateReq := &alivod.SetVodDomainSSLCertificateRequest{ @@ -67,13 +66,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), CertType: tea.String("upload"), SSLProtocol: tea.String("on"), - SSLPub: tea.String(certPem), - SSLPri: tea.String(privkeyPem), + SSLPub: tea.String(certPEM), + SSLPri: tea.String(privkeyPEM), } setVodDomainSSLCertificateResp, err := d.sdkClient.SetVodDomainSSLCertificate(setVodDomainSSLCertificateReq) d.logger.Debug("sdk request 'live.SetVodDomainSSLCertificate'", slog.Any("request", setVodDomainSSLCertificateReq), slog.Any("response", setVodDomainSSLCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.SetVodDomainSSLCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'live.SetVodDomainSSLCertificate': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod_test.go b/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod_test.go index 552ddc0f..4d523d98 100644 --- a/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod_test.go @@ -1,4 +1,4 @@ -package aliyunvod_test +package aliyunvod_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf.go b/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf.go index cc179b34..26dbd008 100644 --- a/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf.go +++ b/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf.go @@ -1,4 +1,4 @@ -package aliyunwaf +package aliyunwaf import ( "context" @@ -10,12 +10,11 @@ import ( aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" "github.com/alibabacloud-go/tea/tea" aliwaf "github.com/alibabacloud-go/waf-openapi-20211001/v5/client" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" - "github.com/usual2970/certimate/internal/pkg/utils/sliceutil" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" ) type DeployerConfig struct { @@ -49,12 +48,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -75,29 +74,29 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.InstanceId == "" { return nil, errors.New("config `instanceId` is required") } switch d.config.ServiceVersion { case "3", "3.0": - if err := d.deployToWAF3(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToWAF3(ctx, certPEM, privkeyPEM); err != nil { return nil, err } default: - return nil, xerrors.Errorf("unsupported service version: %s", d.config.ServiceVersion) + return nil, fmt.Errorf("unsupported service version '%s'", d.config.ServiceVersion) } return &deployer.DeployResult{}, nil } -func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPEM string, privkeyPEM string) error { // 上传证书到 CAS - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return xerrors.Wrap(err, "failed to upload certificate file") + return fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -114,7 +113,7 @@ func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, pri describeDefaultHttpsResp, err := d.sdkClient.DescribeDefaultHttps(describeDefaultHttpsReq) d.logger.Debug("sdk request 'waf.DescribeDefaultHttps'", slog.Any("request", describeDefaultHttpsReq), slog.Any("response", describeDefaultHttpsResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDefaultHttps'") + return fmt.Errorf("failed to execute sdk request 'waf.DescribeDefaultHttps': %w", err) } // 修改默认 SSL/TLS 设置 @@ -133,7 +132,7 @@ func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, pri modifyDefaultHttpsResp, err := d.sdkClient.ModifyDefaultHttps(modifyDefaultHttpsReq) d.logger.Debug("sdk request 'waf.ModifyDefaultHttps'", slog.Any("request", modifyDefaultHttpsReq), slog.Any("response", modifyDefaultHttpsResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifyDefaultHttps'") + return fmt.Errorf("failed to execute sdk request 'waf.ModifyDefaultHttps': %w", err) } } else { // 指定接入域名 @@ -148,7 +147,7 @@ func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, pri describeDomainDetailResp, err := d.sdkClient.DescribeDomainDetail(describeDomainDetailReq) d.logger.Debug("sdk request 'waf.DescribeDomainDetail'", slog.Any("request", describeDomainDetailReq), slog.Any("response", describeDomainDetailResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDomainDetail'") + return fmt.Errorf("failed to execute sdk request 'waf.DescribeDomainDetail': %w", err) } // 修改 CNAME 接入资源 @@ -157,14 +156,14 @@ func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, pri InstanceId: tea.String(d.config.InstanceId), RegionId: tea.String(d.config.Region), Domain: tea.String(d.config.Domain), - Listen: &aliwaf.ModifyDomainRequestListen{CertId: tea.String(upres.CertId)}, + Listen: &aliwaf.ModifyDomainRequestListen{CertId: tea.String(upres.ExtendedData["certIdentifier"].(string))}, Redirect: &aliwaf.ModifyDomainRequestRedirect{Loadbalance: tea.String("iphash")}, } modifyDomainReq = assign(modifyDomainReq, describeDomainDetailResp.Body) modifyDomainResp, err := d.sdkClient.ModifyDomain(modifyDomainReq) d.logger.Debug("sdk request 'waf.ModifyDomain'", slog.Any("request", modifyDomainReq), slog.Any("response", modifyDomainResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifyDomain'") + return fmt.Errorf("failed to execute sdk request 'waf.ModifyDomain': %w", err) } } @@ -193,7 +192,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up // 阿里云 CAS 服务接入点是独立于 WAF 服务的 // 国内版固定接入点:华东一杭州 // 国际版固定接入点:亚太东南一新加坡 - if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") { + if !strings.HasPrefix(casRegion, "cn-") { casRegion = "ap-southeast-1" } else { casRegion = "cn-hangzhou" diff --git a/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf_test.go b/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf_test.go index 2668db47..e1b92613 100644 --- a/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf_test.go @@ -1,4 +1,4 @@ -package aliyunwaf_test +package aliyunwaf_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/aws-acm/aws_acm.go b/internal/pkg/core/deployer/providers/aws-acm/aws_acm.go index 88482de3..3e817dcf 100644 --- a/internal/pkg/core/deployer/providers/aws-acm/aws_acm.go +++ b/internal/pkg/core/deployer/providers/aws-acm/aws_acm.go @@ -1,11 +1,10 @@ -package awsacm +package awsacm import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-acm" @@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -59,11 +58,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 ACM - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } diff --git a/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go b/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go index 6ebfaad2..0808a4fb 100644 --- a/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go +++ b/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go @@ -1,8 +1,9 @@ -package awscloudfront +package awscloudfront import ( "context" "errors" + "fmt" "log/slog" aws "github.com/aws/aws-sdk-go-v2/aws" @@ -10,7 +11,6 @@ import ( awscred "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/cloudfront" "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" @@ -44,7 +44,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -53,7 +53,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -74,15 +74,15 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.DistributionId == "" { return nil, errors.New("config `distribuitionId` is required") } // 上传证书到 ACM - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -95,7 +95,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe getDistributionConfigResp, err := d.sdkClient.GetDistributionConfig(context.TODO(), getDistributionConfigReq) d.logger.Debug("sdk request 'cloudfront.GetDistributionConfig'", slog.Any("request", getDistributionConfigReq), slog.Any("response", getDistributionConfigResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cloudfront.GetDistributionConfig'") + return nil, fmt.Errorf("failed to execute sdk request 'cloudfront.GetDistributionConfig': %w", err) } // 更新分配配置 @@ -113,7 +113,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe updateDistributionResp, err := d.sdkClient.UpdateDistribution(context.TODO(), updateDistributionReq) d.logger.Debug("sdk request 'cloudfront.UpdateDistribution'", slog.Any("request", updateDistributionReq), slog.Any("response", updateDistributionResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cloudfront.UpdateDistribution'") + return nil, fmt.Errorf("failed to execute sdk request 'cloudfront.UpdateDistribution': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go b/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go index 5b4c75db..78228645 100644 --- a/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go +++ b/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go @@ -1,4 +1,4 @@ -package awscloudfront_test +package awscloudfront_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/azure-keyvault/azure_keyvault.go b/internal/pkg/core/deployer/providers/azure-keyvault/azure_keyvault.go index 4439aa68..b1f21fdc 100644 --- a/internal/pkg/core/deployer/providers/azure-keyvault/azure_keyvault.go +++ b/internal/pkg/core/deployer/providers/azure-keyvault/azure_keyvault.go @@ -1,14 +1,23 @@ -package azurekeyvault +package azurekeyvault import ( "context" + "crypto/x509" + "encoding/base64" + "errors" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/azure-keyvault" + azcommon "github.com/usual2970/certimate/internal/pkg/sdk3rd/azure/common" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type DeployerConfig struct { @@ -22,11 +31,15 @@ type DeployerConfig struct { CloudName string `json:"cloudName,omitempty"` // Key Vault 名称。 KeyVaultName string `json:"keyvaultName"` + // Key Vault 证书名称。 + // 选填。 + CertificateName string `json:"certificateName,omitempty"` } type DeployerProvider struct { config *DeployerConfig logger *slog.Logger + sdkClient *azcertificates.Client sslUploader uploader.Uploader } @@ -37,6 +50,11 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } + client, err := createSdkClient(config.TenantId, config.ClientId, config.ClientSecret, config.CloudName, config.KeyVaultName) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ TenantId: config.TenantId, ClientId: config.ClientId, @@ -45,12 +63,13 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { KeyVaultName: config.KeyVaultName, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ config: config, logger: slog.Default(), + sdkClient: client, sslUploader: uploader, }, nil } @@ -65,14 +84,94 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { - // 上传证书到 KeyVault - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 解析证书内容 + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, err + } + + // 转换证书格式 + certPFX, err := certutil.TransformCertificateFromPEMToPFX(certPEM, privkeyPEM, "") + if err != nil { + return nil, fmt.Errorf("failed to transform certificate from PEM to PFX: %w", err) + } + + if d.config.CertificateName == "" { + // 上传证书到 KeyVault + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return nil, fmt.Errorf("failed to upload certificate file: %w", err) + } else { + d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + } } else { - d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + // 获取证书 + // REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/get-certificate/get-certificate + getCertificateResp, err := d.sdkClient.GetCertificate(context.TODO(), d.config.CertificateName, "", nil) + d.logger.Debug("sdk request 'keyvault.GetCertificate'", slog.String("request.certificateName", d.config.CertificateName), slog.Any("response", getCertificateResp)) + if err != nil { + var respErr *azcore.ResponseError + if !errors.As(err, &respErr) || (respErr.ErrorCode != "ResourceNotFound" && respErr.ErrorCode != "CertificateNotFound") { + return nil, fmt.Errorf("failed to execute sdk request 'keyvault.GetCertificate': %w", err) + } + } else { + oldCertX509, err := x509.ParseCertificate(getCertificateResp.CER) + if err == nil { + if certutil.EqualCertificate(certX509, oldCertX509) { + return &deployer.DeployResult{}, nil + } + } + } + + // 导入证书 + // REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/import-certificate/import-certificate + importCertificateParams := azcertificates.ImportCertificateParameters{ + Base64EncodedCertificate: to.Ptr(base64.StdEncoding.EncodeToString(certPFX)), + CertificatePolicy: &azcertificates.CertificatePolicy{ + SecretProperties: &azcertificates.SecretProperties{ + ContentType: to.Ptr("application/x-pkcs12"), + }, + }, + Tags: map[string]*string{ + "certimate/cert-cn": to.Ptr(certX509.Subject.CommonName), + "certimate/cert-sn": to.Ptr(certX509.SerialNumber.Text(16)), + }, + } + importCertificateResp, err := d.sdkClient.ImportCertificate(context.TODO(), d.config.CertificateName, importCertificateParams, nil) + d.logger.Debug("sdk request 'keyvault.ImportCertificate'", slog.String("request.certificateName", d.config.CertificateName), slog.Any("request.parameters", importCertificateParams), slog.Any("response", importCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'keyvault.ImportCertificate': %w", err) + } } return &deployer.DeployResult{}, nil } + +func createSdkClient(tenantId, clientId, clientSecret, cloudName, keyvaultName string) (*azcertificates.Client, error) { + env, err := azcommon.GetCloudEnvironmentConfiguration(cloudName) + if err != nil { + return nil, err + } + clientOptions := azcore.ClientOptions{Cloud: env} + + credential, err := azidentity.NewClientSecretCredential(tenantId, clientId, clientSecret, + &azidentity.ClientSecretCredentialOptions{ClientOptions: clientOptions}) + if err != nil { + return nil, err + } + + endpoint := fmt.Sprintf("https://%s.vault.azure.net", keyvaultName) + if azcommon.IsEnvironmentGovernment(cloudName) { + endpoint = fmt.Sprintf("https://%s.vault.usgovcloudapi.net", keyvaultName) + } else if azcommon.IsEnvironmentChina(cloudName) { + endpoint = fmt.Sprintf("https://%s.vault.azure.cn", keyvaultName) + } + + client, err := azcertificates.NewClient(endpoint, credential, nil) + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/baiducloud-appblb/baiducloud_appblb.go b/internal/pkg/core/deployer/providers/baiducloud-appblb/baiducloud_appblb.go index 7a5f7ef2..3318135f 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-appblb/baiducloud_appblb.go +++ b/internal/pkg/core/deployer/providers/baiducloud-appblb/baiducloud_appblb.go @@ -1,4 +1,4 @@ -package baiducloudappblb +package baiducloudappblb import ( "context" @@ -10,12 +10,11 @@ import ( bceappblb "github.com/baidubce/bce-sdk-go/services/appblb" "github.com/google/uuid" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/baiducloud-cert" - "github.com/usual2970/certimate/internal/pkg/utils/sliceutil" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" ) type DeployerConfig struct { @@ -54,7 +53,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -62,7 +61,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretAccessKey: config.SecretAccessKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -82,11 +81,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CAS - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -104,7 +103,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil @@ -120,7 +119,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeLoadBalancerDetailResp, err := d.sdkClient.DescribeLoadBalancerDetail(d.config.LoadbalancerId) d.logger.Debug("sdk request 'appblb.DescribeLoadBalancerAttribute'", slog.String("blbId", d.config.LoadbalancerId), slog.Any("response", describeLoadBalancerDetailResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'appblb.DescribeLoadBalancerDetail'") + return fmt.Errorf("failed to execute sdk request 'appblb.DescribeLoadBalancerDetail': %w", err) } // 获取全部 HTTPS/SSL 监听端口 @@ -153,8 +152,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId var errs []error for _, listener := range listeners { - if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + + default: + if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -182,7 +187,7 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str describeAppAllListenersResp, err := d.sdkClient.DescribeAppAllListeners(d.config.LoadbalancerId, describeAppAllListenersRequest) d.logger.Debug("sdk request 'appblb.DescribeAppAllListeners'", slog.String("blbId", d.config.LoadbalancerId), slog.Any("request", describeAppAllListenersRequest), slog.Any("response", describeAppAllListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'appblb.DescribeAppAllListeners'") + return fmt.Errorf("failed to execute sdk request 'appblb.DescribeAppAllListeners': %w", err) } // 获取全部 HTTPS/SSL 监听端口 @@ -210,8 +215,14 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str var errs []error for _, listener := range listeners { - if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + + default: + if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -230,7 +241,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL case "SSL": return d.updateSslListenerCertificate(ctx, cloudLoadbalancerId, cloudListenerPort, cloudCertId) default: - return fmt.Errorf("unsupported listener type: %s", cloudListenerType) + return fmt.Errorf("unsupported listener type '%s'", cloudListenerType) } } @@ -244,7 +255,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c describeAppHTTPSListenersResp, err := d.sdkClient.DescribeAppHTTPSListeners(cloudLoadbalancerId, describeAppHTTPSListenersReq) d.logger.Debug("sdk request 'appblb.DescribeAppHTTPSListeners'", slog.String("blbId", cloudLoadbalancerId), slog.Any("request", describeAppHTTPSListenersReq), slog.Any("response", describeAppHTTPSListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'appblb.DescribeAppHTTPSListeners'") + return fmt.Errorf("failed to execute sdk request 'appblb.DescribeAppHTTPSListeners': %w", err) } else if len(describeAppHTTPSListenersResp.ListenerList) == 0 { return fmt.Errorf("listener %s:%d not found", cloudLoadbalancerId, cloudHttpsListenerPort) } @@ -257,12 +268,13 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c updateAppHTTPSListenerReq := &bceappblb.UpdateAppHTTPSListenerArgs{ ClientToken: generateClientToken(), ListenerPort: uint16(cloudHttpsListenerPort), + Scheduler: describeAppHTTPSListenersResp.ListenerList[0].Scheduler, CertIds: []string{cloudCertId}, } err := d.sdkClient.UpdateAppHTTPSListener(cloudLoadbalancerId, updateAppHTTPSListenerReq) d.logger.Debug("sdk request 'appblb.UpdateAppHTTPSListener'", slog.Any("request", updateAppHTTPSListenerReq)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'appblb.UpdateAppHTTPSListener'") + return fmt.Errorf("failed to execute sdk request 'appblb.UpdateAppHTTPSListener': %w", err) } } else { // 指定 SNI,需部署到扩展域名 @@ -272,6 +284,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c updateAppHTTPSListenerReq := &bceappblb.UpdateAppHTTPSListenerArgs{ ClientToken: generateClientToken(), ListenerPort: uint16(cloudHttpsListenerPort), + Scheduler: describeAppHTTPSListenersResp.ListenerList[0].Scheduler, AdditionalCertDomains: sliceutil.Map(describeAppHTTPSListenersResp.ListenerList[0].AdditionalCertDomains, func(domain bceappblb.AdditionalCertDomainsModel) bceappblb.AdditionalCertDomainsModel { if domain.Host == d.config.Domain { return bceappblb.AdditionalCertDomainsModel{ @@ -289,7 +302,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c err := d.sdkClient.UpdateAppHTTPSListener(cloudLoadbalancerId, updateAppHTTPSListenerReq) d.logger.Debug("sdk request 'appblb.UpdateAppHTTPSListener'", slog.Any("request", updateAppHTTPSListenerReq)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'appblb.UpdateAppHTTPSListener'") + return fmt.Errorf("failed to execute sdk request 'appblb.UpdateAppHTTPSListener': %w", err) } } @@ -307,7 +320,7 @@ func (d *DeployerProvider) updateSslListenerCertificate(ctx context.Context, clo err := d.sdkClient.UpdateAppSSLListener(cloudLoadbalancerId, updateAppSSLListenerReq) d.logger.Debug("sdk request 'appblb.UpdateAppSSLListener'", slog.Any("request", updateAppSSLListenerReq)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'appblb.UpdateAppSSLListener'") + return fmt.Errorf("failed to execute sdk request 'appblb.UpdateAppSSLListener': %w", err) } return nil diff --git a/internal/pkg/core/deployer/providers/baiducloud-appblb/baiducloud_appblb_test.go b/internal/pkg/core/deployer/providers/baiducloud-appblb/baiducloud_appblb_test.go index 6753475c..1e60d5f0 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-appblb/baiducloud_appblb_test.go +++ b/internal/pkg/core/deployer/providers/baiducloud-appblb/baiducloud_appblb_test.go @@ -1,4 +1,4 @@ -package baiducloudappblb_test +package baiducloudappblb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/baiducloud-appblb/consts.go b/internal/pkg/core/deployer/providers/baiducloud-appblb/consts.go index 3955f9a0..8e49730b 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-appblb/consts.go +++ b/internal/pkg/core/deployer/providers/baiducloud-appblb/consts.go @@ -1,4 +1,4 @@ -package baiducloudappblb +package baiducloudappblb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/baiducloud-blb/baiducloud_blb.go b/internal/pkg/core/deployer/providers/baiducloud-blb/baiducloud_blb.go index 6be94e6d..a16ea102 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-blb/baiducloud_blb.go +++ b/internal/pkg/core/deployer/providers/baiducloud-blb/baiducloud_blb.go @@ -1,4 +1,4 @@ -package baiducloudblb +package baiducloudblb import ( "context" @@ -10,12 +10,11 @@ import ( bceblb "github.com/baidubce/bce-sdk-go/services/blb" "github.com/google/uuid" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/baiducloud-cert" - "github.com/usual2970/certimate/internal/pkg/utils/sliceutil" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" ) type DeployerConfig struct { @@ -54,7 +53,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -62,7 +61,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretAccessKey: config.SecretAccessKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -82,11 +81,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CAS - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -104,7 +103,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil @@ -120,7 +119,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeLoadBalancerDetailResp, err := d.sdkClient.DescribeLoadBalancerDetail(d.config.LoadbalancerId) d.logger.Debug("sdk request 'blb.DescribeLoadBalancerAttribute'", slog.String("blbId", d.config.LoadbalancerId), slog.Any("response", describeLoadBalancerDetailResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'blb.DescribeLoadBalancerDetail'") + return fmt.Errorf("failed to execute sdk request 'blb.DescribeLoadBalancerDetail': %w", err) } // 获取全部 HTTPS/SSL 监听端口 @@ -153,8 +152,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId var errs []error for _, listener := range listeners { - if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + + default: + if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -182,7 +187,7 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str describeAllListenersResp, err := d.sdkClient.DescribeAllListeners(d.config.LoadbalancerId, describeAllListenersRequest) d.logger.Debug("sdk request 'blb.DescribeAllListeners'", slog.String("blbId", d.config.LoadbalancerId), slog.Any("request", describeAllListenersRequest), slog.Any("response", describeAllListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'blb.DescribeAllListeners'") + return fmt.Errorf("failed to execute sdk request 'blb.DescribeAllListeners': %w", err) } // 获取全部 HTTPS/SSL 监听端口 @@ -210,8 +215,14 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str var errs []error for _, listener := range listeners { - if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + + default: + if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -230,7 +241,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL case "SSL": return d.updateSslListenerCertificate(ctx, cloudLoadbalancerId, cloudListenerPort, cloudCertId) default: - return fmt.Errorf("unsupported listener type: %s", cloudListenerType) + return fmt.Errorf("unsupported listener type '%s'", cloudListenerType) } } @@ -244,7 +255,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c describeHTTPSListenersResp, err := d.sdkClient.DescribeHTTPSListeners(cloudLoadbalancerId, describeHTTPSListenersReq) d.logger.Debug("sdk request 'blb.DescribeHTTPSListeners'", slog.String("blbId", cloudLoadbalancerId), slog.Any("request", describeHTTPSListenersReq), slog.Any("response", describeHTTPSListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'blb.DescribeHTTPSListeners'") + return fmt.Errorf("failed to execute sdk request 'blb.DescribeHTTPSListeners': %w", err) } else if len(describeHTTPSListenersResp.ListenerList) == 0 { return fmt.Errorf("listener %s:%d not found", cloudLoadbalancerId, cloudHttpsListenerPort) } @@ -262,7 +273,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c err := d.sdkClient.UpdateHTTPSListener(cloudLoadbalancerId, updateHTTPSListenerReq) d.logger.Debug("sdk request 'blb.UpdateHTTPSListener'", slog.Any("request", updateHTTPSListenerReq)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'blb.UpdateHTTPSListener'") + return fmt.Errorf("failed to execute sdk request 'blb.UpdateHTTPSListener': %w", err) } } else { // 指定 SNI,需部署到扩展域名 @@ -289,7 +300,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c err := d.sdkClient.UpdateHTTPSListener(cloudLoadbalancerId, updateHTTPSListenerReq) d.logger.Debug("sdk request 'blb.UpdateHTTPSListener'", slog.Any("request", updateHTTPSListenerReq)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'blb.UpdateHTTPSListener'") + return fmt.Errorf("failed to execute sdk request 'blb.UpdateHTTPSListener': %w", err) } } @@ -307,7 +318,7 @@ func (d *DeployerProvider) updateSslListenerCertificate(ctx context.Context, clo err := d.sdkClient.UpdateSSLListener(cloudLoadbalancerId, updateSSLListenerReq) d.logger.Debug("sdk request 'blb.UpdateSSLListener'", slog.Any("request", updateSSLListenerReq)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'blb.UpdateSSLListener'") + return fmt.Errorf("failed to execute sdk request 'blb.UpdateSSLListener': %w", err) } return nil diff --git a/internal/pkg/core/deployer/providers/baiducloud-blb/baiducloud_blb_test.go b/internal/pkg/core/deployer/providers/baiducloud-blb/baiducloud_blb_test.go index 756e6f8f..c96e4f0c 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-blb/baiducloud_blb_test.go +++ b/internal/pkg/core/deployer/providers/baiducloud-blb/baiducloud_blb_test.go @@ -1,4 +1,4 @@ -package baiducloudblb_test +package baiducloudblb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/baiducloud-blb/consts.go b/internal/pkg/core/deployer/providers/baiducloud-blb/consts.go index 52cb8804..7af2d812 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-blb/consts.go +++ b/internal/pkg/core/deployer/providers/baiducloud-blb/consts.go @@ -1,4 +1,4 @@ -package baiducloudblb +package baiducloudblb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn.go b/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn.go index 87f5aa5c..7ef78fb1 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn.go +++ b/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn.go @@ -1,4 +1,4 @@ -package baiducloudcdn +package baiducloudcdn import ( "context" @@ -8,7 +8,6 @@ import ( bcecdn "github.com/baidubce/bce-sdk-go/services/cdn" bcecdnapi "github.com/baidubce/bce-sdk-go/services/cdn/api" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" ) @@ -37,7 +36,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -56,21 +55,21 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 修改域名证书 // REF: https://cloud.baidu.com/doc/CDN/s/qjzuz2hp8 putCertResp, err := d.sdkClient.PutCert( d.config.Domain, &bcecdnapi.UserCertificate{ CertName: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), - ServerData: certPem, - PrivateData: privkeyPem, + ServerData: certPEM, + PrivateData: privkeyPEM, }, "ON", ) d.logger.Debug("sdk request 'cdn.PutCert'", slog.String("request.domain", d.config.Domain), slog.Any("response", putCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.PutCert'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.PutCert': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn_test.go b/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn_test.go index ecb9a9d4..cef03392 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn_test.go @@ -1,4 +1,4 @@ -package baiducloudcdn_test +package baiducloudcdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/baiducloud-cert/baiducloud_cert.go b/internal/pkg/core/deployer/providers/baiducloud-cert/baiducloud_cert.go index d51018f0..200d34ec 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-cert/baiducloud_cert.go +++ b/internal/pkg/core/deployer/providers/baiducloud-cert/baiducloud_cert.go @@ -1,11 +1,10 @@ -package baiducloudcert +package baiducloudcert import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/baiducloud-cert" @@ -36,7 +35,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretAccessKey: config.SecretAccessKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -55,11 +54,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CAS - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } diff --git a/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go b/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go index e6aec2ab..95c0f9f4 100644 --- a/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go +++ b/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go @@ -1,4 +1,4 @@ -package baishancdn +package baishancdn import ( "context" @@ -10,10 +10,8 @@ import ( "strings" "time" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" - bssdk "github.com/usual2970/certimate/internal/pkg/vendors/baishan-sdk" + bssdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/baishan" ) type DeployerConfig struct { @@ -21,6 +19,9 @@ type DeployerConfig struct { ApiToken string `json:"apiToken"` // 加速域名(支持泛域名)。 Domain string `json:"domain"` + // 证书 ID。 + // 选填。 + CertificateId string `json:"certificateId,omitempty"` } type DeployerProvider struct { @@ -38,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.ApiToken) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -57,68 +58,84 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.Domain == "" { return nil, errors.New("config `domain` is required") } - // 查询域名配置 - // REF: https://portal.baishancloud.com/track/document/api/1/1065 - getDomainConfigReq := &bssdk.GetDomainConfigRequest{ - Domains: d.config.Domain, - Config: []string{"https"}, - } - getDomainConfigResp, err := d.sdkClient.GetDomainConfig(getDomainConfigReq) - d.logger.Debug("sdk request 'baishan.GetDomainConfig'", slog.Any("request", getDomainConfigReq), slog.Any("response", getDomainConfigResp)) - if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.GetDomainConfig'") - } else if len(getDomainConfigResp.Data) == 0 { - return nil, errors.New("domain config not found") - } - - // 新增证书 - // REF: https://portal.baishancloud.com/track/document/downloadPdf/1441 - certificateId := "" - createCertificateReq := &bssdk.CreateCertificateRequest{ - Certificate: certPem, - Key: privkeyPem, - Name: fmt.Sprintf("certimate_%d", time.Now().UnixMilli()), - } - createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) - d.logger.Debug("sdk request 'baishan.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) - if err != nil { - if createCertificateResp != nil { - if createCertificateResp.GetCode() == 400699 && strings.Contains(createCertificateResp.GetMessage(), "this certificate is exists") { - // 证书已存在,忽略新增证书接口错误 - re := regexp.MustCompile(`\d+`) - certificateId = re.FindString(createCertificateResp.GetMessage()) + if d.config.CertificateId == "" { + // 新增证书 + // REF: https://portal.baishancloud.com/track/document/downloadPdf/1441 + certificateId := "" + createCertificateReq := &bssdk.CreateCertificateRequest{ + Certificate: certPEM, + Key: privkeyPEM, + Name: fmt.Sprintf("certimate_%d", time.Now().UnixMilli()), + } + createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) + d.logger.Debug("sdk request 'baishan.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) + if err != nil { + if createCertificateResp != nil { + if createCertificateResp.GetCode() == 400699 && strings.Contains(createCertificateResp.GetMessage(), "this certificate is exists") { + // 证书已存在,忽略新增证书接口错误 + re := regexp.MustCompile(`\d+`) + certificateId = re.FindString(createCertificateResp.GetMessage()) + } } + + if certificateId == "" { + return nil, fmt.Errorf("failed to execute sdk request 'baishan.CreateCertificate': %w", err) + } + } else { + certificateId = createCertificateResp.Data.CertId.String() } - if certificateId == "" { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.CreateCertificate'") + // 查询域名配置 + // REF: https://portal.baishancloud.com/track/document/api/1/1065 + getDomainConfigReq := &bssdk.GetDomainConfigRequest{ + Domains: d.config.Domain, + Config: []string{"https"}, + } + getDomainConfigResp, err := d.sdkClient.GetDomainConfig(getDomainConfigReq) + d.logger.Debug("sdk request 'baishan.GetDomainConfig'", slog.Any("request", getDomainConfigReq), slog.Any("response", getDomainConfigResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'baishan.GetDomainConfig': %w", err) + } else if len(getDomainConfigResp.Data) == 0 { + return nil, errors.New("domain config not found") + } + + // 设置域名配置 + // REF: https://portal.baishancloud.com/track/document/api/1/1045 + setDomainConfigReq := &bssdk.SetDomainConfigRequest{ + Domains: d.config.Domain, + Config: &bssdk.DomainConfig{ + Https: &bssdk.DomainConfigHttps{ + CertId: json.Number(certificateId), + ForceHttps: getDomainConfigResp.Data[0].Config.Https.ForceHttps, + EnableHttp2: getDomainConfigResp.Data[0].Config.Https.EnableHttp2, + EnableOcsp: getDomainConfigResp.Data[0].Config.Https.EnableOcsp, + }, + }, + } + setDomainConfigResp, err := d.sdkClient.SetDomainConfig(setDomainConfigReq) + d.logger.Debug("sdk request 'baishan.SetDomainConfig'", slog.Any("request", setDomainConfigReq), slog.Any("response", setDomainConfigResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'baishan.SetDomainConfig': %w", err) } } else { - certificateId = createCertificateResp.Data.CertId.String() - } - - // 设置域名配置 - // REF: https://portal.baishancloud.com/track/document/api/1/1045 - setDomainConfigReq := &bssdk.SetDomainConfigRequest{ - Domains: d.config.Domain, - Config: &bssdk.DomainConfig{ - Https: &bssdk.DomainConfigHttps{ - CertId: json.Number(certificateId), - ForceHttps: getDomainConfigResp.Data[0].Config.Https.ForceHttps, - EnableHttp2: getDomainConfigResp.Data[0].Config.Https.EnableHttp2, - EnableOcsp: getDomainConfigResp.Data[0].Config.Https.EnableOcsp, - }, - }, - } - setDomainConfigResp, err := d.sdkClient.SetDomainConfig(setDomainConfigReq) - d.logger.Debug("sdk request 'baishan.SetDomainConfig'", slog.Any("request", setDomainConfigReq), slog.Any("response", setDomainConfigResp)) - if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.SetDomainConfig'") + // 替换证书 + // REF: https://portal.baishancloud.com/track/document/downloadPdf/1441 + createCertificateReq := &bssdk.CreateCertificateRequest{ + CertificateId: &d.config.CertificateId, + Certificate: certPEM, + Key: privkeyPEM, + Name: fmt.Sprintf("certimate_%d", time.Now().UnixMilli()), + } + createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) + d.logger.Debug("sdk request 'baishan.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'baishan.CreateCertificate': %w", err) + } } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go b/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go index 5534a232..8e3a16b8 100644 --- a/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go +++ b/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go @@ -1,4 +1,4 @@ -package baishancdn_test +package baishancdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go index 57e37f05..b1a57a81 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go +++ b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go @@ -1,16 +1,15 @@ -package baotapanelconsole +package baotapanelconsole import ( "context" "crypto/tls" "errors" + "fmt" "log/slog" "net/url" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" - btsdk "github.com/usual2970/certimate/internal/pkg/vendors/btpanel-sdk" + btsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/btpanel" ) type DeployerConfig struct { @@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -58,16 +57,16 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 设置面板 SSL 证书 configSavePanelSSLReq := &btsdk.ConfigSavePanelSSLRequest{ - PrivateKey: privkeyPem, - Certificate: certPem, + PrivateKey: privkeyPEM, + Certificate: certPEM, } configSavePanelSSLResp, err := d.sdkClient.ConfigSavePanelSSL(configSavePanelSSLReq) d.logger.Debug("sdk request 'bt.ConfigSavePanelSSL'", slog.Any("request", configSavePanelSSLReq), slog.Any("response", configSavePanelSSLResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.ConfigSavePanelSSL'") + return nil, fmt.Errorf("failed to execute sdk request 'bt.ConfigSavePanelSSL': %w", err) } if d.config.AutoRestart { @@ -83,7 +82,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*btsdk.Client, error) { +func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid baota api url") } @@ -93,7 +92,7 @@ func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*btsdk.Client, } client := btsdk.NewClient(apiUrl, apiKey) - if allowInsecure { + if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go index a10afb37..5f3845e4 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go +++ b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go @@ -1,4 +1,4 @@ -package baotapanelconsole_test +package baotapanelconsole_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go index 082b57b7..f8266791 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go @@ -1,4 +1,4 @@ -package baotapanelsite +package baotapanelsite import ( "context" @@ -8,11 +8,9 @@ import ( "log/slog" "net/url" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" - "github.com/usual2970/certimate/internal/pkg/utils/sliceutil" - btsdk "github.com/usual2970/certimate/internal/pkg/vendors/btpanel-sdk" + btsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/btpanel" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" ) type DeployerConfig struct { @@ -45,7 +43,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -64,7 +62,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { switch d.config.SiteType { case "php": { @@ -76,13 +74,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe siteSetSSLReq := &btsdk.SiteSetSSLRequest{ SiteName: d.config.SiteName, Type: "0", - Certificate: certPem, - PrivateKey: privkeyPem, + Certificate: certPEM, + PrivateKey: privkeyPEM, } siteSetSSLResp, err := d.sdkClient.SiteSetSSL(siteSetSSLReq) d.logger.Debug("sdk request 'bt.SiteSetSSL'", slog.Any("request", siteSetSSLReq), slog.Any("response", siteSetSSLResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SiteSetSSL'") + return nil, fmt.Errorf("failed to execute sdk request 'bt.SiteSetSSL': %w", err) } } @@ -94,13 +92,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe // 上传证书 sslCertSaveCertReq := &btsdk.SSLCertSaveCertRequest{ - Certificate: certPem, - PrivateKey: privkeyPem, + Certificate: certPEM, + PrivateKey: privkeyPEM, } sslCertSaveCertResp, err := d.sdkClient.SSLCertSaveCert(sslCertSaveCertReq) d.logger.Debug("sdk request 'bt.SSLCertSaveCert'", slog.Any("request", sslCertSaveCertReq), slog.Any("response", sslCertSaveCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SSLCertSaveCert'") + return nil, fmt.Errorf("failed to execute sdk request 'bt.SSLCertSaveCert': %w", err) } // 设置站点证书 @@ -115,18 +113,18 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe sslSetBatchCertToSiteResp, err := d.sdkClient.SSLSetBatchCertToSite(sslSetBatchCertToSiteReq) d.logger.Debug("sdk request 'bt.SSLSetBatchCertToSite'", slog.Any("request", sslSetBatchCertToSiteReq), slog.Any("response", sslSetBatchCertToSiteResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SSLSetBatchCertToSite'") + return nil, fmt.Errorf("failed to execute sdk request 'bt.SSLSetBatchCertToSite': %w", err) } } default: - return nil, fmt.Errorf("unsupported site type: %s", d.config.SiteType) + return nil, fmt.Errorf("unsupported site type '%s'", d.config.SiteType) } return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*btsdk.Client, error) { +func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid baota api url") } @@ -136,7 +134,7 @@ func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*btsdk.Client, } client := btsdk.NewClient(apiUrl, apiKey) - if allowInsecure { + if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go index f36605fe..5fece978 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go @@ -1,4 +1,4 @@ -package baotapanelsite_test +package baotapanelsite_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/bunny-cdn/bunny_cdn.go b/internal/pkg/core/deployer/providers/bunny-cdn/bunny_cdn.go new file mode 100644 index 00000000..e2bfd696 --- /dev/null +++ b/internal/pkg/core/deployer/providers/bunny-cdn/bunny_cdn.go @@ -0,0 +1,66 @@ +package bunnycdn + +import ( + "context" + "encoding/base64" + "fmt" + "log/slog" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + bunnysdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/bunny" +) + +type DeployerConfig struct { + // Bunny API Key。 + ApiKey string `json:"apiKey"` + // Bunny Pull Zone ID。 + PullZoneId string `json:"pullZoneId"` + // Bunny CDN Hostname(支持泛域名)。 + Hostname string `json:"hostname"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *bunnysdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: bunnysdk.NewClient(config.ApiKey), + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 上传证书 + createCertificateReq := &bunnysdk.AddCustomCertificateRequest{ + Hostname: d.config.Hostname, + PullZoneId: d.config.PullZoneId, + Certificate: base64.StdEncoding.EncodeToString([]byte(certPEM)), + CertificateKey: base64.StdEncoding.EncodeToString([]byte(privkeyPEM)), + } + createCertificateResp, err := d.sdkClient.AddCustomCertificate(createCertificateReq) + d.logger.Debug("sdk request 'bunny.AddCustomCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'bunny.AddCustomCertificate': %w", err) + } + + return &deployer.DeployResult{}, nil +} diff --git a/internal/pkg/core/deployer/providers/bunny-cdn/bunny_cdn_test.go b/internal/pkg/core/deployer/providers/bunny-cdn/bunny_cdn_test.go new file mode 100644 index 00000000..83fbb0f6 --- /dev/null +++ b/internal/pkg/core/deployer/providers/bunny-cdn/bunny_cdn_test.go @@ -0,0 +1,75 @@ +package bunnycdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/bunny-cdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiKey string + fPullZoneId string + fHostName string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_BUNNYCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") + flag.StringVar(&fPullZoneId, argsPrefix+"PULLZONEID", "", "") + flag.StringVar(&fHostName, argsPrefix+"HOSTNAME", "", "") +} + +/* +Shell command to run this test: + + go test -v ./bunny_cdn_test.go -args \ + --CERTIMATE_DEPLOYER_BUNNYCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_BUNNYCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_BUNNYCDN_APITOKEN="your-api-token" \ + --CERTIMATE_DEPLOYER_BUNNYCDN_PULLZONEID="your-pull-zone-id" \ + --CERTIMATE_DEPLOYER_BUNNYCDN_HOSTNAME="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIKEY: %v", fApiKey), + fmt.Sprintf("PULLZONEID: %v", fPullZoneId), + fmt.Sprintf("HOSTNAME: %v", fHostName), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiKey: fApiKey, + PullZoneId: fPullZoneId, + Hostname: fHostName, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn.go b/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn.go index 7406f074..e659c9a1 100644 --- a/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn.go +++ b/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn.go @@ -1,13 +1,13 @@ -package bytepluscdn +package bytepluscdn import ( "context" "errors" + "fmt" "log/slog" "strings" bpcdn "github.com/byteplus-sdk/byteplus-sdk-golang/service/cdn" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" @@ -46,7 +46,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -67,11 +67,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CDN - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -86,7 +86,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq) d.logger.Debug("sdk request 'cdn.DescribeCertConfig'", slog.Any("request", describeCertConfigReq), slog.Any("response", describeCertConfigResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertConfig'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeCertConfig': %w", err) } if describeCertConfigResp.Result.CertNotConfig != nil { @@ -117,16 +117,22 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe var errs []error for _, domain := range domains { - // 关联证书与加速域名 - // REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-batchdeploycert - batchDeployCertReq := &bpcdn.BatchDeployCertRequest{ - CertId: upres.CertId, - Domain: domain, - } - batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq) - d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp)) - if err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return nil, ctx.Err() + + default: + // 关联证书与加速域名 + // REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-batchdeploycert + batchDeployCertReq := &bpcdn.BatchDeployCertRequest{ + CertId: upres.CertId, + Domain: domain, + } + batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq) + d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp)) + if err != nil { + errs = append(errs, err) + } } } diff --git a/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn_test.go b/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn_test.go index ee09e8ee..34e657cb 100644 --- a/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn_test.go +++ b/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn_test.go @@ -1,4 +1,4 @@ -package bytepluscdn_test +package bytepluscdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/cachefly/cachefly.go b/internal/pkg/core/deployer/providers/cachefly/cachefly.go index 63bb4e95..e3e819d7 100644 --- a/internal/pkg/core/deployer/providers/cachefly/cachefly.go +++ b/internal/pkg/core/deployer/providers/cachefly/cachefly.go @@ -1,14 +1,13 @@ -package cachefly +package cachefly import ( "context" "errors" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" - cfsdk "github.com/usual2970/certimate/internal/pkg/vendors/cachefly-sdk" + cfsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/cachefly" ) type DeployerConfig struct { @@ -31,7 +30,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.ApiToken) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -50,16 +49,16 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书 createCertificateReq := &cfsdk.CreateCertificateRequest{ - Certificate: certPem, - CertificateKey: privkeyPem, + Certificate: certPEM, + CertificateKey: privkeyPEM, } createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) d.logger.Debug("sdk request 'cachefly.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cachefly.CreateCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'cachefly.CreateCertificate': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/cachefly/cachefly_test.go b/internal/pkg/core/deployer/providers/cachefly/cachefly_test.go index 1de6047e..802d8b49 100644 --- a/internal/pkg/core/deployer/providers/cachefly/cachefly_test.go +++ b/internal/pkg/core/deployer/providers/cachefly/cachefly_test.go @@ -1,4 +1,4 @@ -package cachefly_test +package cachefly_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go index ec2c1883..909caf3e 100644 --- a/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go +++ b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go @@ -1,7 +1,8 @@ -package cdnfly +package cdnfly import ( "context" + "crypto/tls" "encoding/json" "errors" "fmt" @@ -9,10 +10,8 @@ import ( "net/url" "time" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" - cfsdk "github.com/usual2970/certimate/internal/pkg/vendors/cdnfly-sdk" + cfsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/cdnfly" ) type DeployerConfig struct { @@ -22,6 +21,8 @@ type DeployerConfig struct { ApiKey string `json:"apiKey"` // Cdnfly 用户端 API Secret。 ApiSecret string `json:"apiSecret"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` // 部署资源类型。 ResourceType ResourceType `json:"resourceType"` // 网站 ID。 @@ -45,9 +46,9 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.ApiSecret) + client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.ApiSecret, config.AllowInsecureConnections) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -66,27 +67,27 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 根据部署资源类型决定部署方式 switch d.config.ResourceType { case RESOURCE_TYPE_SITE: - if err := d.deployToSite(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToSite(ctx, certPEM, privkeyPEM); err != nil { return nil, err } case RESOURCE_TYPE_CERTIFICATE: - if err := d.deployToCertificate(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil { return nil, err } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil } -func (d *DeployerProvider) deployToSite(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToSite(ctx context.Context, certPEM string, privkeyPEM string) error { if d.config.SiteId == "" { return errors.New("config `siteId` is required") } @@ -99,7 +100,7 @@ func (d *DeployerProvider) deployToSite(ctx context.Context, certPem string, pri getSiteResp, err := d.sdkClient.GetSite(getSiteReq) d.logger.Debug("sdk request 'cdnfly.GetSite'", slog.Any("request", getSiteReq), slog.Any("response", getSiteResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.GetSite'") + return fmt.Errorf("failed to execute sdk request 'cdnfly.GetSite': %w", err) } // 添加单个证书 @@ -107,13 +108,13 @@ func (d *DeployerProvider) deployToSite(ctx context.Context, certPem string, pri createCertificateReq := &cfsdk.CreateCertificateRequest{ Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), Type: "custom", - Cert: certPem, - Key: privkeyPem, + Cert: certPEM, + Key: privkeyPEM, } createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) d.logger.Debug("sdk request 'cdnfly.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.CreateCertificate'") + return fmt.Errorf("failed to execute sdk request 'cdnfly.CreateCertificate': %w", err) } // 修改单个网站 @@ -130,13 +131,13 @@ func (d *DeployerProvider) deployToSite(ctx context.Context, certPem string, pri updateSiteResp, err := d.sdkClient.UpdateSite(updateSiteReq) d.logger.Debug("sdk request 'cdnfly.UpdateSite'", slog.Any("request", updateSiteReq), slog.Any("response", updateSiteResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.UpdateSite'") + return fmt.Errorf("failed to execute sdk request 'cdnfly.UpdateSite': %w", err) } return nil } -func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error { if d.config.CertificateId == "" { return errors.New("config `certificateId` is required") } @@ -147,19 +148,19 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem stri updateCertificateReq := &cfsdk.UpdateCertificateRequest{ Id: d.config.CertificateId, Type: &updateCertificateType, - Cert: &certPem, - Key: &privkeyPem, + Cert: &certPEM, + Key: &privkeyPEM, } updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq) d.logger.Debug("sdk request 'cdnfly.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.UpdateCertificate'") + return fmt.Errorf("failed to execute sdk request 'cdnfly.UpdateCertificate': %w", err) } return nil } -func createSdkClient(apiUrl, apiKey, apiSecret string) (*cfsdk.Client, error) { +func createSdkClient(apiUrl, apiKey, apiSecret string, skipTlsVerify bool) (*cfsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid cachefly api url") } @@ -173,5 +174,9 @@ func createSdkClient(apiUrl, apiKey, apiSecret string) (*cfsdk.Client, error) { } client := cfsdk.NewClient(apiUrl, apiKey, apiSecret) + if skipTlsVerify { + client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + return client, nil } diff --git a/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go b/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go index fd17df9c..26486721 100644 --- a/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go +++ b/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go @@ -1,4 +1,4 @@ -package cdnfly_test +package cdnfly_test import ( "context" @@ -57,11 +57,12 @@ func TestDeploy(t *testing.T) { }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, - ApiKey: fApiKey, - ApiSecret: fApiSecret, - ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, - CertificateId: fCertificateId, + ApiUrl: fApiUrl, + ApiKey: fApiKey, + ApiSecret: fApiSecret, + AllowInsecureConnections: true, + ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, + CertificateId: fCertificateId, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/internal/pkg/core/deployer/providers/cdnfly/consts.go b/internal/pkg/core/deployer/providers/cdnfly/consts.go index 3f6c6db0..07d896b8 100644 --- a/internal/pkg/core/deployer/providers/cdnfly/consts.go +++ b/internal/pkg/core/deployer/providers/cdnfly/consts.go @@ -1,4 +1,4 @@ -package cdnfly +package cdnfly type ResourceType string diff --git a/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn.go b/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn.go index 94368998..efcf4b7c 100644 --- a/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn.go +++ b/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn.go @@ -1,16 +1,15 @@ -package dogecloudcdn +package dogecloudcdn import ( "context" + "fmt" "log/slog" "strconv" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/dogecloud" - dogesdk "github.com/usual2970/certimate/internal/pkg/vendors/dogecloud-sdk" + dogesdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/dogecloud" ) type DeployerConfig struct { @@ -43,7 +42,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -64,11 +63,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CDN - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -79,7 +78,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe bindCdnCertResp, err := d.sdkClient.BindCdnCertWithDomain(bindCdnCertId, d.config.Domain) d.logger.Debug("sdk request 'cdn.BindCdnCert'", slog.Int64("request.certId", bindCdnCertId), slog.String("request.domain", d.config.Domain), slog.Any("response", bindCdnCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.BindCdnCert'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.BindCdnCert': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn_test.go b/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn_test.go index f9c1e7c9..b2484b0b 100644 --- a/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn_test.go @@ -1,4 +1,4 @@ -package dogecloudcdn_test +package dogecloudcdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go index 8ce6c73d..3dd202a3 100644 --- a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go +++ b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go @@ -1,15 +1,15 @@ -package edgioapplications +package edgioapplications import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" + edgio "github.com/Edgio/edgio-api/applications/v7" + edgiodtos "github.com/Edgio/edgio-api/applications/v7/dtos" "github.com/usual2970/certimate/internal/pkg/core/deployer" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" - edgsdk "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7" - edgsdkdtos "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7/dtos" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type DeployerConfig struct { @@ -24,7 +24,7 @@ type DeployerConfig struct { type DeployerProvider struct { config *DeployerConfig logger *slog.Logger - sdkClient *edgsdk.EdgioClient + sdkClient *edgio.EdgioClient } var _ deployer.Deployer = (*DeployerProvider)(nil) @@ -36,7 +36,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.ClientId, config.ClientSecret) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -55,31 +55,31 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 提取 Edgio 所需的服务端证书和中间证书内容 - privateCertPem, intermediateCertPem, err := certutil.ExtractCertificatesFromPEM(certPem) + privateCertPEM, intermediateCertPEM, err := certutil.ExtractCertificatesFromPEM(certPEM) if err != nil { return nil, err } // 上传 TLS 证书 // REF: https://docs.edg.io/rest_api/#tag/tls-certs/operation/postConfigV01TlsCerts - uploadTlsCertReq := edgsdkdtos.UploadTlsCertRequest{ + uploadTlsCertReq := edgiodtos.UploadTlsCertRequest{ EnvironmentID: d.config.EnvironmentId, - PrimaryCert: privateCertPem, - IntermediateCert: intermediateCertPem, - PrivateKey: privkeyPem, + PrimaryCert: privateCertPEM, + IntermediateCert: intermediateCertPEM, + PrivateKey: privkeyPEM, } uploadTlsCertResp, err := d.sdkClient.UploadTlsCert(uploadTlsCertReq) d.logger.Debug("sdk request 'edgio.UploadTlsCert'", slog.Any("request", uploadTlsCertReq), slog.Any("response", uploadTlsCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'edgio.UploadTlsCert'") + return nil, fmt.Errorf("failed to execute sdk request 'edgio.UploadTlsCert': %w", err) } return &deployer.DeployResult{}, nil } -func createSdkClient(clientId, clientSecret string) (*edgsdk.EdgioClient, error) { - client := edgsdk.NewEdgioClient(clientId, clientSecret, "", "") +func createSdkClient(clientId, clientSecret string) (*edgio.EdgioClient, error) { + client := edgio.NewEdgioClient(clientId, clientSecret, "", "") return client, nil } diff --git a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go index bd05b16c..23f9c56e 100644 --- a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go +++ b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go @@ -1,4 +1,4 @@ -package edgioapplications_test +package edgioapplications_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn.go b/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn.go index a4d1c33e..2f36c35a 100644 --- a/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn.go +++ b/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn.go @@ -1,19 +1,19 @@ -package gcorecdn +package gcorecdn import ( "context" "errors" + "fmt" "log/slog" "strconv" gprovider "github.com/G-Core/gcorelabscdn-go/gcore/provider" gresources "github.com/G-Core/gcorelabscdn-go/resources" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/gcore-cdn" - gcoresdk "github.com/usual2970/certimate/internal/pkg/vendors/gcore-sdk/common" + gcoresdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/gcore/common" ) type DeployerConfig struct { @@ -39,14 +39,14 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.ApiToken) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ ApiToken: config.ApiToken, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -67,15 +67,15 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.ResourceId == 0 { return nil, errors.New("config `resourceId` is required") } // 上传证书到 CDN - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -85,7 +85,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe getResourceResp, err := d.sdkClient.Get(context.TODO(), d.config.ResourceId) d.logger.Debug("sdk request 'resources.Get'", slog.Any("resourceId", d.config.ResourceId), slog.Any("response", getResourceResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'resources.Get'") + return nil, fmt.Errorf("failed to execute sdk request 'resources.Get': %w", err) } // 更新 CDN 资源详情 @@ -100,14 +100,20 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe SSlEnabled: true, SSLData: int(updateResourceCertId), ProxySSLEnabled: getResourceResp.ProxySSLEnabled, - ProxySSLCA: &getResourceResp.ProxySSLCA, - ProxySSLData: &getResourceResp.ProxySSLData, - Options: getResourceResp.Options, + } + if getResourceResp.ProxySSLCA != 0 { + updateResourceReq.ProxySSLCA = &getResourceResp.ProxySSLCA + } + if getResourceResp.ProxySSLData != 0 { + updateResourceReq.ProxySSLData = &getResourceResp.ProxySSLData + } + if getResourceResp.Options != nil { + updateResourceReq.Options = getResourceResp.Options } updateResourceResp, err := d.sdkClient.Update(context.TODO(), d.config.ResourceId, updateResourceReq) d.logger.Debug("sdk request 'resources.Update'", slog.Int64("resourceId", d.config.ResourceId), slog.Any("request", updateResourceReq), slog.Any("response", updateResourceResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'resources.Update'") + return nil, fmt.Errorf("failed to execute sdk request 'resources.Update': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn_test.go b/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn_test.go index 34d4a9d6..808d724d 100644 --- a/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn_test.go +++ b/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn_test.go @@ -1,4 +1,4 @@ -package gcorecdn_test +package gcorecdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/goedge/consts.go b/internal/pkg/core/deployer/providers/goedge/consts.go new file mode 100644 index 00000000..91eaa9a3 --- /dev/null +++ b/internal/pkg/core/deployer/providers/goedge/consts.go @@ -0,0 +1,8 @@ +package goedge + +type ResourceType string + +const ( + // 资源类型:替换指定证书。 + RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate") +) diff --git a/internal/pkg/core/deployer/providers/goedge/goedge.go b/internal/pkg/core/deployer/providers/goedge/goedge.go new file mode 100644 index 00000000..61153b1b --- /dev/null +++ b/internal/pkg/core/deployer/providers/goedge/goedge.go @@ -0,0 +1,138 @@ +package goedge + +import ( + "context" + "crypto/tls" + "encoding/base64" + "errors" + "fmt" + "log/slog" + "net/url" + "time" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + goedgesdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/goedge" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" +) + +type DeployerConfig struct { + // GoEdge URL。 + ApiUrl string `json:"apiUrl"` + // GoEdge 用户 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // GoEdge 用户 AccessKey。 + AccessKey string `json:"accessKey"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` + // 证书 ID。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。 + CertificateId int64 `json:"certificateId,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *goedgesdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_CERTIFICATE: + if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error { + if d.config.CertificateId == 0 { + return errors.New("config `certificateId` is required") + } + + // 解析证书内容 + certX509, err := certutil.ParseCertificateFromPEM(certPEM) + if err != nil { + return err + } + + // 修改证书 + // REF: https://goedge.cloud/dev/api/service/SSLCertService?role=user#updateSSLCert + updateSSLCertReq := &goedgesdk.UpdateSSLCertRequest{ + SSLCertId: d.config.CertificateId, + IsOn: true, + Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), + Description: "upload from certimate", + ServerName: certX509.Subject.CommonName, + IsCA: false, + CertData: base64.StdEncoding.EncodeToString([]byte(certPEM)), + KeyData: base64.StdEncoding.EncodeToString([]byte(privkeyPEM)), + TimeBeginAt: certX509.NotBefore.Unix(), + TimeEndAt: certX509.NotAfter.Unix(), + DNSNames: certX509.DNSNames, + CommonNames: []string{certX509.Subject.CommonName}, + } + updateSSLCertResp, err := d.sdkClient.UpdateSSLCert(updateSSLCertReq) + d.logger.Debug("sdk request 'goedge.UpdateSSLCert'", slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'goedge.UpdateSSLCert': %w", err) + } + + return nil +} + +func createSdkClient(apiUrl, accessKeyId, accessKey string, skipTlsVerify bool) (*goedgesdk.Client, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid goedge api url") + } + + if accessKeyId == "" { + return nil, errors.New("invalid goedge access key id") + } + + if accessKey == "" { + return nil, errors.New("invalid goedge access key") + } + + client := goedgesdk.NewClient(apiUrl, "user", accessKeyId, accessKey) + if skipTlsVerify { + client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/goedge/goedge_test.go b/internal/pkg/core/deployer/providers/goedge/goedge_test.go new file mode 100644 index 00000000..c8c32b37 --- /dev/null +++ b/internal/pkg/core/deployer/providers/goedge/goedge_test.go @@ -0,0 +1,82 @@ +package goedge_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/goedge" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fAccessKeyId string + fAccessKey string + fCertificateId int +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_GOEDGE_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "") + flag.IntVar(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./goedge_test.go -args \ + --CERTIMATE_DEPLOYER_GOEDGE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_GOEDGE_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_GOEDGE_APIURL="http://127.0.0.1:7788" \ + --CERTIMATE_DEPLOYER_GOEDGE_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_GOEDGE_ACCESSKEY="your-access-key" \ + --CERTIMATE_DEPLOYER_GOEDGE_CERTIFICATEID="your-cerficiate-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEY: %v", fAccessKey), + fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + AccessKeyId: fAccessKeyId, + AccessKey: fAccessKey, + AllowInsecureConnections: true, + ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, + CertificateId: int64(fCertificateId), + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn.go b/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn.go index 048ccbd2..d33dafff 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn.go @@ -1,19 +1,19 @@ -package huaweicloudcdn +package huaweicloudcdn import ( "context" + "fmt" "log/slog" "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global" hccdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2" hccdnmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/model" hccdnregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/region" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-scm" - hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" ) type DeployerConfig struct { @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { config.Region, ) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -55,7 +55,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretAccessKey: config.SecretAccessKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -76,11 +76,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SCM - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -93,7 +93,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe showDomainFullConfigResp, err := d.sdkClient.ShowDomainFullConfig(showDomainFullConfigReq) d.logger.Debug("sdk request 'cdn.ShowDomainFullConfig'", slog.Any("request", showDomainFullConfigReq), slog.Any("response", showDomainFullConfigResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ShowDomainFullConfig'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.ShowDomainFullConfig': %w", err) } // 更新加速域名配置 @@ -102,9 +102,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe updateDomainMultiCertificatesReqBodyContent := &hccdnmodel.UpdateDomainMultiCertificatesRequestBodyContent{} updateDomainMultiCertificatesReqBodyContent.DomainName = d.config.Domain updateDomainMultiCertificatesReqBodyContent.HttpsSwitch = 1 - updateDomainMultiCertificatesReqBodyContent.CertificateType = hwsdk.Int32Ptr(2) - updateDomainMultiCertificatesReqBodyContent.ScmCertificateId = hwsdk.StringPtr(upres.CertId) - updateDomainMultiCertificatesReqBodyContent.CertName = hwsdk.StringPtr(upres.CertName) + updateDomainMultiCertificatesReqBodyContent.CertificateType = typeutil.ToPtr(int32(2)) + updateDomainMultiCertificatesReqBodyContent.ScmCertificateId = typeutil.ToPtr(upres.CertId) + updateDomainMultiCertificatesReqBodyContent.CertName = typeutil.ToPtr(upres.CertName) updateDomainMultiCertificatesReqBodyContent = assign(updateDomainMultiCertificatesReqBodyContent, showDomainFullConfigResp.Configs) updateDomainMultiCertificatesReq := &hccdnmodel.UpdateDomainMultiCertificatesRequest{ Body: &hccdnmodel.UpdateDomainMultiCertificatesRequestBody{ @@ -114,7 +114,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe updateDomainMultiCertificatesResp, err := d.sdkClient.UpdateDomainMultiCertificates(updateDomainMultiCertificatesReq) d.logger.Debug("sdk request 'cdn.UploadDomainMultiCertificates'", slog.Any("request", updateDomainMultiCertificatesReq), slog.Any("response", updateDomainMultiCertificatesResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.UploadDomainMultiCertificates'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.UploadDomainMultiCertificates': %w", err) } return &deployer.DeployResult{}, nil @@ -159,11 +159,11 @@ func assign(source *hccdnmodel.UpdateDomainMultiCertificatesRequestBodyContent, } if *target.OriginProtocol == "follow" { - source.AccessOriginWay = hwsdk.Int32Ptr(1) + source.AccessOriginWay = typeutil.ToPtr(int32(1)) } else if *target.OriginProtocol == "http" { - source.AccessOriginWay = hwsdk.Int32Ptr(2) + source.AccessOriginWay = typeutil.ToPtr(int32(2)) } else if *target.OriginProtocol == "https" { - source.AccessOriginWay = hwsdk.Int32Ptr(3) + source.AccessOriginWay = typeutil.ToPtr(int32(3)) } if target.ForceRedirect != nil { @@ -181,7 +181,7 @@ func assign(source *hccdnmodel.UpdateDomainMultiCertificatesRequestBodyContent, if target.Https != nil { if *target.Https.Http2Status == "on" { - source.Http2 = hwsdk.Int32Ptr(1) + source.Http2 = typeutil.ToPtr(int32(1)) } } diff --git a/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn_test.go b/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn_test.go index aaf8d6c7..cb4ab1a4 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn_test.go @@ -1,4 +1,4 @@ -package huaweicloudcdn_test +package huaweicloudcdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/huaweicloud-elb/consts.go b/internal/pkg/core/deployer/providers/huaweicloud-elb/consts.go index b097a958..ecd3987d 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-elb/consts.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-elb/consts.go @@ -1,4 +1,4 @@ -package huaweicloudelb +package huaweicloudelb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go index 904c138f..748111dd 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go @@ -1,4 +1,4 @@ -package huaweicloudelb +package huaweicloudelb import ( "context" @@ -14,13 +14,12 @@ import ( hciam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3" hciammodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model" hciamregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region" - xerrors "github.com/pkg/errors" "golang.org/x/exp/slices" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-elb" - hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" ) type DeployerConfig struct { @@ -59,7 +58,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -68,7 +67,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -89,32 +88,32 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 根据部署资源类型决定部署方式 switch d.config.ResourceType { case RESOURCE_TYPE_CERTIFICATE: - if err := d.deployToCertificate(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil { return nil, err } case RESOURCE_TYPE_LOADBALANCER: - if err := d.deployToLoadbalancer(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToLoadbalancer(ctx, certPEM, privkeyPEM); err != nil { return nil, err } case RESOURCE_TYPE_LISTENER: - if err := d.deployToListener(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToListener(ctx, certPEM, privkeyPEM); err != nil { return nil, err } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil } -func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error { if d.config.CertificateId == "" { return errors.New("config `certificateId` is required") } @@ -125,21 +124,21 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem stri CertificateId: d.config.CertificateId, Body: &hcelbmodel.UpdateCertificateRequestBody{ Certificate: &hcelbmodel.UpdateCertificateOption{ - Certificate: hwsdk.StringPtr(certPem), - PrivateKey: hwsdk.StringPtr(privkeyPem), + Certificate: typeutil.ToPtr(certPEM), + PrivateKey: typeutil.ToPtr(privkeyPEM), }, }, } updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq) d.logger.Debug("sdk request 'elb.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.UpdateCertificate'") + return fmt.Errorf("failed to execute sdk request 'elb.UpdateCertificate': %w", err) } return nil } -func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPEM string, privkeyPEM string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -152,7 +151,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem str showLoadBalancerResp, err := d.sdkClient.ShowLoadBalancer(showLoadBalancerReq) d.logger.Debug("sdk request 'elb.ShowLoadBalancer'", slog.Any("request", showLoadBalancerReq), slog.Any("response", showLoadBalancerResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowLoadBalancer'") + return fmt.Errorf("failed to execute sdk request 'elb.ShowLoadBalancer': %w", err) } // 查询监听器列表 @@ -161,8 +160,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem str listListenersLimit := int32(2000) var listListenersMarker *string = nil for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + listListenersReq := &hcelbmodel.ListListenersRequest{ - Limit: hwsdk.Int32Ptr(listListenersLimit), + Limit: typeutil.ToPtr(listListenersLimit), Marker: listListenersMarker, Protocol: &[]string{"HTTPS", "TERMINATED_HTTPS"}, LoadbalancerId: &[]string{showLoadBalancerResp.Loadbalancer.Id}, @@ -170,7 +175,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem str listListenersResp, err := d.sdkClient.ListListeners(listListenersReq) d.logger.Debug("sdk request 'elb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ListListeners'") + return fmt.Errorf("failed to execute sdk request 'elb.ListListeners': %w", err) } if listListenersResp.Listeners != nil { @@ -187,9 +192,9 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem str } // 上传证书到 SCM - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return xerrors.Wrap(err, "failed to upload certificate file") + return fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -202,8 +207,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem str var errs []error for _, listenerId := range listenerIds { - if err := d.modifyListenerCertificate(ctx, listenerId, upres.CertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + + default: + if err := d.modifyListenerCertificate(ctx, listenerId, upres.CertId); err != nil { + errs = append(errs, err) + } } } @@ -215,15 +226,15 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem str return nil } -func (d *DeployerProvider) deployToListener(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToListener(ctx context.Context, certPEM string, privkeyPEM string) error { if d.config.ListenerId == "" { return errors.New("config `listenerId` is required") } // 上传证书到 SCM - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return xerrors.Wrap(err, "failed to upload certificate file") + return fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -245,7 +256,7 @@ func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudL showListenerResp, err := d.sdkClient.ShowListener(showListenerReq) d.logger.Debug("sdk request 'elb.ShowListener'", slog.Any("request", showListenerReq), slog.Any("response", showListenerResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowListener'") + return fmt.Errorf("failed to execute sdk request 'elb.ShowListener': %w", err) } // 更新监听器 @@ -254,7 +265,7 @@ func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudL ListenerId: cloudListenerId, Body: &hcelbmodel.UpdateListenerRequestBody{ Listener: &hcelbmodel.UpdateListenerOption{ - DefaultTlsContainerRef: hwsdk.StringPtr(cloudCertId), + DefaultTlsContainerRef: typeutil.ToPtr(cloudCertId), }, }, } @@ -270,7 +281,7 @@ func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudL listOldCertificateResp, err := d.sdkClient.ListCertificates(listOldCertificateReq) d.logger.Debug("sdk request 'elb.ListCertificates'", slog.Any("request", listOldCertificateReq), slog.Any("response", listOldCertificateResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ListCertificates'") + return fmt.Errorf("failed to execute sdk request 'elb.ListCertificates': %w", err) } showNewCertificateReq := &hcelbmodel.ShowCertificateRequest{ @@ -279,7 +290,7 @@ func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudL showNewCertificateResp, err := d.sdkClient.ShowCertificate(showNewCertificateReq) d.logger.Debug("sdk request 'elb.ShowCertificate'", slog.Any("request", showNewCertificateReq), slog.Any("response", showNewCertificateResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowCertificate'") + return fmt.Errorf("failed to execute sdk request 'elb.ShowCertificate': %w", err) } for _, certificate := range *listOldCertificateResp.Certificates { @@ -303,13 +314,13 @@ func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudL } if showListenerResp.Listener.SniMatchAlgo != "" { - updateListenerReq.Body.Listener.SniMatchAlgo = hwsdk.StringPtr(showListenerResp.Listener.SniMatchAlgo) + updateListenerReq.Body.Listener.SniMatchAlgo = typeutil.ToPtr(showListenerResp.Listener.SniMatchAlgo) } } updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq) d.logger.Debug("sdk request 'elb.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'elb.UpdateListener'") + return fmt.Errorf("failed to execute sdk request 'elb.UpdateListener': %w", err) } return nil diff --git a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb_test.go b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb_test.go index 4325ba40..42fee652 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb_test.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb_test.go @@ -1,4 +1,4 @@ -package huaweicloudelb_test +package huaweicloudelb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/huaweicloud-scm/huaweicloud_scm.go b/internal/pkg/core/deployer/providers/huaweicloud-scm/huaweicloud_scm.go index 69b75f2f..c8c208ad 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-scm/huaweicloud_scm.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-scm/huaweicloud_scm.go @@ -1,11 +1,10 @@ -package huaweicloudscm +package huaweicloudscm import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-scm" @@ -36,7 +35,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretAccessKey: config.SecretAccessKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -56,11 +55,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SCM - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } diff --git a/internal/pkg/core/deployer/providers/huaweicloud-waf/consts.go b/internal/pkg/core/deployer/providers/huaweicloud-waf/consts.go index fc574c32..8f1b59c4 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-waf/consts.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-waf/consts.go @@ -1,4 +1,4 @@ -package huaweicloudwaf +package huaweicloudwaf type ResourceType string diff --git a/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf.go b/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf.go index 0e29567c..8fe96ee0 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf.go @@ -1,4 +1,4 @@ -package huaweicloudwaf +package huaweicloudwaf import ( "context" @@ -15,12 +15,11 @@ import ( hcwaf "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1" hcwafmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/model" hcwafregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/region" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-waf" - hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" ) type DeployerConfig struct { @@ -56,7 +55,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -65,7 +64,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -86,11 +85,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 WAF - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -98,28 +97,28 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe // 根据部署资源类型决定部署方式 switch d.config.ResourceType { case RESOURCE_TYPE_CERTIFICATE: - if err := d.deployToCertificate(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil { return nil, err } case RESOURCE_TYPE_CLOUDSERVER: - if err := d.deployToCloudServer(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToCloudServer(ctx, certPEM, privkeyPEM); err != nil { return nil, err } case RESOURCE_TYPE_PREMIUMHOST: - if err := d.deployToPremiumHost(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToPremiumHost(ctx, certPEM, privkeyPEM); err != nil { return nil, err } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil } -func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error { if d.config.CertificateId == "" { return errors.New("config `certificateId` is required") } @@ -132,7 +131,7 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem stri showCertificateResp, err := d.sdkClient.ShowCertificate(showCertificateReq) d.logger.Debug("sdk request 'waf.ShowCertificate'", slog.Any("request", showCertificateReq), slog.Any("response", showCertificateResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.ShowCertificate'") + return fmt.Errorf("failed to execute sdk request 'waf.ShowCertificate': %w", err) } // 更新证书 @@ -141,28 +140,28 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem stri CertificateId: d.config.CertificateId, Body: &hcwafmodel.UpdateCertificateRequestBody{ Name: *showCertificateResp.Name, - Content: hwsdk.StringPtr(certPem), - Key: hwsdk.StringPtr(privkeyPem), + Content: typeutil.ToPtr(certPEM), + Key: typeutil.ToPtr(privkeyPEM), }, } updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq) d.logger.Debug("sdk request 'waf.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.UpdateCertificate'") + return fmt.Errorf("failed to execute sdk request 'waf.UpdateCertificate': %w", err) } return nil } -func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPEM string, privkeyPEM string) error { if d.config.Domain == "" { return errors.New("config `domain` is required") } // 上传证书到 WAF - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return xerrors.Wrap(err, "failed to upload certificate file") + return fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -173,15 +172,21 @@ func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPem stri listHostPage := int32(1) listHostPageSize := int32(100) for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + listHostReq := &hcwafmodel.ListHostRequest{ - Hostname: hwsdk.StringPtr(strings.TrimPrefix(d.config.Domain, "*")), - Page: hwsdk.Int32Ptr(listHostPage), - Pagesize: hwsdk.Int32Ptr(listHostPageSize), + Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")), + Page: typeutil.ToPtr(listHostPage), + Pagesize: typeutil.ToPtr(listHostPageSize), } listHostResp, err := d.sdkClient.ListHost(listHostReq) d.logger.Debug("sdk request 'waf.ListHost'", slog.Any("request", listHostReq), slog.Any("response", listHostResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.ListHost'") + return fmt.Errorf("failed to execute sdk request 'waf.ListHost': %w", err) } if listHostResp.Items != nil { @@ -208,28 +213,28 @@ func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPem stri updateHostReq := &hcwafmodel.UpdateHostRequest{ InstanceId: hostId, Body: &hcwafmodel.UpdateHostRequestBody{ - Certificateid: hwsdk.StringPtr(upres.CertId), - Certificatename: hwsdk.StringPtr(upres.CertName), + Certificateid: typeutil.ToPtr(upres.CertId), + Certificatename: typeutil.ToPtr(upres.CertName), }, } updateHostResp, err := d.sdkClient.UpdateHost(updateHostReq) d.logger.Debug("sdk request 'waf.UpdateHost'", slog.Any("request", updateHostReq), slog.Any("response", updateHostResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.UpdateHost'") + return fmt.Errorf("failed to execute sdk request 'waf.UpdateHost': %w", err) } return nil } -func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPEM string, privkeyPEM string) error { if d.config.Domain == "" { return errors.New("config `domain` is required") } // 上传证书到 WAF - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return xerrors.Wrap(err, "failed to upload certificate file") + return fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -240,15 +245,21 @@ func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPem stri listPremiumHostPage := int32(1) listPremiumHostPageSize := int32(100) for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + listPremiumHostReq := &hcwafmodel.ListPremiumHostRequest{ - Hostname: hwsdk.StringPtr(strings.TrimPrefix(d.config.Domain, "*")), - Page: hwsdk.StringPtr(fmt.Sprintf("%d", listPremiumHostPage)), - Pagesize: hwsdk.StringPtr(fmt.Sprintf("%d", listPremiumHostPageSize)), + Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")), + Page: typeutil.ToPtr(fmt.Sprintf("%d", listPremiumHostPage)), + Pagesize: typeutil.ToPtr(fmt.Sprintf("%d", listPremiumHostPageSize)), } listPremiumHostResp, err := d.sdkClient.ListPremiumHost(listPremiumHostReq) d.logger.Debug("sdk request 'waf.ListPremiumHost'", slog.Any("request", listPremiumHostReq), slog.Any("response", listPremiumHostResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.ListPremiumHost'") + return fmt.Errorf("failed to execute sdk request 'waf.ListPremiumHost': %w", err) } if listPremiumHostResp.Items != nil { @@ -275,14 +286,14 @@ func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPem stri updatePremiumHostReq := &hcwafmodel.UpdatePremiumHostRequest{ HostId: hostId, Body: &hcwafmodel.UpdatePremiumHostRequestBody{ - Certificateid: hwsdk.StringPtr(upres.CertId), - Certificatename: hwsdk.StringPtr(upres.CertName), + Certificateid: typeutil.ToPtr(upres.CertId), + Certificatename: typeutil.ToPtr(upres.CertName), }, } updatePremiumHostResp, err := d.sdkClient.UpdatePremiumHost(updatePremiumHostReq) d.logger.Debug("sdk request 'waf.UpdatePremiumHost'", slog.Any("request", updatePremiumHostReq), slog.Any("response", updatePremiumHostResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'waf.UpdatePremiumHost'") + return fmt.Errorf("failed to execute sdk request 'waf.UpdatePremiumHost': %w", err) } return nil diff --git a/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf_test.go b/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf_test.go index 83142325..4f6b5654 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf_test.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf_test.go @@ -1,4 +1,4 @@ -package huaweicloudwaf_test +package huaweicloudwaf_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go b/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go index 13525c20..de6e4ef0 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go +++ b/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go @@ -1,4 +1,4 @@ -package jdcloudalb +package jdcloudalb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go index d6c3f05a..ca42126e 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go +++ b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go @@ -1,4 +1,4 @@ -package jdcloudalb +package jdcloudalb import ( "context" @@ -12,12 +12,11 @@ import ( jdlbapi "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/apis" jdlbclient "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/client" jdlbmodel "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/models" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/jdcloud-ssl" - "github.com/usual2970/certimate/internal/pkg/utils/sliceutil" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" ) type DeployerConfig struct { @@ -56,7 +55,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -64,7 +63,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { AccessKeySecret: config.AccessKeySecret, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -85,11 +84,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -107,7 +106,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil @@ -124,7 +123,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeLoadBalancerResp, err := d.sdkClient.DescribeLoadBalancer(describeLoadBalancerReq) d.logger.Debug("sdk request 'lb.DescribeLoadBalancer'", slog.Any("request", describeLoadBalancerReq), slog.Any("response", describeLoadBalancerResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeLoadBalancer'") + return fmt.Errorf("failed to execute sdk request 'lb.DescribeLoadBalancer': %w", err) } // 查询监听器列表 @@ -133,6 +132,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeListenersPageNumber := 1 describeListenersPageSize := 100 for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + describeListenersReq := jdlbapi.NewDescribeListenersRequest(d.config.RegionId) describeListenersReq.SetFilters([]jdcommon.Filter{{Name: "loadBalancerId", Values: []string{d.config.LoadbalancerId}}}) describeListenersReq.SetPageSize(describeListenersPageNumber) @@ -140,7 +145,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeListenersResp, err := d.sdkClient.DescribeListeners(describeListenersReq) d.logger.Debug("sdk request 'lb.DescribeListeners'", slog.Any("request", describeListenersReq), slog.Any("response", describeListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListeners'") + return fmt.Errorf("failed to execute sdk request 'lb.DescribeListeners': %w", err) } for _, listener := range describeListenersResp.Result.Listeners { @@ -165,8 +170,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId var errs []error for _, listenerId := range listenerIds { - if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + default: + if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -198,7 +208,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL describeListenerResp, err := d.sdkClient.DescribeListener(describeListenerReq) d.logger.Debug("sdk request 'lb.DescribeListener'", slog.Any("request", describeListenerReq), slog.Any("response", describeListenerResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListener'") + return fmt.Errorf("failed to execute sdk request 'lb.DescribeListener': %w", err) } if d.config.Domain == "" { @@ -211,7 +221,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq) d.logger.Debug("sdk request 'lb.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListener'") + return fmt.Errorf("failed to execute sdk request 'lb.UpdateListener': %w", err) } } else { // 指定 SNI,需部署到扩展证书 @@ -239,7 +249,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL updateListenerCertificatesResp, err := d.sdkClient.UpdateListenerCertificates(updateListenerCertificatesReq) d.logger.Debug("sdk request 'lb.UpdateListenerCertificates'", slog.Any("request", updateListenerCertificatesReq), slog.Any("response", updateListenerCertificatesResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListenerCertificates'") + return fmt.Errorf("failed to execute sdk request 'lb.UpdateListenerCertificates': %w", err) } } diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go index 9c9cc9cc..b6c063e2 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go +++ b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go @@ -1,4 +1,4 @@ -package jdcloudalb_test +package jdcloudalb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn.go b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn.go index 8fe7f6ea..10ccf19d 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn.go +++ b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn.go @@ -1,13 +1,13 @@ -package jdcloudcdn +package jdcloudcdn import ( "context" + "fmt" "log/slog" jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core" jdcdnapi "github.com/jdcloud-api/jdcloud-sdk-go/services/cdn/apis" jdcdnclient "github.com/jdcloud-api/jdcloud-sdk-go/services/cdn/client" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" @@ -39,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { AccessKeySecret: config.AccessKeySecret, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -68,20 +68,20 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 查询域名配置信息 // REF: https://docs.jdcloud.com/cn/cdn/api/querydomainconfig queryDomainConfigReq := jdcdnapi.NewQueryDomainConfigRequest(d.config.Domain) queryDomainConfigResp, err := d.sdkClient.QueryDomainConfig(queryDomainConfigReq) d.logger.Debug("sdk request 'cdn.QueryDomainConfig'", slog.Any("request", queryDomainConfigReq), slog.Any("response", queryDomainConfigResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.QueryDomainConfig'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.QueryDomainConfig': %w", err) } // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -90,15 +90,15 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe // REF: https://docs.jdcloud.com/cn/cdn/api/sethttptype setHttpTypeReq := jdcdnapi.NewSetHttpTypeRequest(d.config.Domain) setHttpTypeReq.SetHttpType("https") - setHttpTypeReq.SetCertificate(certPem) - setHttpTypeReq.SetRsaKey(privkeyPem) + setHttpTypeReq.SetCertificate(certPEM) + setHttpTypeReq.SetRsaKey(privkeyPEM) setHttpTypeReq.SetCertFrom("ssl") setHttpTypeReq.SetSslCertId(upres.CertId) setHttpTypeReq.SetJumpType(queryDomainConfigResp.Result.HttpsJumpType) setHttpTypeResp, err := d.sdkClient.SetHttpType(setHttpTypeReq) d.logger.Debug("sdk request 'cdn.QueryDomainConfig'", slog.Any("request", setHttpTypeReq), slog.Any("response", setHttpTypeResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.SetHttpType'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.SetHttpType': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go index 2d2f7ed0..1bed5d15 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go @@ -1,4 +1,4 @@ -package jdcloudcdn_test +package jdcloudcdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go index c89a67b9..24e5bc7a 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go +++ b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go @@ -1,13 +1,13 @@ -package jdcloudlive +package jdcloudlive import ( "context" + "fmt" "log/slog" jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core" jdliveapi "github.com/jdcloud-api/jdcloud-sdk-go/services/live/apis" jdliveclient "github.com/jdcloud-api/jdcloud-sdk-go/services/live/client" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" ) @@ -36,7 +36,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -55,16 +55,16 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 设置直播证书 // REF: https://docs.jdcloud.com/cn/live-video/api/setlivedomaincertificate setLiveDomainCertificateReq := jdliveapi.NewSetLiveDomainCertificateRequest(d.config.Domain, "on") - setLiveDomainCertificateReq.SetCert(certPem) - setLiveDomainCertificateReq.SetKey(privkeyPem) + setLiveDomainCertificateReq.SetCert(certPEM) + setLiveDomainCertificateReq.SetKey(privkeyPEM) setLiveDomainCertificateResp, err := d.sdkClient.SetLiveDomainCertificate(setLiveDomainCertificateReq) d.logger.Debug("sdk request 'live.SetLiveDomainCertificate'", slog.Any("request", setLiveDomainCertificateReq), slog.Any("response", setLiveDomainCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.SetLiveDomainCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'live.SetLiveDomainCertificate': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go index 076202b5..d544690d 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go +++ b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go @@ -1,4 +1,4 @@ -package jdcloudlive_test +package jdcloudlive_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod.go b/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod.go index 95be5c15..6f61625d 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod.go +++ b/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod.go @@ -1,7 +1,8 @@ -package jdcloudvod +package jdcloudvod import ( "context" + "errors" "fmt" "log/slog" "strconv" @@ -10,7 +11,6 @@ import ( jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core" jdvodapi "github.com/jdcloud-api/jdcloud-sdk-go/services/vod/apis" jdvodclient "github.com/jdcloud-api/jdcloud-sdk-go/services/vod/client" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" ) @@ -39,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -58,20 +58,26 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 查询域名列表 // REF: https://docs.jdcloud.com/cn/video-on-demand/api/listdomains var domainId int listDomainsPageNumber := 1 listDomainsPageSize := 100 for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + listDomainsReq := jdvodapi.NewListDomainsRequest() listDomainsReq.SetPageNumber(1) listDomainsReq.SetPageSize(100) listDomainsResp, err := d.sdkClient.ListDomains(listDomainsReq) d.logger.Debug("sdk request 'vod.ListDomains'", slog.Any("request", listDomainsReq), slog.Any("response", listDomainsResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.ListDomains'") + return nil, fmt.Errorf("failed to execute sdk request 'vod.ListDomains': %w", err) } for _, domain := range listDomainsResp.Result.Content { @@ -88,7 +94,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } } if domainId == 0 { - return nil, xerrors.New("domain not found") + return nil, errors.New("domain not found") } // 查询域名 SSL 配置 @@ -97,22 +103,22 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe getHttpSslResp, err := d.sdkClient.GetHttpSsl(getHttpSslReq) d.logger.Debug("sdk request 'vod.GetHttpSsl'", slog.Any("request", getHttpSslReq), slog.Any("response", getHttpSslResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.GetHttpSsl'") + return nil, fmt.Errorf("failed to execute sdk request 'vod.GetHttpSsl': %w", err) } // 设置域名 SSL 配置 // REF: https://docs.jdcloud.com/cn/video-on-demand/api/sethttpssl setHttpSslReq := jdvodapi.NewSetHttpSslRequest(domainId) setHttpSslReq.SetTitle(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())) - setHttpSslReq.SetSslCert(certPem) - setHttpSslReq.SetSslKey(privkeyPem) + setHttpSslReq.SetSslCert(certPEM) + setHttpSslReq.SetSslKey(privkeyPEM) setHttpSslReq.SetSource("default") setHttpSslReq.SetJumpType(getHttpSslResp.Result.JumpType) setHttpSslReq.SetEnabled(true) setHttpSslResp, err := d.sdkClient.SetHttpSsl(setHttpSslReq) d.logger.Debug("sdk request 'vod.SetHttpSsl'", slog.Any("request", setHttpSslReq), slog.Any("response", setHttpSslResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.SetHttpSsl'") + return nil, fmt.Errorf("failed to execute sdk request 'vod.SetHttpSsl': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod_test.go b/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod_test.go index 6046982b..b8c38b1d 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod_test.go +++ b/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod_test.go @@ -1,4 +1,4 @@ -package jdcloudvod_test +package jdcloudvod_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go b/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go index 88b4b6a1..de2e62be 100644 --- a/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go +++ b/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go @@ -1,12 +1,12 @@ -package k8ssecret +package k8ssecret import ( "context" "errors" + "fmt" "log/slog" "strings" - xerrors "github.com/pkg/errors" k8score "k8s.io/api/core/v1" k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -14,7 +14,7 @@ import ( "k8s.io/client-go/tools/clientcmd" "github.com/usual2970/certimate/internal/pkg/core/deployer" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type DeployerConfig struct { @@ -59,7 +59,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.Namespace == "" { return nil, errors.New("config `namespace` is required") } @@ -76,7 +76,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe return nil, errors.New("config `secretDataKeyForKey` is required") } - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -84,7 +84,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe // 连接 client, err := createK8sClient(d.config.KubeConfig) if err != nil { - return nil, xerrors.Wrap(err, "failed to create k8s client") + return nil, fmt.Errorf("failed to create k8s client: %w", err) } var secretPayload *k8score.Secret @@ -111,13 +111,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe Type: k8score.SecretType(d.config.SecretType), } secretPayload.Data = make(map[string][]byte) - secretPayload.Data[d.config.SecretDataKeyForCrt] = []byte(certPem) - secretPayload.Data[d.config.SecretDataKeyForKey] = []byte(privkeyPem) + secretPayload.Data[d.config.SecretDataKeyForCrt] = []byte(certPEM) + secretPayload.Data[d.config.SecretDataKeyForKey] = []byte(privkeyPEM) secretPayload, err = client.CoreV1().Secrets(d.config.Namespace).Create(context.TODO(), secretPayload, k8smeta.CreateOptions{}) d.logger.Debug("k8s operate 'Secrets.Create'", slog.String("namespace", d.config.Namespace), slog.Any("secret", secretPayload)) if err != nil { - return nil, xerrors.Wrap(err, "failed to create k8s secret") + return nil, fmt.Errorf("failed to create k8s secret: %w", err) } else { return &deployer.DeployResult{}, nil } @@ -135,12 +135,12 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe if secretPayload.Data == nil { secretPayload.Data = make(map[string][]byte) } - secretPayload.Data[d.config.SecretDataKeyForCrt] = []byte(certPem) - secretPayload.Data[d.config.SecretDataKeyForKey] = []byte(privkeyPem) + secretPayload.Data[d.config.SecretDataKeyForCrt] = []byte(certPEM) + secretPayload.Data[d.config.SecretDataKeyForKey] = []byte(privkeyPEM) secretPayload, err = client.CoreV1().Secrets(d.config.Namespace).Update(context.TODO(), secretPayload, k8smeta.UpdateOptions{}) d.logger.Debug("k8s operate 'Secrets.Update'", slog.String("namespace", d.config.Namespace), slog.Any("secret", secretPayload)) if err != nil { - return nil, xerrors.Wrap(err, "failed to update k8s secret") + return nil, fmt.Errorf("failed to update k8s secret: %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go b/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go index 75f15502..90bdf4ab 100644 --- a/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go +++ b/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go @@ -1,4 +1,4 @@ -package k8ssecret_test +package k8ssecret_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/local/defines.go b/internal/pkg/core/deployer/providers/local/defines.go index 5b3118d8..2021f1ea 100644 --- a/internal/pkg/core/deployer/providers/local/defines.go +++ b/internal/pkg/core/deployer/providers/local/defines.go @@ -1,4 +1,4 @@ -package local +package local type OutputFormatType string diff --git a/internal/pkg/core/deployer/providers/local/local.go b/internal/pkg/core/deployer/providers/local/local.go index b4d07711..77f96543 100644 --- a/internal/pkg/core/deployer/providers/local/local.go +++ b/internal/pkg/core/deployer/providers/local/local.go @@ -1,4 +1,4 @@ -package local +package local import ( "bytes" @@ -8,11 +8,9 @@ import ( "os/exec" "runtime" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" - "github.com/usual2970/certimate/internal/pkg/utils/fileutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" + fileutil "github.com/usual2970/certimate/internal/pkg/utils/file" ) type DeployerConfig struct { @@ -70,55 +68,55 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 执行前置命令 if d.config.PreCommand != "" { stdout, stderr, err := execCommand(d.config.ShellEnv, d.config.PreCommand) d.logger.Debug("run pre-command", slog.String("stdout", stdout), slog.String("stderr", stderr)) if err != nil { - return nil, xerrors.Wrapf(err, "failed to execute pre-command, stdout: %s, stderr: %s", stdout, stderr) + return nil, fmt.Errorf("failed to execute pre-command (stdout: %s, stderr: %s): %w ", stdout, stderr, err) } } // 写入证书和私钥文件 switch d.config.OutputFormat { case OUTPUT_FORMAT_PEM: - if err := fileutil.WriteString(d.config.OutputCertPath, certPem); err != nil { - return nil, xerrors.Wrap(err, "failed to save certificate file") + if err := fileutil.WriteString(d.config.OutputCertPath, certPEM); err != nil { + return nil, fmt.Errorf("failed to save certificate file: %w", err) } d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath)) - if err := fileutil.WriteString(d.config.OutputKeyPath, privkeyPem); err != nil { - return nil, xerrors.Wrap(err, "failed to save private key file") + if err := fileutil.WriteString(d.config.OutputKeyPath, privkeyPEM); err != nil { + return nil, fmt.Errorf("failed to save private key file: %w", err) } d.logger.Info("ssl private key file saved", slog.String("path", d.config.OutputKeyPath)) case OUTPUT_FORMAT_PFX: - pfxData, err := certutil.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword) + pfxData, err := certutil.TransformCertificateFromPEMToPFX(certPEM, privkeyPEM, d.config.PfxPassword) if err != nil { - return nil, xerrors.Wrap(err, "failed to transform certificate to PFX") + return nil, fmt.Errorf("failed to transform certificate to PFX: %w", err) } d.logger.Info("ssl certificate transformed to pfx") if err := fileutil.Write(d.config.OutputCertPath, pfxData); err != nil { - return nil, xerrors.Wrap(err, "failed to save certificate file") + return nil, fmt.Errorf("failed to save certificate file: %w", err) } d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath)) case OUTPUT_FORMAT_JKS: - jksData, err := certutil.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass) + jksData, err := certutil.TransformCertificateFromPEMToJKS(certPEM, privkeyPEM, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass) if err != nil { - return nil, xerrors.Wrap(err, "failed to transform certificate to JKS") + return nil, fmt.Errorf("failed to transform certificate to JKS: %w", err) } d.logger.Info("ssl certificate transformed to jks") if err := fileutil.Write(d.config.OutputCertPath, jksData); err != nil { - return nil, xerrors.Wrap(err, "failed to save certificate file") + return nil, fmt.Errorf("failed to save certificate file: %w", err) } d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath)) default: - return nil, fmt.Errorf("unsupported output format: %s", d.config.OutputFormat) + return nil, fmt.Errorf("unsupported output format '%s'", d.config.OutputFormat) } // 执行后置命令 @@ -126,7 +124,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe stdout, stderr, err := execCommand(d.config.ShellEnv, d.config.PostCommand) d.logger.Debug("run post-command", slog.String("stdout", stdout), slog.String("stderr", stderr)) if err != nil { - return nil, xerrors.Wrapf(err, "failed to execute post-command, stdout: %s, stderr: %s", stdout, stderr) + return nil, fmt.Errorf("failed to execute post-command (stdout: %s, stderr: %s): %w ", stdout, stderr, err) } } @@ -154,7 +152,7 @@ func execCommand(shellEnv ShellEnvType, command string) (string, string, error) } default: - return "", "", fmt.Errorf("unsupported shell env: %s", shellEnv) + return "", "", fmt.Errorf("unsupported shell env '%s'", shellEnv) } stdoutBuf := bytes.NewBuffer(nil) @@ -163,7 +161,7 @@ func execCommand(shellEnv ShellEnvType, command string) (string, string, error) cmd.Stderr = stderrBuf err := cmd.Run() if err != nil { - return stdoutBuf.String(), stderrBuf.String(), xerrors.Wrap(err, "failed to execute command") + return stdoutBuf.String(), stderrBuf.String(), fmt.Errorf("failed to execute command: %w", err) } return stdoutBuf.String(), stderrBuf.String(), nil diff --git a/internal/pkg/core/deployer/providers/local/local_test.go b/internal/pkg/core/deployer/providers/local/local_test.go index e86ba7e0..4225864b 100644 --- a/internal/pkg/core/deployer/providers/local/local_test.go +++ b/internal/pkg/core/deployer/providers/local/local_test.go @@ -1,4 +1,4 @@ -package local_test +package local_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go new file mode 100644 index 00000000..d2e92460 --- /dev/null +++ b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go @@ -0,0 +1,121 @@ +package proxmoxve + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "log/slog" + "net/http" + "net/url" + "strings" + + "github.com/luthermonson/go-proxmox" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" +) + +type DeployerConfig struct { + // Proxmox VE 地址。 + ApiUrl string `json:"apiUrl"` + // Proxmox VE API Token。 + ApiToken string `json:"apiToken"` + // Proxmox VE API Token Secret。 + ApiTokenSecret string `json:"apiTokenSecret,omitempty"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + // 集群节点名称。 + NodeName string `json:"nodeName"` + // 是否自动重启。 + AutoRestart bool `json:"autoRestart"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *proxmox.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.ApiToken, config.ApiTokenSecret, config.AllowInsecureConnections) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + if d.config.NodeName == "" { + return nil, errors.New("config `nodeName` is required") + } + + // 获取节点信息 + // REF: https://pve.proxmox.com/pve-docs/api-viewer/index.html#/nodes/{node} + node, err := d.sdkClient.Node(context.TODO(), d.config.NodeName) + if err != nil { + return nil, fmt.Errorf("failed to get node '%s': %w", d.config.NodeName, err) + } + + // 上传自定义证书 + // REF: https://pve.proxmox.com/pve-docs/api-viewer/index.html#/nodes/{node}/certificates/custom + err = node.UploadCustomCertificate(context.TODO(), &proxmox.CustomCertificate{ + Certificates: certPEM, + Key: privkeyPEM, + Force: true, + Restart: d.config.AutoRestart, + }) + if err != nil { + return nil, fmt.Errorf("failed to upload custom certificate to node '%s': %w", node.Name, err) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(apiUrl, apiToken, apiTokenSecret string, skipTlsVerify bool) (*proxmox.Client, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid pve api url") + } + + if apiToken == "" { + return nil, errors.New("invalid pve api token") + } + + httpClient := &http.Client{ + Transport: http.DefaultTransport, + Timeout: http.DefaultClient.Timeout, + } + if skipTlsVerify { + httpClient.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + } + client := proxmox.NewClient( + strings.TrimRight(apiUrl, "/")+"/api2/json", + proxmox.WithHTTPClient(httpClient), + proxmox.WithAPIToken(apiToken, apiTokenSecret), + ) + + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go new file mode 100644 index 00000000..6251bd75 --- /dev/null +++ b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go @@ -0,0 +1,82 @@ +package proxmoxve_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/proxmoxve" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fApiToken string + fApiTokenSecret string + fNodeName string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_PROXMOXVE_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") + flag.StringVar(&fApiTokenSecret, argsPrefix+"APITOKENSECRET", "", "") + flag.StringVar(&fNodeName, argsPrefix+"NODENAME", "", "") +} + +/* +Shell command to run this test: + + go test -v ./proxmoxve_test.go -args \ + --CERTIMATE_DEPLOYER_PROXMOXVE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_PROXMOXVE_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_PROXMOXVE_APIURL="http://127.0.0.1:8006" \ + --CERTIMATE_DEPLOYER_PROXMOXVE_APITOKEN="your-api-token" \ + --CERTIMATE_DEPLOYER_PROXMOXVE_APITOKENSECRET="your-api-token-secret" \ + --CERTIMATE_DEPLOYER_PROXMOXVE_NODENAME="your-cluster-node-name" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("APITOKEN: %v", fApiToken), + fmt.Sprintf("APITOKENSECRET: %v", fApiTokenSecret), + fmt.Sprintf("NODENAME: %v", fNodeName), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + ApiToken: fApiToken, + ApiTokenSecret: fApiTokenSecret, + AllowInsecureConnections: true, + NodeName: fNodeName, + AutoRestart: true, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go index e8166afd..573eeb94 100644 --- a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go +++ b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go @@ -1,17 +1,17 @@ -package qiniucdn +package qiniucdn import ( "context" + "fmt" "log/slog" "strings" - xerrors "github.com/pkg/errors" "github.com/qiniu/go-sdk/v7/auth" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/qiniu-sslcert" - qiniusdk "github.com/usual2970/certimate/internal/pkg/vendors/qiniu-sdk" + qiniusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/qiniu" ) type DeployerConfig struct { @@ -44,7 +44,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -65,11 +65,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CDN - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -82,7 +82,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe getDomainInfoResp, err := d.sdkClient.GetDomainInfo(context.TODO(), domain) d.logger.Debug("sdk request 'cdn.GetDomainInfo'", slog.String("request.domain", domain), slog.Any("response", getDomainInfoResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.GetDomainInfo'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetDomainInfo': %w", err) } // 判断域名是否已启用 HTTPS。如果已启用,修改域名证书;否则,启用 HTTPS @@ -91,13 +91,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe enableDomainHttpsResp, err := d.sdkClient.EnableDomainHttps(context.TODO(), domain, upres.CertId, true, true) d.logger.Debug("sdk request 'cdn.EnableDomainHttps'", slog.String("request.domain", domain), slog.String("request.certId", upres.CertId), slog.Any("response", enableDomainHttpsResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.EnableDomainHttps'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.EnableDomainHttps': %w", err) } } else if getDomainInfoResp.Https.CertID != upres.CertId { modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(context.TODO(), domain, upres.CertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable) d.logger.Debug("sdk request 'cdn.ModifyDomainHttpsConf'", slog.String("request.domain", domain), slog.String("request.certId", upres.CertId), slog.Any("response", modifyDomainHttpsConfResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ModifyDomainHttpsConf'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.ModifyDomainHttpsConf': %w", err) } } diff --git a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go index fffbfca5..51c1201c 100644 --- a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go +++ b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go @@ -1,4 +1,4 @@ -package qiniucdn_test +package qiniucdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go b/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go index f684253b..db8d899e 100644 --- a/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go +++ b/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go @@ -1,10 +1,10 @@ -package qiniupili +package qiniupili import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/qiniu/go-sdk/v7/pili" "github.com/usual2970/certimate/internal/pkg/core/deployer" @@ -44,7 +44,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -65,11 +65,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CDN - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -84,7 +84,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe err = d.sdkClient.SetDomainCert(context.TODO(), setDomainCertReq) d.logger.Debug("sdk request 'pili.SetDomainCert'", slog.Any("request", setDomainCertReq)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'pili.SetDomainCert'") + return nil, fmt.Errorf("failed to execute sdk request 'pili.SetDomainCert': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go b/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go index 73e69855..1c165e0e 100644 --- a/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go +++ b/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go @@ -1,4 +1,4 @@ -package qiniupili_test +package qiniupili_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/rainyun-rcdn/rainyun_rcdn.go b/internal/pkg/core/deployer/providers/rainyun-rcdn/rainyun_rcdn.go new file mode 100644 index 00000000..0b003bee --- /dev/null +++ b/internal/pkg/core/deployer/providers/rainyun-rcdn/rainyun_rcdn.go @@ -0,0 +1,101 @@ +package rainyunrcdn + +import ( + "context" + "errors" + "fmt" + "log/slog" + "strconv" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/rainyun-sslcenter" + rainyunsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/rainyun" +) + +type DeployerConfig struct { + // 雨云 API 密钥。 + ApiKey string `json:"apiKey"` + // RCDN 实例 ID。 + InstanceId int32 `json:"instanceId"` + // 加速域名(支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *rainyunsdk.Client + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiKey) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + ApiKey: config.ApiKey, + }) + if err != nil { + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + d.sslUploader.WithLogger(logger) + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 上传证书到 SSL 证书 + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return nil, fmt.Errorf("failed to upload certificate file: %w", err) + } else { + d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + } + + // RCDN SSL 绑定域名 + // REF: https://apifox.com/apidoc/shared/a4595cc8-44c5-4678-a2a3-eed7738dab03/api-184214120 + certId, _ := strconv.Atoi(upres.CertId) + rcdnInstanceSslBindReq := &rainyunsdk.RcdnInstanceSslBindRequest{ + CertId: int32(certId), + Domains: []string{d.config.Domain}, + } + rcdnInstanceSslBindResp, err := d.sdkClient.RcdnInstanceSslBind(d.config.InstanceId, rcdnInstanceSslBindReq) + d.logger.Debug("sdk request 'rcdn.InstanceSslBind'", slog.Any("instanceId", d.config.InstanceId), slog.Any("request", rcdnInstanceSslBindReq), slog.Any("response", rcdnInstanceSslBindResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'rcdn.InstanceSslBind': %w", err) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(apiKey string) (*rainyunsdk.Client, error) { + if apiKey == "" { + return nil, errors.New("invalid rainyun api key") + } + + client := rainyunsdk.NewClient(apiKey) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/rainyun-rcdn/rainyun_rcdn_test.go b/internal/pkg/core/deployer/providers/rainyun-rcdn/rainyun_rcdn_test.go new file mode 100644 index 00000000..fd81bffa --- /dev/null +++ b/internal/pkg/core/deployer/providers/rainyun-rcdn/rainyun_rcdn_test.go @@ -0,0 +1,75 @@ +package rainyunrcdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/rainyun-rcdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiKey string + fInstanceId int64 + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_RAINYUNRCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") + flag.Int64Var(&fInstanceId, argsPrefix+"INSTANCEID", 0, "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./ucloud_ucdn_test.go -args \ + --CERTIMATE_DEPLOYER_RAINYUNRCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_RAINYUNRCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_RAINYUNRCDN_APIKEY="your-api-key" \ + --CERTIMATE_DEPLOYER_RAINYUNRCDN_INSTANCEID="your-rcdn-instance-id" \ + --CERTIMATE_DEPLOYER_RAINYUNRCDN_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIKEY: %v", fApiKey), + fmt.Sprintf("INSTANCEID: %v", fInstanceId), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + PrivateKey: fApiKey, + InstanceId: fInstanceId, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/safeline/consts.go b/internal/pkg/core/deployer/providers/safeline/consts.go index a19e3866..1777a32e 100644 --- a/internal/pkg/core/deployer/providers/safeline/consts.go +++ b/internal/pkg/core/deployer/providers/safeline/consts.go @@ -1,4 +1,4 @@ -package safeline +package safeline type ResourceType string diff --git a/internal/pkg/core/deployer/providers/safeline/safeline.go b/internal/pkg/core/deployer/providers/safeline/safeline.go index 8079027f..f737fda9 100644 --- a/internal/pkg/core/deployer/providers/safeline/safeline.go +++ b/internal/pkg/core/deployer/providers/safeline/safeline.go @@ -1,4 +1,4 @@ -package safeline +package safeline import ( "context" @@ -8,10 +8,8 @@ import ( "log/slog" "net/url" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" - safelinesdk "github.com/usual2970/certimate/internal/pkg/vendors/safeline-sdk" + safelinesdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/safeline" ) type DeployerConfig struct { @@ -43,7 +41,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.ApiUrl, config.ApiToken, config.AllowInsecureConnections) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &DeployerProvider{ @@ -62,22 +60,22 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 根据部署资源类型决定部署方式 switch d.config.ResourceType { case RESOURCE_TYPE_CERTIFICATE: - if err := d.deployToCertificate(ctx, certPem, privkeyPem); err != nil { + if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil { return nil, err } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil } -func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error { if d.config.CertificateId == 0 { return errors.New("config `certificateId` is required") } @@ -87,20 +85,20 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem stri Id: d.config.CertificateId, Type: 2, Manual: &safelinesdk.UpdateCertificateRequestBodyManul{ - Crt: certPem, - Key: privkeyPem, + Crt: certPEM, + Key: privkeyPEM, }, } updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq) d.logger.Debug("sdk request 'safeline.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'safeline.UpdateCertificate'") + return fmt.Errorf("failed to execute sdk request 'safeline.UpdateCertificate': %w", err) } return nil } -func createSdkClient(apiUrl, apiToken string, allowInsecure bool) (*safelinesdk.Client, error) { +func createSdkClient(apiUrl, apiToken string, skipTlsVerify bool) (*safelinesdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid safeline api url") } @@ -110,7 +108,7 @@ func createSdkClient(apiUrl, apiToken string, allowInsecure bool) (*safelinesdk. } client := safelinesdk.NewClient(apiUrl, apiToken) - if allowInsecure { + if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/safeline/safeline_test.go b/internal/pkg/core/deployer/providers/safeline/safeline_test.go index 42c6313f..294086c8 100644 --- a/internal/pkg/core/deployer/providers/safeline/safeline_test.go +++ b/internal/pkg/core/deployer/providers/safeline/safeline_test.go @@ -1,4 +1,4 @@ -package safeline_test +package safeline_test import ( "context" @@ -56,7 +56,7 @@ func TestDeploy(t *testing.T) { ApiUrl: fApiUrl, ApiToken: fApiToken, AllowInsecureConnections: true, - ResourceType: provider.ResourceType("certificate"), + ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, CertificateId: int32(fCertificateId), }) if err != nil { diff --git a/internal/pkg/core/deployer/providers/ssh/defines.go b/internal/pkg/core/deployer/providers/ssh/defines.go index 6f30871b..de1b255f 100644 --- a/internal/pkg/core/deployer/providers/ssh/defines.go +++ b/internal/pkg/core/deployer/providers/ssh/defines.go @@ -1,4 +1,4 @@ -package ssh +package ssh type OutputFormatType string diff --git a/internal/pkg/core/deployer/providers/ssh/ssh.go b/internal/pkg/core/deployer/providers/ssh/ssh.go index 8fba9490..4b8b433d 100644 --- a/internal/pkg/core/deployer/providers/ssh/ssh.go +++ b/internal/pkg/core/deployer/providers/ssh/ssh.go @@ -1,4 +1,4 @@ -package ssh +package ssh import ( "bytes" @@ -8,13 +8,12 @@ import ( "os" "path/filepath" - xerrors "github.com/pkg/errors" "github.com/pkg/sftp" "github.com/povsister/scp" "golang.org/x/crypto/ssh" "github.com/usual2970/certimate/internal/pkg/core/deployer" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type DeployerConfig struct { @@ -85,7 +84,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 连接 client, err := createSshClient( d.config.SshHost, @@ -96,7 +95,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe d.config.SshKeyPassphrase, ) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssh client") + return nil, fmt.Errorf("failed to create ssh client: %w", err) } defer client.Close() @@ -107,49 +106,49 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe stdout, stderr, err := execSshCommand(client, d.config.PreCommand) d.logger.Debug("run pre-command", slog.String("stdout", stdout), slog.String("stderr", stderr)) if err != nil { - return nil, xerrors.Wrapf(err, "failed to execute pre-command: stdout: %s, stderr: %s", stdout, stderr) + return nil, fmt.Errorf("failed to execute pre-command (stdout: %s, stderr: %s): %w ", stdout, stderr, err) } } // 上传证书和私钥文件 switch d.config.OutputFormat { case OUTPUT_FORMAT_PEM: - if err := writeFileString(client, d.config.UseSCP, d.config.OutputCertPath, certPem); err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + if err := writeFileString(client, d.config.UseSCP, d.config.OutputCertPath, certPEM); err != nil { + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } d.logger.Info("ssl certificate file uploaded", slog.String("path", d.config.OutputCertPath)) - if err := writeFileString(client, d.config.UseSCP, d.config.OutputKeyPath, privkeyPem); err != nil { - return nil, xerrors.Wrap(err, "failed to upload private key file") + if err := writeFileString(client, d.config.UseSCP, d.config.OutputKeyPath, privkeyPEM); err != nil { + return nil, fmt.Errorf("failed to upload private key file: %w", err) } d.logger.Info("ssl private key file uploaded", slog.String("path", d.config.OutputKeyPath)) case OUTPUT_FORMAT_PFX: - pfxData, err := certutil.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword) + pfxData, err := certutil.TransformCertificateFromPEMToPFX(certPEM, privkeyPEM, d.config.PfxPassword) if err != nil { - return nil, xerrors.Wrap(err, "failed to transform certificate to PFX") + return nil, fmt.Errorf("failed to transform certificate to PFX: %w", err) } d.logger.Info("ssl certificate transformed to pfx") if err := writeFile(client, d.config.UseSCP, d.config.OutputCertPath, pfxData); err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } d.logger.Info("ssl certificate file uploaded", slog.String("path", d.config.OutputCertPath)) case OUTPUT_FORMAT_JKS: - jksData, err := certutil.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass) + jksData, err := certutil.TransformCertificateFromPEMToJKS(certPEM, privkeyPEM, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass) if err != nil { - return nil, xerrors.Wrap(err, "failed to transform certificate to JKS") + return nil, fmt.Errorf("failed to transform certificate to JKS: %w", err) } d.logger.Info("ssl certificate transformed to jks") if err := writeFile(client, d.config.UseSCP, d.config.OutputCertPath, jksData); err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } d.logger.Info("ssl certificate file uploaded", slog.String("path", d.config.OutputCertPath)) default: - return nil, fmt.Errorf("unsupported output format: %s", d.config.OutputFormat) + return nil, fmt.Errorf("unsupported output format '%s'", d.config.OutputFormat) } // 执行后置命令 @@ -157,7 +156,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe stdout, stderr, err := execSshCommand(client, d.config.PostCommand) d.logger.Debug("run post-command", slog.String("stdout", stdout), slog.String("stderr", stderr)) if err != nil { - return nil, xerrors.Wrapf(err, "failed to execute post-command, stdout: %s, stderr: %s", stdout, stderr) + return nil, fmt.Errorf("failed to execute post-command (stdout: %s, stderr: %s): %w ", stdout, stderr, err) } } @@ -212,7 +211,7 @@ func execSshCommand(sshCli *ssh.Client, command string) (string, string, error) session.Stderr = stderrBuf err = session.Run(command) if err != nil { - return stdoutBuf.String(), stderrBuf.String(), xerrors.Wrap(err, "failed to execute ssh command") + return stdoutBuf.String(), stderrBuf.String(), fmt.Errorf("failed to execute ssh command: %w", err) } return stdoutBuf.String(), stderrBuf.String(), nil @@ -241,14 +240,13 @@ func writeFileStringWithSCP(sshCli *ssh.Client, path string, content string) err func writeFileWithSCP(sshCli *ssh.Client, path string, data []byte) error { scpCli, err := scp.NewClientFromExistingSSH(sshCli, &scp.ClientOption{}) if err != nil { - return xerrors.Wrap(err, "failed to create scp client") + return fmt.Errorf("failed to create scp client: %w", err) } - defer scpCli.Close() reader := bytes.NewReader(data) err = scpCli.CopyToRemote(reader, path, &scp.FileTransferOption{}) if err != nil { - return xerrors.Wrap(err, "failed to write to remote file") + return fmt.Errorf("failed to write to remote file: %w", err) } return nil @@ -261,23 +259,23 @@ func writeFileStringWithSFTP(sshCli *ssh.Client, path string, content string) er func writeFileWithSFTP(sshCli *ssh.Client, path string, data []byte) error { sftpCli, err := sftp.NewClient(sshCli) if err != nil { - return xerrors.Wrap(err, "failed to create sftp client") + return fmt.Errorf("failed to create sftp client: %w", err) } defer sftpCli.Close() if err := sftpCli.MkdirAll(filepath.Dir(path)); err != nil { - return xerrors.Wrap(err, "failed to create remote directory") + return fmt.Errorf("failed to create remote directory: %w", err) } file, err := sftpCli.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC) if err != nil { - return xerrors.Wrap(err, "failed to open remote file") + return fmt.Errorf("failed to open remote file: %w", err) } defer file.Close() _, err = file.Write(data) if err != nil { - return xerrors.Wrap(err, "failed to write to remote file") + return fmt.Errorf("failed to write to remote file: %w", err) } return nil diff --git a/internal/pkg/core/deployer/providers/ssh/ssh_test.go b/internal/pkg/core/deployer/providers/ssh/ssh_test.go index 8312d680..b63471d1 100644 --- a/internal/pkg/core/deployer/providers/ssh/ssh_test.go +++ b/internal/pkg/core/deployer/providers/ssh/ssh_test.go @@ -1,4 +1,4 @@ -package ssh_test +package ssh_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go b/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go index 38e58c7b..01f71d9e 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go @@ -1,11 +1,11 @@ -package tencentcloudcdn +package tencentcloudcdn import ( "context" + "fmt" "log/slog" "strings" - xerrors "github.com/pkg/errors" tccdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { clients, err := createSdkClients(config.SecretId, config.SecretKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") + return nil, fmt.Errorf("failed to create sdk clients: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -55,7 +55,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -76,11 +76,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -130,7 +130,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe deployCertificateInstanceResp, err := d.sdkClients.SSL.DeployCertificateInstance(deployCertificateInstanceReq) d.logger.Debug("sdk request 'ssl.DeployCertificateInstance'", slog.Any("request", deployCertificateInstanceReq), slog.Any("response", deployCertificateInstanceResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'") + return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err) } } @@ -146,7 +146,7 @@ func (d *DeployerProvider) getDomainsByCertificateId(cloudCertId string) ([]stri describeCertDomainsResp, err := d.sdkClients.CDN.DescribeCertDomains(describeCertDomainsReq) d.logger.Debug("sdk request 'cdn.DescribeCertDomains'", slog.Any("request", describeCertDomainsReq), slog.Any("response", describeCertDomainsResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertDomains'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeCertDomains': %w", err) } domains := make([]string, 0) @@ -168,7 +168,7 @@ func (d *DeployerProvider) getDeployedDomainsByCertificateId(cloudCertId string) describeDeployedResourcesResp, err := d.sdkClients.SSL.DescribeDeployedResources(describeDeployedResourcesReq) d.logger.Debug("sdk request 'cdn.DescribeDeployedResources'", slog.Any("request", describeDeployedResourcesReq), slog.Any("response", describeDeployedResourcesResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeDeployedResources'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeDeployedResources': %w", err) } domains := make([]string, 0) diff --git a/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn_test.go b/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn_test.go index 0361e17b..95285930 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn_test.go @@ -1,4 +1,4 @@ -package tencentcloudcdn_test +package tencentcloudcdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/tencentcloud-clb/consts.go b/internal/pkg/core/deployer/providers/tencentcloud-clb/consts.go index 7d72d5d3..2728e730 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-clb/consts.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-clb/consts.go @@ -1,4 +1,4 @@ -package tencentcloudclb +package tencentcloudclb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go index e8edb9cc..0c2f8902 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go @@ -1,4 +1,4 @@ -package tencentcloudclb +package tencentcloudclb import ( "context" @@ -6,7 +6,6 @@ import ( "fmt" "log/slog" - xerrors "github.com/pkg/errors" tcclb "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb/v20180317" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" @@ -58,7 +57,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { clients, err := createSdkClients(config.SecretId, config.SecretKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") + return nil, fmt.Errorf("failed to create sdk clients: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -66,7 +65,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -87,11 +86,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -119,7 +118,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil @@ -149,7 +148,7 @@ func (d *DeployerProvider) deployViaSslService(ctx context.Context, cloudCertId deployCertificateInstanceResp, err := d.sdkClients.SSL.DeployCertificateInstance(deployCertificateInstanceReq) d.logger.Debug("sdk request 'ssl.DeployCertificateInstance'", slog.Any("request", deployCertificateInstanceReq), slog.Any("response", deployCertificateInstanceResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'") + return fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err) } return nil @@ -168,7 +167,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeListenersResp, err := d.sdkClients.CLB.DescribeListeners(describeListenersReq) d.logger.Debug("sdk request 'clb.DescribeListeners'", slog.Any("request", describeListenersReq), slog.Any("response", describeListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeListeners'") + return fmt.Errorf("failed to execute sdk request 'clb.DescribeListeners': %w", err) } else { if describeListenersResp.Response.Listeners != nil { for _, listener := range describeListenersResp.Response.Listeners { @@ -189,8 +188,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId var errs []error for _, listenerId := range listenerIds { - if err := d.modifyListenerCertificate(ctx, d.config.LoadbalancerId, listenerId, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + default: + if err := d.modifyListenerCertificate(ctx, d.config.LoadbalancerId, listenerId, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -242,7 +246,7 @@ func (d *DeployerProvider) deployToRuleDomain(ctx context.Context, cloudCertId s modifyDomainAttributesResp, err := d.sdkClients.CLB.ModifyDomainAttributes(modifyDomainAttributesReq) d.logger.Debug("sdk request 'clb.ModifyDomainAttributes'", slog.Any("request", modifyDomainAttributesReq), slog.Any("response", modifyDomainAttributesResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.ModifyDomainAttributes'") + return fmt.Errorf("failed to execute sdk request 'clb.ModifyDomainAttributes': %w", err) } return nil @@ -257,7 +261,7 @@ func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudL describeListenersResp, err := d.sdkClients.CLB.DescribeListeners(describeListenersReq) d.logger.Debug("sdk request 'clb.DescribeListeners'", slog.Any("request", describeListenersReq), slog.Any("response", describeListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeListeners'") + return fmt.Errorf("failed to execute sdk request 'clb.DescribeListeners': %w", err) } else if len(describeListenersResp.Response.Listeners) == 0 { return errors.New("listener not found") } @@ -277,7 +281,7 @@ func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudL modifyListenerResp, err := d.sdkClients.CLB.ModifyListener(modifyListenerReq) d.logger.Debug("sdk request 'clb.ModifyListener'", slog.Any("request", modifyListenerReq), slog.Any("response", modifyListenerResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.ModifyListener'") + return fmt.Errorf("failed to execute sdk request 'clb.ModifyListener': %w", err) } return nil diff --git a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb_test.go b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb_test.go index 0aeb1e7c..59cd9b8d 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb_test.go @@ -1,4 +1,4 @@ -package tencentcloudclb_test +package tencentcloudclb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go b/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go index 7f1992e9..0949c5a3 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go @@ -1,4 +1,4 @@ -package tencentcloudcos +package tencentcloudcos import ( "context" @@ -6,7 +6,6 @@ import ( "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" tcssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" @@ -45,7 +44,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.SecretId, config.SecretKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -53,7 +52,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -74,7 +73,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.Bucket == "" { return nil, errors.New("config `bucket` is required") } @@ -83,9 +82,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -100,7 +99,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe deployCertificateInstanceResp, err := d.sdkClient.DeployCertificateInstance(deployCertificateInstanceReq) d.logger.Debug("sdk request 'ssl.DeployCertificateInstance'", slog.Any("request", deployCertificateInstanceReq), slog.Any("response", deployCertificateInstanceResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'") + return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos_test.go b/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos_test.go index ab29a893..24e41d47 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos_test.go @@ -1,4 +1,4 @@ -package tencentcloudcos_test +package tencentcloudcos_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css.go b/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css.go index e29e25ea..7de626d9 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css.go @@ -1,10 +1,10 @@ -package tencentcloudcss +package tencentcloudcss import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" tclive "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live/v20180801" @@ -39,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.SecretId, config.SecretKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -68,11 +68,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -90,7 +90,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe modifyLiveDomainCertBindingsResp, err := d.sdkClient.ModifyLiveDomainCertBindings(modifyLiveDomainCertBindingsReq) d.logger.Debug("sdk request 'live.ModifyLiveDomainCertBindings'", slog.Any("request", modifyLiveDomainCertBindingsReq), slog.Any("response", modifyLiveDomainCertBindingsResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.ModifyLiveDomainCertBindings'") + return nil, fmt.Errorf("failed to execute sdk request 'live.ModifyLiveDomainCertBindings': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css_test.go b/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css_test.go index 92127b3b..4aaa344a 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css_test.go @@ -1,4 +1,4 @@ -package tencentcloudcss_test +package tencentcloudcss_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go b/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go index bd2aebe2..ebe87025 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go @@ -1,11 +1,11 @@ -package tencentcloudecdn +package tencentcloudecdn import ( "context" + "fmt" "log/slog" "strings" - xerrors "github.com/pkg/errors" tccdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" @@ -46,7 +46,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { clients, err := createSdkClients(config.SecretId, config.SecretKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") + return nil, fmt.Errorf("failed to create sdk clients: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -54,7 +54,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -75,11 +75,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -107,13 +107,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe // REF: https://cloud.tencent.com/document/product/400/91667 deployCertificateInstanceReq := tcssl.NewDeployCertificateInstanceRequest() deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId) - deployCertificateInstanceReq.ResourceType = common.StringPtr("ecdn") + deployCertificateInstanceReq.ResourceType = common.StringPtr("cdn") deployCertificateInstanceReq.Status = common.Int64Ptr(1) deployCertificateInstanceReq.InstanceIdList = common.StringPtrs(instanceIds) deployCertificateInstanceResp, err := d.sdkClients.SSL.DeployCertificateInstance(deployCertificateInstanceReq) d.logger.Debug("sdk request 'ssl.DeployCertificateInstance'", slog.Any("request", deployCertificateInstanceReq), slog.Any("response", deployCertificateInstanceResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'") + return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err) } } @@ -129,7 +129,7 @@ func (d *DeployerProvider) getDomainsByCertificateId(cloudCertId string) ([]stri describeCertDomainsResp, err := d.sdkClients.CDN.DescribeCertDomains(describeCertDomainsReq) d.logger.Debug("sdk request 'cdn.DescribeCertDomains'", slog.Any("request", describeCertDomainsReq), slog.Any("response", describeCertDomainsResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertDomains'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeCertDomains': %w", err) } domains := make([]string, 0) diff --git a/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn_test.go b/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn_test.go index da03a7ab..1cffd10d 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn_test.go @@ -1,4 +1,4 @@ -package tencentcloudecdn_test +package tencentcloudecdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo.go b/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo.go index b6ebbf94..919339bb 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo.go @@ -1,11 +1,11 @@ -package tencentcloudeo +package tencentcloudeo import ( "context" "errors" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" tcssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" @@ -48,7 +48,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { clients, err := createSdkClients(config.SecretId, config.SecretKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk clients") + return nil, fmt.Errorf("failed to create sdk clients: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -56,7 +56,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -77,15 +77,15 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.ZoneId == "" { return nil, errors.New("config `zoneId` is required") } // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -100,7 +100,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe modifyHostsCertificateResp, err := d.sdkClients.TEO.ModifyHostsCertificate(modifyHostsCertificateReq) d.logger.Debug("sdk request 'teo.ModifyHostsCertificate'", slog.Any("request", modifyHostsCertificateReq), slog.Any("response", modifyHostsCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'teo.ModifyHostsCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'teo.ModifyHostsCertificate': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo_test.go b/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo_test.go index 8e5b70fe..81c3b9da 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo_test.go @@ -1,4 +1,4 @@ -package tencentcloudeo_test +package tencentcloudeo_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/tencentcloud-scf/tencentcloud_scf.go b/internal/pkg/core/deployer/providers/tencentcloud-scf/tencentcloud_scf.go index 9d8512c2..bc8d8696 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-scf/tencentcloud_scf.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-scf/tencentcloud_scf.go @@ -1,10 +1,10 @@ -package tencentcloudscf +package tencentcloudscf import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" tcscf "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf/v20180416" @@ -41,7 +41,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.SecretId, config.SecretKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -49,7 +49,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -70,7 +70,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 查看云函数自定义域名详情 // REF: https://cloud.tencent.com/document/product/583/111924 getCustomDomainReq := tcscf.NewGetCustomDomainRequest() @@ -78,13 +78,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe getCustomDomainResp, err := d.sdkClient.GetCustomDomain(getCustomDomainReq) d.logger.Debug("sdk request 'scf.GetCustomDomain'", slog.Any("request", getCustomDomainReq), slog.Any("response", getCustomDomainResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'scf.GetCustomDomain'") + return nil, fmt.Errorf("failed to execute sdk request 'scf.GetCustomDomain': %w", err) } // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -100,7 +100,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe updateCustomDomainResp, err := d.sdkClient.UpdateCustomDomain(updateCustomDomainReq) d.logger.Debug("sdk request 'scf.UpdateCustomDomain'", slog.Any("request", updateCustomDomainReq), slog.Any("response", updateCustomDomainResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'scf.UpdateCustomDomain'") + return nil, fmt.Errorf("failed to execute sdk request 'scf.UpdateCustomDomain': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/tencentcloud-scf/tencentcloud_scf_test.go b/internal/pkg/core/deployer/providers/tencentcloud-scf/tencentcloud_scf_test.go index 84a1ad79..f0aa0cfe 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-scf/tencentcloud_scf_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-scf/tencentcloud_scf_test.go @@ -1,4 +1,4 @@ -package tencentcloudscf_test +package tencentcloudscf_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy/tencentcloud_ssl_deploy.go b/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy/tencentcloud_ssl_deploy.go index 8e06920a..5f13660d 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy/tencentcloud_ssl_deploy.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy/tencentcloud_ssl_deploy.go @@ -1,4 +1,4 @@ -package tencentcloudssldeploy +package tencentcloudssldeploy import ( "context" @@ -7,7 +7,6 @@ import ( "log/slog" "time" - xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" tcssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" @@ -46,7 +45,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.SecretId, config.SecretKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -54,7 +53,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -75,7 +74,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.ResourceType == "" { return nil, errors.New("config `resourceType` is required") } @@ -84,9 +83,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -101,7 +100,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe deployCertificateInstanceResp, err := d.sdkClient.DeployCertificateInstance(deployCertificateInstanceReq) d.logger.Debug("sdk request 'ssl.DeployCertificateInstance'", slog.Any("request", deployCertificateInstanceReq), slog.Any("response", deployCertificateInstanceResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.DeployCertificateInstance'") + return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err) } else if deployCertificateInstanceResp.Response == nil || deployCertificateInstanceResp.Response.DeployRecordId == nil { return nil, errors.New("failed to create deploy record") } @@ -109,8 +108,10 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe // 循环获取部署任务详情,等待任务状态变更 // REF: https://cloud.tencent.com.cn/document/api/400/91658 for { - if ctx.Err() != nil { + select { + case <-ctx.Done(): return nil, ctx.Err() + default: } describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest() @@ -119,7 +120,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe describeHostDeployRecordDetailResp, err := d.sdkClient.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq) d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail'") + return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err) } if describeHostDeployRecordDetailResp.Response.TotalCount == nil { diff --git a/internal/pkg/core/deployer/providers/tencentcloud-ssl/tencentcloud_ssl.go b/internal/pkg/core/deployer/providers/tencentcloud-ssl/tencentcloud_ssl.go index 8f8676de..5fbdb7c6 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-ssl/tencentcloud_ssl.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-ssl/tencentcloud_ssl.go @@ -1,11 +1,10 @@ -package tencentcloudssl +package tencentcloudssl import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" @@ -36,7 +35,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -56,11 +55,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } diff --git a/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod.go b/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod.go index 6a32eb62..1b8553c5 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod.go @@ -1,10 +1,10 @@ -package tencentcloudvod +package tencentcloudvod import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" tcvod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod/v20180717" @@ -41,7 +41,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.SecretId, config.SecretKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -49,7 +49,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -70,11 +70,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -91,7 +91,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe setVodDomainCertificateResp, err := d.sdkClient.SetVodDomainCertificate(setVodDomainCertificateReq) d.logger.Debug("sdk request 'vod.SetVodDomainCertificate'", slog.Any("request", setVodDomainCertificateReq), slog.Any("response", setVodDomainCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.SetVodDomainCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'vod.SetVodDomainCertificate': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod_test.go b/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod_test.go index ffd085cd..52382808 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod_test.go @@ -1,4 +1,4 @@ -package tencentcloudvod_test +package tencentcloudvod_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf.go b/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf.go index 2921fb84..18380289 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf.go @@ -1,11 +1,11 @@ -package tencentcloudwaf +package tencentcloudwaf import ( "context" "errors" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" tcwaf "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf/v20180125" @@ -46,7 +46,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.SecretId, config.SecretKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -54,7 +54,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { SecretKey: config.SecretKey, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -75,7 +75,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.Domain == "" { return nil, errors.New("config `domain` is required") } @@ -87,9 +87,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -103,7 +103,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe describeDomainDetailsSaasResp, err := d.sdkClient.DescribeDomainDetailsSaas(describeDomainDetailsSaasReq) d.logger.Debug("sdk request 'waf.DescribeDomainDetailsSaas'", slog.Any("request", describeDomainDetailsSaasReq), slog.Any("response", describeDomainDetailsSaasResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDomainDetailsSaas'") + return nil, fmt.Errorf("failed to execute sdk request 'waf.DescribeDomainDetailsSaas': %w", err) } // 编辑 SaaS 型 WAF 域名 @@ -117,7 +117,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe modifySpartaProtectionResp, err := d.sdkClient.ModifySpartaProtection(modifySpartaProtectionReq) d.logger.Debug("sdk request 'waf.ModifySpartaProtection'", slog.Any("request", modifySpartaProtectionReq), slog.Any("response", modifySpartaProtectionResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifySpartaProtection'") + return nil, fmt.Errorf("failed to execute sdk request 'waf.ModifySpartaProtection': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf_test.go b/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf_test.go index 42ba1ffe..8cb63b7d 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf_test.go @@ -1,4 +1,4 @@ -package tencentcloudwaf_test +package tencentcloudwaf_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go index 82be501c..5f90b943 100644 --- a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go +++ b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go @@ -1,12 +1,12 @@ -package uclouducdn +package uclouducdn import ( "context" "errors" + "fmt" "log/slog" "strconv" - xerrors "github.com/pkg/errors" "github.com/ucloud/ucloud-sdk-go/services/ucdn" "github.com/ucloud/ucloud-sdk-go/ucloud" "github.com/ucloud/ucloud-sdk-go/ucloud/auth" @@ -43,7 +43,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.PrivateKey, config.PublicKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -52,7 +52,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { ProjectId: config.ProjectId, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -73,11 +73,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 USSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -92,7 +92,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe getUcdnDomainConfigResp, err := d.sdkClient.GetUcdnDomainConfig(getUcdnDomainConfigReq) d.logger.Debug("sdk request 'ucdn.GetUcdnDomainConfig'", slog.Any("request", getUcdnDomainConfigReq), slog.Any("response", getUcdnDomainConfigResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ucdn.GetUcdnDomainConfig'") + return nil, fmt.Errorf("failed to execute sdk request 'ucdn.GetUcdnDomainConfig': %w", err) } else if len(getUcdnDomainConfigResp.DomainList) == 0 { return nil, errors.New("no domain found") } @@ -114,7 +114,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe updateUcdnDomainHttpsConfigV2Resp, err := d.sdkClient.UpdateUcdnDomainHttpsConfigV2(updateUcdnDomainHttpsConfigV2Req) d.logger.Debug("sdk request 'ucdn.UpdateUcdnDomainHttpsConfigV2'", slog.Any("request", updateUcdnDomainHttpsConfigV2Req), slog.Any("response", updateUcdnDomainHttpsConfigV2Resp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ucdn.UpdateUcdnDomainHttpsConfigV2'") + return nil, fmt.Errorf("failed to execute sdk request 'ucdn.UpdateUcdnDomainHttpsConfigV2': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go index fc952da1..f49a026a 100644 --- a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go +++ b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go @@ -1,4 +1,4 @@ -package uclouducdn_test +package uclouducdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3.go b/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3.go index d3a68868..5564e6a8 100644 --- a/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3.go +++ b/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3.go @@ -1,17 +1,17 @@ -package ucloudus3 +package ucloudus3 import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/ucloud/ucloud-sdk-go/ucloud" "github.com/ucloud/ucloud-sdk-go/ucloud/auth" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ucloud-ussl" - usdkFile "github.com/usual2970/certimate/internal/pkg/vendors/ucloud-sdk/ufile" + usdkFile "github.com/usual2970/certimate/internal/pkg/sdk3rd/ucloud/ufile" ) type DeployerConfig struct { @@ -45,7 +45,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.PrivateKey, config.PublicKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -54,7 +54,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { ProjectId: config.ProjectId, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -75,11 +75,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 USSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -97,7 +97,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe addUFileSSLCertResp, err := d.sdkClient.AddUFileSSLCert(addUFileSSLCertReq) d.logger.Debug("sdk request 'us3.AddUFileSSLCert'", slog.Any("request", addUFileSSLCertReq), slog.Any("response", addUFileSSLCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'us3.AddUFileSSLCert'") + return nil, fmt.Errorf("failed to execute sdk request 'us3.AddUFileSSLCert': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3_test.go b/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3_test.go index 52f78664..9977ee42 100644 --- a/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3_test.go +++ b/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3_test.go @@ -1,4 +1,4 @@ -package ucloudus3_test +package ucloudus3_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn.go b/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn.go index 84d6cafb..4c9987a3 100644 --- a/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn.go +++ b/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn.go @@ -1,17 +1,17 @@ -package upyuncdn +package upyuncdn import ( "context" "errors" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" "golang.org/x/exp/slices" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/upyun-ssl" - upyunsdk "github.com/usual2970/certimate/internal/pkg/vendors/upyun-sdk/console" + upyunsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/upyun/console" ) type DeployerConfig struct { @@ -39,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.Username, config.Password) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Password: config.Password, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -68,11 +68,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 SSL - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -81,7 +81,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe getHttpsServiceManagerResp, err := d.sdkClient.GetHttpsServiceManager(d.config.Domain) d.logger.Debug("sdk request 'console.GetHttpsServiceManager'", slog.String("request.domain", d.config.Domain), slog.Any("response", getHttpsServiceManagerResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'console.GetHttpsServiceManager'") + return nil, fmt.Errorf("failed to execute sdk request 'console.GetHttpsServiceManager': %w", err) } // 判断域名是否已启用 HTTPS。如果已启用,迁移域名证书;否则,设置新证书 @@ -98,7 +98,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe updateHttpsCertificateManagerResp, err := d.sdkClient.UpdateHttpsCertificateManager(updateHttpsCertificateManagerReq) d.logger.Debug("sdk request 'console.EnableDomainHttps'", slog.Any("request", updateHttpsCertificateManagerReq), slog.Any("response", updateHttpsCertificateManagerResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'console.UpdateHttpsCertificateManager'") + return nil, fmt.Errorf("failed to execute sdk request 'console.UpdateHttpsCertificateManager': %w", err) } } else if getHttpsServiceManagerResp.Data.Domains[lastCertIndex].CertificateId != upres.CertId { migrateHttpsDomainReq := &upyunsdk.MigrateHttpsDomainRequest{ @@ -108,7 +108,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe migrateHttpsDomainResp, err := d.sdkClient.MigrateHttpsDomain(migrateHttpsDomainReq) d.logger.Debug("sdk request 'console.MigrateHttpsDomain'", slog.Any("request", migrateHttpsDomainReq), slog.Any("response", migrateHttpsDomainResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'console.MigrateHttpsDomain'") + return nil, fmt.Errorf("failed to execute sdk request 'console.MigrateHttpsDomain': %w", err) } } diff --git a/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn_test.go b/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn_test.go index 7e9e16ff..9fd32fd4 100644 --- a/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn_test.go +++ b/internal/pkg/core/deployer/providers/upyun-cdn/upyun_cdn_test.go @@ -1,4 +1,4 @@ -package upyuncdn_test +package upyuncdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/volcengine-alb/consts.go b/internal/pkg/core/deployer/providers/volcengine-alb/consts.go index aba1182c..11944379 100644 --- a/internal/pkg/core/deployer/providers/volcengine-alb/consts.go +++ b/internal/pkg/core/deployer/providers/volcengine-alb/consts.go @@ -1,4 +1,4 @@ -package volcenginealb +package volcenginealb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/volcengine-alb/volcengine_alb.go b/internal/pkg/core/deployer/providers/volcengine-alb/volcengine_alb.go index 0c6ba1b4..b17ae729 100644 --- a/internal/pkg/core/deployer/providers/volcengine-alb/volcengine_alb.go +++ b/internal/pkg/core/deployer/providers/volcengine-alb/volcengine_alb.go @@ -1,4 +1,4 @@ -package volcenginealb +package volcenginealb import ( "context" @@ -6,7 +6,6 @@ import ( "fmt" "log/slog" - xerrors "github.com/pkg/errors" vealb "github.com/volcengine/volcengine-go-sdk/service/alb" ve "github.com/volcengine/volcengine-go-sdk/volcengine" vesession "github.com/volcengine/volcengine-go-sdk/volcengine/session" @@ -14,7 +13,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-certcenter" - "github.com/usual2970/certimate/internal/pkg/utils/sliceutil" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" ) type DeployerConfig struct { @@ -53,7 +52,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -62,7 +61,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -83,11 +82,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到证书中心 - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -105,7 +104,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil @@ -124,7 +123,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeLoadBalancerAttributesResp, err := d.sdkClient.DescribeLoadBalancerAttributes(describeLoadBalancerAttributesReq) d.logger.Debug("sdk request 'alb.DescribeLoadBalancerAttributes'", slog.Any("request", describeLoadBalancerAttributesReq), slog.Any("response", describeLoadBalancerAttributesResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.DescribeLoadBalancerAttributes'") + return fmt.Errorf("failed to execute sdk request 'alb.DescribeLoadBalancerAttributes': %w", err) } // 查询 HTTPS 监听器列表 @@ -133,6 +132,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeListenersPageSize := int64(100) describeListenersPageNumber := int64(1) for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + describeListenersReq := &vealb.DescribeListenersInput{ LoadBalancerId: ve.String(d.config.LoadbalancerId), Protocol: ve.String("HTTPS"), @@ -142,7 +147,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeListenersResp, err := d.sdkClient.DescribeListeners(describeListenersReq) d.logger.Debug("sdk request 'alb.DescribeListeners'", slog.Any("request", describeListenersReq), slog.Any("response", describeListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.DescribeListeners'") + return fmt.Errorf("failed to execute sdk request 'alb.DescribeListeners': %w", err) } for _, listener := range describeListenersResp.Listeners { @@ -164,8 +169,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId var errs []error for _, listenerId := range listenerIds { - if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + default: + if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -182,7 +192,7 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str return errors.New("config `listenerId` is required") } - if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, cloudCertId); err != nil { + if err := d.updateListenerCertificate(ctx, d.config.ListenerId, cloudCertId); err != nil { return err } @@ -198,7 +208,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL describeListenerAttributesResp, err := d.sdkClient.DescribeListenerAttributes(describeListenerAttributesReq) d.logger.Debug("sdk request 'alb.DescribeListenerAttributes'", slog.Any("request", describeListenerAttributesReq), slog.Any("response", describeListenerAttributesResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.DescribeListenerAttributes'") + return fmt.Errorf("failed to execute sdk request 'alb.DescribeListenerAttributes': %w", err) } if d.config.Domain == "" { @@ -214,7 +224,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL modifyListenerAttributesResp, err := d.sdkClient.ModifyListenerAttributes(modifyListenerAttributesReq) d.logger.Debug("sdk request 'alb.ModifyListenerAttributes'", slog.Any("request", modifyListenerAttributesReq), slog.Any("response", modifyListenerAttributesResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.ModifyListenerAttributes'") + return fmt.Errorf("failed to execute sdk request 'alb.ModifyListenerAttributes': %w", err) } } else { // 指定 SNI,需部署到扩展域名 @@ -243,7 +253,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL modifyListenerAttributesResp, err := d.sdkClient.ModifyListenerAttributes(modifyListenerAttributesReq) d.logger.Debug("sdk request 'alb.ModifyListenerAttributes'", slog.Any("request", modifyListenerAttributesReq), slog.Any("response", modifyListenerAttributesResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'alb.ModifyListenerAttributes'") + return fmt.Errorf("failed to execute sdk request 'alb.ModifyListenerAttributes': %w", err) } } diff --git a/internal/pkg/core/deployer/providers/volcengine-alb/volcengine_alb_test.go b/internal/pkg/core/deployer/providers/volcengine-alb/volcengine_alb_test.go index 23a79200..18999397 100644 --- a/internal/pkg/core/deployer/providers/volcengine-alb/volcengine_alb_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-alb/volcengine_alb_test.go @@ -1,4 +1,4 @@ -package volcenginealb_test +package volcenginealb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn.go b/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn.go index fe0844d9..e9b2c325 100644 --- a/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn.go +++ b/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn.go @@ -1,12 +1,12 @@ -package volcenginecdn +package volcenginecdn import ( "context" "errors" + "fmt" "log/slog" "strings" - xerrors "github.com/pkg/errors" vecdn "github.com/volcengine/volc-sdk-golang/service/cdn" "github.com/usual2970/certimate/internal/pkg/core/deployer" @@ -46,7 +46,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { AccessKeySecret: config.AccessKeySecret, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -67,11 +67,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 CDN - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -86,7 +86,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq) d.logger.Debug("sdk request 'cdn.DescribeCertConfig'", slog.Any("request", describeCertConfigReq), slog.Any("response", describeCertConfigResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertConfig'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeCertConfig': %w", err) } if describeCertConfigResp.Result.CertNotConfig != nil { @@ -117,16 +117,21 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe var errs []error for _, domain := range domains { - // 关联证书与加速域名 - // REF: https://www.volcengine.com/docs/6454/125712 - batchDeployCertReq := &vecdn.BatchDeployCertRequest{ - CertId: upres.CertId, - Domain: domain, - } - batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq) - d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp)) - if err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + // 关联证书与加速域名 + // REF: https://www.volcengine.com/docs/6454/125712 + batchDeployCertReq := &vecdn.BatchDeployCertRequest{ + CertId: upres.CertId, + Domain: domain, + } + batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq) + d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp)) + if err != nil { + errs = append(errs, err) + } } } diff --git a/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn_test.go b/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn_test.go index c94b9828..1535deae 100644 --- a/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn_test.go @@ -1,4 +1,4 @@ -package volcenginecdn_test +package volcenginecdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/volcengine-certcenter/volcengine_certcenter.go b/internal/pkg/core/deployer/providers/volcengine-certcenter/volcengine_certcenter.go index a8062641..3989a000 100644 --- a/internal/pkg/core/deployer/providers/volcengine-certcenter/volcengine_certcenter.go +++ b/internal/pkg/core/deployer/providers/volcengine-certcenter/volcengine_certcenter.go @@ -1,11 +1,10 @@ -package volcenginecertcenter +package volcenginecertcenter import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-certcenter" @@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -59,11 +58,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到证书中心 - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } diff --git a/internal/pkg/core/deployer/providers/volcengine-clb/consts.go b/internal/pkg/core/deployer/providers/volcengine-clb/consts.go index 4d9ab1e3..3dc0f557 100644 --- a/internal/pkg/core/deployer/providers/volcengine-clb/consts.go +++ b/internal/pkg/core/deployer/providers/volcengine-clb/consts.go @@ -1,4 +1,4 @@ -package volcengineclb +package volcengineclb type ResourceType string diff --git a/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb.go b/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb.go index 37481a3f..3b6a37bf 100644 --- a/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb.go +++ b/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb.go @@ -1,4 +1,4 @@ -package volcengineclb +package volcengineclb import ( "context" @@ -6,7 +6,6 @@ import ( "fmt" "log/slog" - xerrors "github.com/pkg/errors" veclb "github.com/volcengine/volcengine-go-sdk/service/clb" ve "github.com/volcengine/volcengine-go-sdk/volcengine" vesession "github.com/volcengine/volcengine-go-sdk/volcengine/session" @@ -49,7 +48,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -58,7 +57,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -79,11 +78,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到证书中心 - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -101,7 +100,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } default: - return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) } return &deployer.DeployResult{}, nil @@ -120,7 +119,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeLoadBalancerAttributesResp, err := d.sdkClient.DescribeLoadBalancerAttributes(describeLoadBalancerAttributesReq) d.logger.Debug("sdk request 'clb.DescribeLoadBalancerAttributes'", slog.Any("request", describeLoadBalancerAttributesReq), slog.Any("response", describeLoadBalancerAttributesResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeLoadBalancerAttributes'") + return fmt.Errorf("failed to execute sdk request 'clb.DescribeLoadBalancerAttributes': %w", err) } // 查询 HTTPS 监听器列表 @@ -129,6 +128,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeListenersPageSize := int64(100) describeListenersPageNumber := int64(1) for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + describeListenersReq := &veclb.DescribeListenersInput{ LoadBalancerId: ve.String(d.config.LoadbalancerId), Protocol: ve.String("HTTPS"), @@ -138,7 +143,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId describeListenersResp, err := d.sdkClient.DescribeListeners(describeListenersReq) d.logger.Debug("sdk request 'clb.DescribeListeners'", slog.Any("request", describeListenersReq), slog.Any("response", describeListenersResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.DescribeListeners'") + return fmt.Errorf("failed to execute sdk request 'clb.DescribeListeners': %w", err) } for _, listener := range describeListenersResp.Listeners { @@ -160,8 +165,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId var errs []error for _, listenerId := range listenerIds { - if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + default: + if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { + errs = append(errs, err) + } } } @@ -178,7 +188,7 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str return errors.New("config `listenerId` is required") } - if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, cloudCertId); err != nil { + if err := d.updateListenerCertificate(ctx, d.config.ListenerId, cloudCertId); err != nil { return err } @@ -196,7 +206,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL modifyListenerAttributesResp, err := d.sdkClient.ModifyListenerAttributes(modifyListenerAttributesReq) d.logger.Debug("sdk request 'clb.ModifyListenerAttributes'", slog.Any("request", modifyListenerAttributesReq), slog.Any("response", modifyListenerAttributesResp)) if err != nil { - return xerrors.Wrap(err, "failed to execute sdk request 'clb.ModifyListenerAttributes'") + return fmt.Errorf("failed to execute sdk request 'clb.ModifyListenerAttributes': %w", err) } return nil diff --git a/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb_test.go b/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb_test.go index 9e9e63e2..fb78eba5 100644 --- a/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb_test.go @@ -1,4 +1,4 @@ -package volcengineclb_test +package volcengineclb_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn.go b/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn.go index 23af16e5..707ccde3 100644 --- a/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn.go +++ b/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn.go @@ -1,11 +1,11 @@ -package volcenginedcdn +package volcenginedcdn import ( "context" + "fmt" "log/slog" "strings" - xerrors "github.com/pkg/errors" vedcdn "github.com/volcengine/volcengine-go-sdk/service/dcdn" ve "github.com/volcengine/volcengine-go-sdk/volcengine" vesession "github.com/volcengine/volcengine-go-sdk/volcengine/session" @@ -42,7 +42,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -51,7 +51,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -72,11 +72,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到证书中心 - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -94,7 +94,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe createCertBindResp, err := d.sdkClient.CreateCertBind(createCertBindReq) d.logger.Debug("sdk request 'dcdn.CreateCertBind'", slog.Any("request", createCertBindReq), slog.Any("response", createCertBindResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'dcdn.CreateCertBind'") + return nil, fmt.Errorf("failed to execute sdk request 'dcdn.CreateCertBind': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn_test.go b/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn_test.go index 1d34a295..27bd02ce 100644 --- a/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn_test.go @@ -1,4 +1,4 @@ -package volcenginedcdn_test +package volcenginedcdn_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex.go b/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex.go index 26696d57..2f419752 100644 --- a/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex.go +++ b/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex.go @@ -1,11 +1,11 @@ -package volcengineimagex +package volcengineimagex import ( "context" "errors" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" vebase "github.com/volcengine/volc-sdk-golang/base" veimagex "github.com/volcengine/volc-sdk-golang/service/imagex/v2" @@ -43,7 +43,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -52,7 +52,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -73,7 +73,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.ServiceId == "" { return nil, errors.New("config `serviceId` is required") } @@ -82,9 +82,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } // 上传证书到证书中心 - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -98,7 +98,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe getDomainConfigResp, err := d.sdkClient.GetDomainConfig(context.TODO(), getDomainConfigReq) d.logger.Debug("sdk request 'imagex.GetDomainConfig'", slog.Any("request", getDomainConfigReq), slog.Any("response", getDomainConfigResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'imagex.GetDomainConfig'") + return nil, fmt.Errorf("failed to execute sdk request 'imagex.GetDomainConfig': %w", err) } // 更新 HTTPS 配置 @@ -127,7 +127,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe updateHttpsResp, err := d.sdkClient.UpdateHTTPS(context.TODO(), updateHttpsReq) d.logger.Debug("sdk request 'imagex.UpdateHttps'", slog.Any("request", updateHttpsReq), slog.Any("response", updateHttpsResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'imagex.UpdateHttps'") + return nil, fmt.Errorf("failed to execute sdk request 'imagex.UpdateHttps': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex_test.go b/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex_test.go index f2acb8f2..32a43555 100644 --- a/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex_test.go @@ -1,4 +1,4 @@ -package volcengineimagex_test +package volcengineimagex_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live.go b/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live.go index a12df9f2..46c0b9dc 100644 --- a/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live.go +++ b/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live.go @@ -1,12 +1,12 @@ -package volcenginelive +package volcenginelive import ( "context" "errors" + "fmt" "log/slog" "strings" - xerrors "github.com/pkg/errors" velive "github.com/volcengine/volc-sdk-golang/service/live/v20230101" ve "github.com/volcengine/volcengine-go-sdk/volcengine" @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { AccessKeySecret: config.AccessKeySecret, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -68,11 +68,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 上传证书到 Live - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -92,7 +92,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe listDomainDetailResp, err := d.sdkClient.ListDomainDetail(ctx, listDomainDetailReq) d.logger.Debug("sdk request 'live.ListDomainDetail'", slog.Any("request", listDomainDetailReq), slog.Any("response", listDomainDetailResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.ListDomainDetail'") + return nil, fmt.Errorf("failed to execute sdk request 'live.ListDomainDetail': %w", err) } if listDomainDetailResp.Result.DomainList != nil { @@ -125,17 +125,22 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe var errs []error for _, domain := range domains { - // 绑定证书 - // REF: https://www.volcengine.com/docs/6469/1186278#%E7%BB%91%E5%AE%9A%E8%AF%81%E4%B9%A6 - bindCertReq := &velive.BindCertBody{ - ChainID: upres.CertId, - Domain: domain, - HTTPS: ve.Bool(true), - } - bindCertResp, err := d.sdkClient.BindCert(ctx, bindCertReq) - d.logger.Debug("sdk request 'live.BindCert'", slog.Any("request", bindCertReq), slog.Any("response", bindCertResp)) - if err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + // 绑定证书 + // REF: https://www.volcengine.com/docs/6469/1186278#%E7%BB%91%E5%AE%9A%E8%AF%81%E4%B9%A6 + bindCertReq := &velive.BindCertBody{ + ChainID: upres.CertId, + Domain: domain, + HTTPS: ve.Bool(true), + } + bindCertResp, err := d.sdkClient.BindCert(ctx, bindCertReq) + d.logger.Debug("sdk request 'live.BindCert'", slog.Any("request", bindCertReq), slog.Any("response", bindCertResp)) + if err != nil { + errs = append(errs, err) + } } } diff --git a/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live_test.go b/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live_test.go index 2222e7b9..57891eea 100644 --- a/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live_test.go @@ -1,4 +1,4 @@ -package volcenginelive_test +package volcenginelive_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos.go b/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos.go index f2ac4128..365d95f2 100644 --- a/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos.go +++ b/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos.go @@ -1,4 +1,4 @@ -package volcenginetos +package volcenginetos import ( "context" @@ -6,7 +6,6 @@ import ( "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/volcengine/ve-tos-golang-sdk/v2/tos" "github.com/usual2970/certimate/internal/pkg/core/deployer" @@ -43,7 +42,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ @@ -52,7 +51,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { Region: config.Region, }) if err != nil { - return nil, xerrors.Wrap(err, "failed to create ssl uploader") + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) } return &DeployerProvider{ @@ -73,7 +72,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { if d.config.Bucket == "" { return nil, errors.New("config `bucket` is required") } @@ -82,9 +81,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe } // 上传证书到证书中心 - upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to upload certificate file") + return nil, fmt.Errorf("failed to upload certificate file: %w", err) } else { d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } @@ -101,7 +100,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe putBucketCustomDomainResp, err := d.sdkClient.PutBucketCustomDomain(context.TODO(), putBucketCustomDomainReq) d.logger.Debug("sdk request 'tos.PutBucketCustomDomain'", slog.Any("request", putBucketCustomDomainReq), slog.Any("response", putBucketCustomDomainResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'tos.PutBucketCustomDomain'") + return nil, fmt.Errorf("failed to execute sdk request 'tos.PutBucketCustomDomain': %w", err) } return &deployer.DeployResult{}, nil diff --git a/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos_test.go b/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos_test.go index 0c9b398a..8dc1cf3d 100644 --- a/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos_test.go @@ -1,4 +1,4 @@ -package volcenginetos_test +package volcenginetos_test import ( "context" diff --git a/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go new file mode 100644 index 00000000..2faf1b03 --- /dev/null +++ b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go @@ -0,0 +1,281 @@ +package wangsucdnpro + +import ( + "bytes" + "context" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "log/slog" + "regexp" + "strconv" + "time" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + wangsucdn "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdn" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" +) + +type DeployerConfig struct { + // 网宿云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 网宿云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 网宿云 API Key。 + ApiKey string `json:"apiKey"` + // 网宿云环境。 + Environment string `json:"environment"` + // 加速域名(支持泛域名)。 + Domain string `json:"domain"` + // 证书 ID。 + // 选填。 + CertificateId string `json:"certificateId,omitempty"` + // Webhook ID。 + // 选填。 + WebhookId string `json:"webhookId,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *wangsucdn.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + if d.config.Domain == "" { + return nil, errors.New("config `domain` is required") + } + + // 解析证书内容 + certX509, err := certutil.ParseCertificateFromPEM(certPEM) + if err != nil { + return nil, err + } + + // 查询已部署加速域名的详情 + getHostnameDetailResp, err := d.sdkClient.GetHostnameDetail(d.config.Domain) + d.logger.Debug("sdk request 'cdn.GetHostnameDetail'", slog.String("hostname", d.config.Domain), slog.Any("response", getHostnameDetailResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetHostnameDetail': %w", err) + } + + // 生成网宿云证书参数 + encryptedPrivateKey, err := encryptPrivateKey(privkeyPEM, d.config.ApiKey, time.Now().Unix()) + if err != nil { + return nil, fmt.Errorf("failed to encrypt private key: %w", err) + } + certificateNewVersionInfo := &wangsucdn.CertificateVersion{ + PrivateKey: typeutil.ToPtr(encryptedPrivateKey), + Certificate: typeutil.ToPtr(certPEM), + IdentificationInfo: &wangsucdn.CertificateVersionIdentificationInfo{ + CommonName: typeutil.ToPtr(certX509.Subject.CommonName), + SubjectAlternativeNames: &certX509.DNSNames, + }, + } + + // 网宿云证书 URL 中包含证书 ID 及版本号 + // 格式: + // http://open.chinanetcenter.com/cdn/certificates/5dca2205f9e9cc0001df7b33 + // http://open.chinanetcenter.com/cdn/certificates/329f12c1fe6708c23c31e91f/versions/5 + var wangsuCertUrl string + var wangsuCertId string + var wangsuCertVer int32 + + // 如果原证书 ID 为空,则创建证书;否则更新证书。 + timestamp := time.Now().Unix() + if d.config.CertificateId == "" { + // 创建证书 + createCertificateReq := &wangsucdn.CreateCertificateRequest{ + Timestamp: timestamp, + Name: typeutil.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())), + AutoRenew: typeutil.ToPtr("Off"), + NewVersion: certificateNewVersionInfo, + } + createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) + d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.CreateCertificate': %w", err) + } + + wangsuCertUrl = createCertificateResp.CertificateUrl + d.logger.Info("ssl certificate uploaded", slog.Any("certUrl", wangsuCertUrl)) + + wangsuCertIdMatches := regexp.MustCompile(`/certificates/([a-zA-Z0-9-]+)`).FindStringSubmatch(wangsuCertUrl) + if len(wangsuCertIdMatches) > 1 { + wangsuCertId = wangsuCertIdMatches[1] + } + + wangsuCertVer = 1 + } else { + // 更新证书 + updateCertificateReq := &wangsucdn.UpdateCertificateRequest{ + Timestamp: timestamp, + Name: typeutil.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())), + AutoRenew: typeutil.ToPtr("Off"), + NewVersion: certificateNewVersionInfo, + } + updateCertificateResp, err := d.sdkClient.UpdateCertificate(d.config.CertificateId, updateCertificateReq) + d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("certificateId", d.config.CertificateId), slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.UpdateCertificate': %w", err) + } + + wangsuCertUrl = updateCertificateResp.CertificateUrl + d.logger.Info("ssl certificate uploaded", slog.Any("certUrl", wangsuCertUrl)) + + wangsuCertIdMatches := regexp.MustCompile(`/certificates/([a-zA-Z0-9-]+)`).FindStringSubmatch(wangsuCertUrl) + if len(wangsuCertIdMatches) > 1 { + wangsuCertId = wangsuCertIdMatches[1] + } + + wangsuCertVerMatches := regexp.MustCompile(`/versions/(\d+)`).FindStringSubmatch(wangsuCertUrl) + if len(wangsuCertVerMatches) > 1 { + n, _ := strconv.ParseInt(wangsuCertVerMatches[1], 10, 32) + wangsuCertVer = int32(n) + } + } + + // 创建部署任务 + // REF: https://www.wangsu.com/document/api-doc/27034 + createDeploymentTaskReq := &wangsucdn.CreateDeploymentTaskRequest{ + Name: typeutil.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())), + Target: typeutil.ToPtr(d.config.Environment), + Actions: &[]wangsucdn.DeploymentTaskAction{ + { + Action: typeutil.ToPtr("deploy_cert"), + CertificateId: typeutil.ToPtr(wangsuCertId), + Version: typeutil.ToPtr(wangsuCertVer), + }, + }, + } + if d.config.WebhookId != "" { + createDeploymentTaskReq.Webhook = typeutil.ToPtr(d.config.WebhookId) + } + createDeploymentTaskResp, err := d.sdkClient.CreateDeploymentTask(createDeploymentTaskReq) + d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("request", createDeploymentTaskReq), slog.Any("response", createDeploymentTaskResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.CreateDeploymentTask': %w", err) + } + + // 循环获取部署任务详细信息,等待任务状态变更 + // REF: https://www.wangsu.com/document/api-doc/27038 + var wangsuTaskId string + wangsuTaskMatches := regexp.MustCompile(`/deploymentTasks/([a-zA-Z0-9-]+)`).FindStringSubmatch(createDeploymentTaskResp.DeploymentTaskUrl) + if len(wangsuTaskMatches) > 1 { + wangsuTaskId = wangsuTaskMatches[1] + } + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + getDeploymentTaskDetailResp, err := d.sdkClient.GetDeploymentTaskDetail(wangsuTaskId) + d.logger.Info("sdk request 'cdn.GetDeploymentTaskDetail'", slog.Any("taskId", wangsuTaskId), slog.Any("response", getDeploymentTaskDetailResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetDeploymentTaskDetail': %w", err) + } + + if getDeploymentTaskDetailResp.Status == "failed" { + return nil, errors.New("unexpected deployment task status") + } else if getDeploymentTaskDetailResp.Status == "succeeded" || getDeploymentTaskDetailResp.FinishTime != "" { + break + } + + d.logger.Info(fmt.Sprintf("waiting for deployment task completion (current status: %s) ...", getDeploymentTaskDetailResp.Status)) + time.Sleep(time.Second * 5) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*wangsucdn.Client, error) { + if accessKeyId == "" { + return nil, errors.New("invalid wangsu access key id") + } + + if accessKeySecret == "" { + return nil, errors.New("invalid wangsu access key secret") + } + + return wangsucdn.NewClient(accessKeyId, accessKeySecret), nil +} + +func encryptPrivateKey(privkeyPEM string, apiKey string, timestamp int64) (string, error) { + date := time.Unix(timestamp, 0).UTC() + dateStr := date.Format("Mon, 02 Jan 2006 15:04:05 GMT") + + mac := hmac.New(sha256.New, []byte(apiKey)) + mac.Write([]byte(dateStr)) + aesivkey := mac.Sum(nil) + aesivkeyHex := hex.EncodeToString(aesivkey) + + if len(aesivkeyHex) != 64 { + return "", fmt.Errorf("invalid hmac length: %d", len(aesivkeyHex)) + } + ivHex := aesivkeyHex[:32] + keyHex := aesivkeyHex[32:64] + + iv, err := hex.DecodeString(ivHex) + if err != nil { + return "", fmt.Errorf("failed to decode iv: %w", err) + } + + key, err := hex.DecodeString(keyHex) + if err != nil { + return "", fmt.Errorf("failed to decode key: %w", err) + } + + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + + plainBytes := []byte(privkeyPEM) + padlen := aes.BlockSize - len(plainBytes)%aes.BlockSize + if padlen > 0 { + paddata := bytes.Repeat([]byte{byte(padlen)}, padlen) + plainBytes = append(plainBytes, paddata...) + } + + encBytes := make([]byte, len(plainBytes)) + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(encBytes, plainBytes) + + return base64.StdEncoding.EncodeToString(encBytes), nil +} diff --git a/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro_test.go b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro_test.go new file mode 100644 index 00000000..51018fe4 --- /dev/null +++ b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro_test.go @@ -0,0 +1,95 @@ +package wangsucdnpro_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-cdnpro" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fApiKey string + fEnvironment string + fDomain string + fCertificateId string + fWebhookId string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_WANGSUCDNPRO_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") + flag.StringVar(&fEnvironment, argsPrefix+"ENVIRONMENT", "production", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") + flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "") + flag.StringVar(&fWebhookId, argsPrefix+"WEBHOOKID", "", "") +} + +/* +Shell command to run this test: + + go test -v ./wangsu_cdnpro_test.go -args \ + --CERTIMATE_DEPLOYER_WANGSUCDNPRO_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_WANGSUCDNPRO_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_WANGSUCDNPRO_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_WANGSUCDNPRO_ACCESSKEYSECRET="your-access-key-secret" \ + --CERTIMATE_DEPLOYER_WANGSUCDNPRO_APIKEY="your-api-key" \ + --CERTIMATE_DEPLOYER_WANGSUCDNPRO_ENVIRONMENT="production" \ + --CERTIMATE_DEPLOYER_WANGSUCDNPRO_DOMAIN="example.com" \ + --CERTIMATE_DEPLOYER_WANGSUCDNPRO_CERTIFICATEID="your-certificate-id" \ + --CERTIMATE_DEPLOYER_WANGSUCDNPRO_WEBHOOKID="your-webhook-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("APIKEY: %v", fApiKey), + fmt.Sprintf("ENVIRONMENT: %v", fEnvironment), + fmt.Sprintf("DOMAIN: %v", fDomain), + fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), + fmt.Sprintf("WEBHOOKID: %v", fWebhookId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + ApiKey: fApiKey, + Environment: fEnvironment, + Domain: fDomain, + CertificateId: fCertificateId, + WebhookId: fWebhookId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/webhook/webhook.go b/internal/pkg/core/deployer/providers/webhook/webhook.go index 0afc60f0..07b2eaaa 100644 --- a/internal/pkg/core/deployer/providers/webhook/webhook.go +++ b/internal/pkg/core/deployer/providers/webhook/webhook.go @@ -4,22 +4,29 @@ import ( "context" "crypto/tls" "encoding/json" + "fmt" "log/slog" + "net/http" + "net/url" "strings" "time" "github.com/go-resty/resty/v2" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type DeployerConfig struct { // Webhook URL。 WebhookUrl string `json:"webhookUrl"` - // Webhook 回调数据(JSON 格式)。 + // Webhook 回调数据(application/json 或 application/x-www-form-urlencoded 格式)。 WebhookData string `json:"webhookData,omitempty"` + // 请求谓词。 + // 零值时默认为 "POST"。 + Method string `json:"method,omitempty"` + // 请求标头。 + Headers map[string]string `json:"headers,omitempty"` // 是否允许不安全的连接。 AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } @@ -61,33 +68,113 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { return d } -func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { - certX509, err := certutil.ParseCertificateFromPEM(certPem) +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 解析证书内容 + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { - return nil, xerrors.Wrap(err, "failed to parse x509") + return nil, fmt.Errorf("failed to parse x509: %w", err) } + // 处理 Webhook URL + webhookUrl, err := url.Parse(d.config.WebhookUrl) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook url: %w", err) + } else if webhookUrl.Scheme != "http" && webhookUrl.Scheme != "https" { + return nil, fmt.Errorf("unsupported webhook url scheme '%s'", webhookUrl.Scheme) + } else { + webhookUrl.Path = strings.ReplaceAll(webhookUrl.Path, "${DOMAIN}", url.PathEscape(certX509.Subject.CommonName)) + } + + // 处理 Webhook 请求谓词 + webhookMethod := strings.ToUpper(d.config.Method) + if webhookMethod == "" { + webhookMethod = http.MethodPost + } else if webhookMethod != http.MethodGet && + webhookMethod != http.MethodPost && + webhookMethod != http.MethodPut && + webhookMethod != http.MethodPatch && + webhookMethod != http.MethodDelete { + return nil, fmt.Errorf("unsupported webhook request method '%s'", webhookMethod) + } + + // 处理 Webhook 请求标头 + webhookHeaders := make(http.Header) + for k, v := range d.config.Headers { + webhookHeaders.Set(k, v) + } + + // 处理 Webhook 请求内容类型 + const CONTENT_TYPE_JSON = "application/json" + const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded" + const CONTENT_TYPE_MULTIPART = "multipart/form-data" + webhookContentType := webhookHeaders.Get("Content-Type") + if webhookContentType == "" { + webhookContentType = CONTENT_TYPE_JSON + webhookHeaders.Set("Content-Type", CONTENT_TYPE_JSON) + } else if strings.HasPrefix(webhookContentType, CONTENT_TYPE_JSON) && + strings.HasPrefix(webhookContentType, CONTENT_TYPE_FORM) && + strings.HasPrefix(webhookContentType, CONTENT_TYPE_MULTIPART) { + return nil, fmt.Errorf("unsupported webhook content type '%s'", webhookContentType) + } + + // 处理 Webhook 请求数据 var webhookData interface{} - err = json.Unmarshal([]byte(d.config.WebhookData), &webhookData) - if err != nil { - return nil, xerrors.Wrap(err, "failed to unmarshall webhook data") + if d.config.WebhookData == "" { + webhookData = map[string]string{ + "name": strings.Join(certX509.DNSNames, ";"), + "cert": certPEM, + "privkey": privkeyPEM, + } + } else { + err = json.Unmarshal([]byte(d.config.WebhookData), &webhookData) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err) + } + + replaceJsonValueRecursively(webhookData, "${DOMAIN}", certX509.Subject.CommonName) + replaceJsonValueRecursively(webhookData, "${DOMAINS}", strings.Join(certX509.DNSNames, ";")) + replaceJsonValueRecursively(webhookData, "${CERTIFICATE}", certPEM) + replaceJsonValueRecursively(webhookData, "${PRIVATE_KEY}", privkeyPEM) + + if webhookMethod == http.MethodGet || webhookContentType == CONTENT_TYPE_FORM || webhookContentType == CONTENT_TYPE_MULTIPART { + temp := make(map[string]string) + jsonb, err := json.Marshal(webhookData) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err) + } else if err := json.Unmarshal(jsonb, &temp); err != nil { + return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err) + } else { + webhookData = temp + } + } } - replaceJsonValueRecursively(webhookData, "${DOMAIN}", certX509.Subject.CommonName) - replaceJsonValueRecursively(webhookData, "${DOMAINS}", strings.Join(certX509.DNSNames, ";")) - replaceJsonValueRecursively(webhookData, "${SUBJECT_ALT_NAMES}", strings.Join(certX509.DNSNames, ";")) - replaceJsonValueRecursively(webhookData, "${CERTIFICATE}", certPem) - replaceJsonValueRecursively(webhookData, "${PRIVATE_KEY}", privkeyPem) - - resp, err := d.httpClient.R(). + // 生成请求 + // 其中 GET 请求需转换为查询参数 + req := d.httpClient.R(). SetContext(ctx). - SetHeader("Content-Type", "application/json"). - SetBody(webhookData). - Post(d.config.WebhookUrl) + SetHeaderMultiValues(webhookHeaders) + req.URL = webhookUrl.String() + req.Method = webhookMethod + if webhookMethod == http.MethodGet { + req.SetQueryParams(webhookData.(map[string]string)) + } else { + switch webhookContentType { + case CONTENT_TYPE_JSON: + req.SetBody(webhookData) + case CONTENT_TYPE_FORM: + req.SetFormData(webhookData.(map[string]string)) + case CONTENT_TYPE_MULTIPART: + req.SetMultipartFormData(webhookData.(map[string]string)) + } + } + + // 发送请求 + resp, err := req.SetDebug(true).Send() if err != nil { - return nil, xerrors.Wrap(err, "failed to send webhook request") - } else if resp.StatusCode() != 200 { - return nil, xerrors.Errorf("unexpected webhook response status code: %d", resp.StatusCode()) + return nil, fmt.Errorf("failed to send webhook request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("unexpected webhook response status code: %d", resp.StatusCode()) } d.logger.Debug("webhook responded", slog.Any("response", resp.String())) diff --git a/internal/pkg/core/deployer/providers/webhook/webhook_test.go b/internal/pkg/core/deployer/providers/webhook/webhook_test.go index a31ef913..8642ef14 100644 --- a/internal/pkg/core/deployer/providers/webhook/webhook_test.go +++ b/internal/pkg/core/deployer/providers/webhook/webhook_test.go @@ -1,4 +1,4 @@ -package webhook_test +package webhook_test import ( "context" @@ -12,10 +12,11 @@ import ( ) var ( - fInputCertPath string - fInputKeyPath string - fWebhookUrl string - fWebhookData string + fInputCertPath string + fInputKeyPath string + fWebhookUrl string + fWebhookContentType string + fWebhookData string ) func init() { @@ -24,6 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fWebhookUrl, argsPrefix+"URL", "", "") + flag.StringVar(&fWebhookContentType, argsPrefix+"CONTENTTYPE", "application/json", "") flag.StringVar(&fWebhookData, argsPrefix+"DATA", "", "") } @@ -34,7 +36,8 @@ Shell command to run this test: --CERTIMATE_DEPLOYER_WEBHOOK_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_WEBHOOK_INPUTKEYPATH="/path/to/your-input-key.pem" \ --CERTIMATE_DEPLOYER_WEBHOOK_URL="https://example.com/your-webhook-url" \ - --CERTIMATE_DEPLOYER_WEBHOOK_DATA="{\"certificate\":\"${Certificate}\",\"privateKey\":\"${PrivateKey}\"}" + --CERTIMATE_DEPLOYER_WEBHOOK_CONTENTTYPE="application/json" \ + --CERTIMATE_DEPLOYER_WEBHOOK_DATA="{\"certificate\":\"${CERTIFICATE}\",\"privateKey\":\"${PRIVATE_KEY}\"}" */ func TestDeploy(t *testing.T) { flag.Parse() @@ -45,12 +48,17 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl), + fmt.Sprintf("WEBHOOKCONTENTTYPE: %v", fWebhookContentType), fmt.Sprintf("WEBHOOKDATA: %v", fWebhookData), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - WebhookUrl: fWebhookUrl, - WebhookData: fWebhookData, + WebhookUrl: fWebhookUrl, + WebhookData: fWebhookData, + Method: "POST", + Headers: map[string]string{ + "Content-Type": fWebhookContentType, + }, AllowInsecureConnections: true, }) if err != nil { diff --git a/internal/pkg/core/notifier/notifier.go b/internal/pkg/core/notifier/notifier.go index 97485215..876b5d48 100644 --- a/internal/pkg/core/notifier/notifier.go +++ b/internal/pkg/core/notifier/notifier.go @@ -1,4 +1,4 @@ -package notifier +package notifier import ( "context" diff --git a/internal/pkg/core/notifier/providers/bark/bark.go b/internal/pkg/core/notifier/providers/bark/bark.go index a6833ae1..ccdd5736 100644 --- a/internal/pkg/core/notifier/providers/bark/bark.go +++ b/internal/pkg/core/notifier/providers/bark/bark.go @@ -1,4 +1,4 @@ -package bark +package bark import ( "context" @@ -32,6 +32,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { return &NotifierProvider{ config: config, + logger: slog.Default(), }, nil } diff --git a/internal/pkg/core/notifier/providers/bark/bark_test.go b/internal/pkg/core/notifier/providers/bark/bark_test.go index bd0441b4..f90448a9 100644 --- a/internal/pkg/core/notifier/providers/bark/bark_test.go +++ b/internal/pkg/core/notifier/providers/bark/bark_test.go @@ -1,4 +1,4 @@ -package bark_test +package bark_test import ( "context" diff --git a/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go b/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go index f0ab4741..9eb94dcf 100644 --- a/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go +++ b/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go @@ -1,8 +1,10 @@ -package dingtalk +package dingtalk import ( "context" + "fmt" "log/slog" + "net/url" "github.com/nikoksr/notify/service/dingding" @@ -10,8 +12,8 @@ import ( ) type NotifierConfig struct { - // 钉钉机器人的 Token。 - AccessToken string `json:"accessToken"` + // 钉钉机器人的 Webhook 地址。 + WebhookUrl string `json:"webhookUrl"` // 钉钉机器人的 Secret。 Secret string `json:"secret"` } @@ -30,6 +32,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { return &NotifierProvider{ config: config, + logger: slog.Default(), }, nil } @@ -43,8 +46,13 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { } func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { + webhookUrl, err := url.Parse(n.config.WebhookUrl) + if err != nil { + return nil, fmt.Errorf("invalid webhook url: %w", err) + } + srv := dingding.New(&dingding.Config{ - Token: n.config.AccessToken, + Token: webhookUrl.Query().Get("access_token"), Secret: n.config.Secret, }) diff --git a/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go b/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go index 836cf498..086e3a94 100644 --- a/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go +++ b/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go @@ -1,4 +1,4 @@ -package dingtalk_test +package dingtalk_test import ( "context" diff --git a/internal/pkg/core/notifier/providers/email/email.go b/internal/pkg/core/notifier/providers/email/email.go index ebf2d150..69d39012 100644 --- a/internal/pkg/core/notifier/providers/email/email.go +++ b/internal/pkg/core/notifier/providers/email/email.go @@ -1,4 +1,4 @@ -package email +package email import ( "context" @@ -19,7 +19,7 @@ type NotifierConfig struct { // 零值时根据是否启用 TLS 决定。 SmtpPort int32 `json:"smtpPort"` // 是否启用 TLS。 - SmtpTLS bool `json:"smtpTLS"` + SmtpTls bool `json:"smtpTls"` // 用户名。 Username string `json:"username"` // 密码。 @@ -44,6 +44,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { return &NotifierProvider{ config: config, + logger: slog.Default(), }, nil } @@ -64,7 +65,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s var smtpAddr string if n.config.SmtpPort == 0 { - if n.config.SmtpTLS { + if n.config.SmtpTls { smtpAddr = fmt.Sprintf("%s:465", n.config.SmtpHost) } else { smtpAddr = fmt.Sprintf("%s:25", n.config.SmtpHost) @@ -74,7 +75,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s } var yak *mailyak.MailYak - if n.config.SmtpTLS { + if n.config.SmtpTls { yak, err = mailyak.NewWithTLS(smtpAddr, smtpAuth, newTlsConfig()) if err != nil { return nil, err diff --git a/internal/pkg/core/notifier/providers/email/email_test.go b/internal/pkg/core/notifier/providers/email/email_test.go index df006c24..30bfba07 100644 --- a/internal/pkg/core/notifier/providers/email/email_test.go +++ b/internal/pkg/core/notifier/providers/email/email_test.go @@ -1,4 +1,4 @@ -package email_test +package email_test import ( "context" @@ -67,7 +67,7 @@ func TestNotify(t *testing.T) { notifier, err := provider.NewNotifier(&provider.NotifierConfig{ SmtpHost: fSmtpHost, SmtpPort: int32(fSmtpPort), - SmtpTLS: fSmtpTLS, + SmtpTls: fSmtpTLS, Username: fUsername, Password: fPassword, SenderAddress: fSenderAddress, diff --git a/internal/pkg/core/notifier/providers/gotify/gotify.go b/internal/pkg/core/notifier/providers/gotify/gotify.go new file mode 100644 index 00000000..aed6e7c8 --- /dev/null +++ b/internal/pkg/core/notifier/providers/gotify/gotify.go @@ -0,0 +1,96 @@ +package gotify + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + + "github.com/usual2970/certimate/internal/pkg/core/notifier" +) + +type NotifierConfig struct { + // Gotify 服务地址。 + Url string `json:"url"` + // Gotify Token。 + Token string `json:"token"` + // Gotify 消息优先级。 + Priority int64 `json:"priority,omitempty"` +} + +type NotifierProvider struct { + config *NotifierConfig + logger *slog.Logger + httpClient *http.Client +} + +var _ notifier.Notifier = (*NotifierProvider)(nil) + +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { + if config == nil { + panic("config is nil") + } + + return &NotifierProvider{ + config: config, + logger: slog.Default(), + httpClient: http.DefaultClient, + }, nil +} + +func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { + if logger == nil { + n.logger = slog.Default() + } else { + n.logger = logger + } + return n +} + +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { + reqBody := &struct { + Title string `json:"title"` + Message string `json:"message"` + Priority int64 `json:"priority"` + }{ + Title: subject, + Message: message, + Priority: n.config.Priority, + } + + body, err := json.Marshal(reqBody) + if err != nil { + return nil, fmt.Errorf("gotify api error: failed to encode message body: %w", err) + } + + req, err := http.NewRequestWithContext( + ctx, + http.MethodPost, + fmt.Sprintf("%s/message", n.config.Url), + bytes.NewReader(body), + ) + if err != nil { + return nil, fmt.Errorf("gotify api error: failed to create new request: %w", err) + } + + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", n.config.Token)) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + resp, err := n.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("gotify api error: failed to send request: %w", err) + } + defer resp.Body.Close() + + result, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("gotify api error: failed to read response: %w", err) + } else if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("gotify api error: unexpected status code: %d, resp: %s", resp.StatusCode, string(result)) + } + + return ¬ifier.NotifyResult{}, nil +} diff --git a/internal/pkg/core/notifier/providers/gotify/gotify_test.go b/internal/pkg/core/notifier/providers/gotify/gotify_test.go new file mode 100644 index 00000000..31ad64af --- /dev/null +++ b/internal/pkg/core/notifier/providers/gotify/gotify_test.go @@ -0,0 +1,68 @@ +package gotify_test + +import ( + "context" + "flag" + "fmt" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify" +) + +const ( + mockSubject = "test_subject" + mockMessage = "test_message" +) + +var ( + fUrl string + fToken string + fPriority int64 +) + +func init() { + argsPrefix := "CERTIMATE_NOTIFIER_GOTIFY_" + flag.StringVar(&fUrl, argsPrefix+"URL", "", "") + flag.StringVar(&fToken, argsPrefix+"TOKEN", "", "") + flag.Int64Var(&fPriority, argsPrefix+"PRIORITY", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./gotify_test.go -args \ + --CERTIMATE_NOTIFIER_GOTIFY_URL="https://example.com" \ + --CERTIMATE_NOTIFIER_GOTIFY_TOKEN="your-gotify-application-token" \ + --CERTIMATE_NOTIFIER_GOTIFY_PRIORITY="your-message-priority" +*/ +func TestNotify(t *testing.T) { + flag.Parse() + + t.Run("Notify", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("URL: %v", fUrl), + fmt.Sprintf("TOKEN: %v", fToken), + fmt.Sprintf("PRIORITY: %d", fPriority), + }, "\n")) + + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ + Url: fUrl, + Token: fToken, + Priority: fPriority, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + res, err := notifier.Notify(context.Background(), mockSubject, mockMessage) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/notifier/providers/lark/lark.go b/internal/pkg/core/notifier/providers/lark/lark.go index 150f4e04..e8ad7816 100644 --- a/internal/pkg/core/notifier/providers/lark/lark.go +++ b/internal/pkg/core/notifier/providers/lark/lark.go @@ -1,4 +1,4 @@ -package lark +package lark import ( "context" @@ -28,6 +28,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { return &NotifierProvider{ config: config, + logger: slog.Default(), }, nil } diff --git a/internal/pkg/core/notifier/providers/lark/lark_test.go b/internal/pkg/core/notifier/providers/lark/lark_test.go index 2b04a7a6..f72ca443 100644 --- a/internal/pkg/core/notifier/providers/lark/lark_test.go +++ b/internal/pkg/core/notifier/providers/lark/lark_test.go @@ -1,4 +1,4 @@ -package lark_test +package lark_test import ( "context" diff --git a/internal/pkg/core/notifier/providers/mattermost/mattermost.go b/internal/pkg/core/notifier/providers/mattermost/mattermost.go new file mode 100644 index 00000000..ed3a507a --- /dev/null +++ b/internal/pkg/core/notifier/providers/mattermost/mattermost.go @@ -0,0 +1,92 @@ +package mattermost + +import ( + "bytes" + "context" + "encoding/json" + "io" + "log/slog" + "net/http" + "strings" + + "github.com/nikoksr/notify/service/mattermost" + "github.com/usual2970/certimate/internal/pkg/core/notifier" +) + +type NotifierConfig struct { + // 服务地址。 + ServerUrl string `json:"serverUrl"` + // 用户名。 + Username string `json:"username"` + // 密码。 + Password string `json:"password"` + // 频道 ID。 + ChannelId string `json:"channelId"` +} + +type NotifierProvider struct { + config *NotifierConfig + logger *slog.Logger +} + +var _ notifier.Notifier = (*NotifierProvider)(nil) + +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { + if config == nil { + panic("config is nil") + } + + return &NotifierProvider{ + config: config, + logger: slog.Default(), + }, nil +} + +func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { + if logger == nil { + n.logger = slog.Default() + } else { + n.logger = logger + } + return n +} + +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { + srv := mattermost.New(strings.TrimRight(n.config.ServerUrl, "/")) + + if err := srv.LoginWithCredentials(ctx, n.config.Username, n.config.Password); err != nil { + return nil, err + } + + srv.AddReceivers(n.config.ChannelId) + + // 复写消息样式 + srv.PreSend(func(req *http.Request) error { + m := map[string]interface{}{ + "channel_id": n.config.ChannelId, + "props": map[string]interface{}{ + "attachments": []map[string]interface{}{ + { + "title": subject, + "text": message, + }, + }, + }, + } + + if body, err := json.Marshal(m); err != nil { + return err + } else { + req.ContentLength = int64(len(body)) + req.Body = io.NopCloser(bytes.NewReader(body)) + } + + return nil + }) + + if err = srv.Send(ctx, subject, message); err != nil { + return nil, err + } + + return ¬ifier.NotifyResult{}, nil +} diff --git a/internal/pkg/core/notifier/providers/mattermost/mattermost_test.go b/internal/pkg/core/notifier/providers/mattermost/mattermost_test.go new file mode 100644 index 00000000..6db6cc42 --- /dev/null +++ b/internal/pkg/core/notifier/providers/mattermost/mattermost_test.go @@ -0,0 +1,74 @@ +package mattermost_test + +import ( + "context" + "flag" + "fmt" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost" +) + +const ( + mockSubject = "test_subject" + mockMessage = "test_message" +) + +var ( + fServerUrl string + fChannelId string + fUsername string + fPassword string +) + +func init() { + argsPrefix := "CERTIMATE_NOTIFIER_MATTERMOST_" + + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") + flag.StringVar(&fChannelId, argsPrefix+"CHANNELID", "", "") + flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "") + flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "") +} + +/* +Shell command to run this test: + + go test -v ./mattermost_test.go -args \ + --CERTIMATE_NOTIFIER_MATTERMOST_SERVERURL="https://example.com/your-server-url" \ + --CERTIMATE_NOTIFIER_MATTERMOST_CHANNELID="your-chanel-id" \ + --CERTIMATE_NOTIFIER_MATTERMOST_USERNAME="your-username" \ + --CERTIMATE_NOTIFIER_MATTERMOST_PASSWORD="your-password" +*/ +func TestNotify(t *testing.T) { + flag.Parse() + + t.Run("Notify", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("SERVERURL: %v", fServerUrl), + fmt.Sprintf("CHANNELID: %v", fChannelId), + fmt.Sprintf("USERNAME: %v", fUsername), + fmt.Sprintf("PASSWORD: %v", fPassword), + }, "\n")) + + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ + ServerUrl: fServerUrl, + ChannelId: fChannelId, + Username: fUsername, + Password: fPassword, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + res, err := notifier.Notify(context.Background(), mockSubject, mockMessage) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/notifier/providers/pushover/pushover.go b/internal/pkg/core/notifier/providers/pushover/pushover.go new file mode 100644 index 00000000..f306df1f --- /dev/null +++ b/internal/pkg/core/notifier/providers/pushover/pushover.go @@ -0,0 +1,96 @@ +package pushover + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + + "github.com/usual2970/certimate/internal/pkg/core/notifier" +) + +type NotifierConfig struct { + // Pushover API Token。 + Token string `json:"token"` + // 用户或分组标识。 + User string `json:"user"` +} + +type NotifierProvider struct { + config *NotifierConfig + logger *slog.Logger + httpClient *http.Client +} + +var _ notifier.Notifier = (*NotifierProvider)(nil) + +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { + if config == nil { + panic("config is nil") + } + + return &NotifierProvider{ + config: config, + logger: slog.Default(), + httpClient: http.DefaultClient, + }, nil +} + +func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { + if logger == nil { + n.logger = slog.Default() + } else { + n.logger = logger + } + return n +} + +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { + // REF: https://pushover.net/api + reqBody := &struct { + Token string `json:"token"` + User string `json:"user"` + Title string `json:"title"` + Message string `json:"message"` + }{ + Token: n.config.Token, + User: n.config.User, + Title: subject, + Message: message, + } + + body, err := json.Marshal(reqBody) + if err != nil { + return nil, fmt.Errorf("pushover api error: failed to encode message body: %w", err) + } + + req, err := http.NewRequestWithContext( + ctx, + http.MethodPost, + "https://api.pushover.net/1/messages.json", + bytes.NewReader(body), + ) + if err != nil { + return nil, fmt.Errorf("pushover api error: failed to create new request: %w", err) + } + + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + resp, err := n.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("pushover api error: failed to send request: %w", err) + } + defer resp.Body.Close() + + result, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("pushover api error: failed to read response: %w", err) + } else if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("pushover api error: unexpected status code: %d, resp: %s", resp.StatusCode, string(result)) + } + + return ¬ifier.NotifyResult{}, nil +} diff --git a/internal/pkg/core/notifier/providers/pushover/pushover_test.go b/internal/pkg/core/notifier/providers/pushover/pushover_test.go new file mode 100644 index 00000000..450beac1 --- /dev/null +++ b/internal/pkg/core/notifier/providers/pushover/pushover_test.go @@ -0,0 +1,62 @@ +package pushover_test + +import ( + "context" + "flag" + "fmt" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover" +) + +const ( + mockSubject = "test_subject" + mockMessage = "test_message" +) + +var ( + fToken string + fUser string +) + +func init() { + argsPrefix := "CERTIMATE_NOTIFIER_PUSHOVER_" + flag.StringVar(&fToken, argsPrefix+"TOKEN", "", "") + flag.StringVar(&fUser, argsPrefix+"USER", "", "") +} + +/* +Shell command to run this test: + + go test -v ./pushover_test.go -args \ + --CERTIMATE_NOTIFIER_PUSHOVER_TOKEN="your-pushover-token" \ + --CERTIMATE_NOTIFIER_PUSHOVER_USER="your-pushover-user" \ +*/ +func TestNotify(t *testing.T) { + flag.Parse() + + t.Run("Notify", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("TOKEN: %v", fToken), + }, "\n")) + + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ + Token: fToken, + User: fUser, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + res, err := notifier.Notify(context.Background(), mockSubject, mockMessage) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/notifier/providers/pushplus/pushplus.go b/internal/pkg/core/notifier/providers/pushplus/pushplus.go new file mode 100644 index 00000000..a0ef4c7f --- /dev/null +++ b/internal/pkg/core/notifier/providers/pushplus/pushplus.go @@ -0,0 +1,102 @@ +package pushplus + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log/slog" + "net/http" + + "github.com/usual2970/certimate/internal/pkg/core/notifier" +) + +type NotifierConfig struct { + // PushPlus Token。 + Token string `json:"token"` +} + +type NotifierProvider struct { + config *NotifierConfig + logger *slog.Logger + httpClient *http.Client +} + +var _ notifier.Notifier = (*NotifierProvider)(nil) + +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { + if config == nil { + panic("config is nil") + } + + return &NotifierProvider{ + config: config, + logger: slog.Default(), + httpClient: http.DefaultClient, + }, nil +} + +func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { + if logger == nil { + n.logger = slog.Default() + } else { + n.logger = logger + } + return n +} + +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { + // REF: https://pushplus.plus/doc/guide/api.html + reqBody := &struct { + Token string `json:"token"` + Title string `json:"title"` + Content string `json:"content"` + }{ + Token: n.config.Token, + Title: subject, + Content: message, + } + + body, err := json.Marshal(reqBody) + if err != nil { + return nil, fmt.Errorf("pushplus api error: failed to encode message body: %w", err) + } + + req, err := http.NewRequestWithContext( + ctx, + http.MethodPost, + "https://www.pushplus.plus/send", + bytes.NewReader(body), + ) + if err != nil { + return nil, fmt.Errorf("pushplus api error: failed to create new request: %w", err) + } + + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + resp, err := n.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("pushplus api error: failed to send request: %w", err) + } + defer resp.Body.Close() + + result, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("pushplus api error: failed to read response: %w", err) + } else if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("pushplus api error: unexpected status code: %d, resp: %s", resp.StatusCode, string(result)) + } + + var errorResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + } + if err := json.Unmarshal(result, &errorResponse); err != nil { + return nil, fmt.Errorf("pushplus api error: failed to decode response: %w", err) + } else if errorResponse.Code != 200 { + return nil, fmt.Errorf("pushplus api error: unexpected response code: %d, msg: %s", errorResponse.Code, errorResponse.Msg) + } + + return ¬ifier.NotifyResult{}, nil +} diff --git a/internal/pkg/core/notifier/providers/pushplus/pushplus_test.go b/internal/pkg/core/notifier/providers/pushplus/pushplus_test.go new file mode 100644 index 00000000..f504c168 --- /dev/null +++ b/internal/pkg/core/notifier/providers/pushplus/pushplus_test.go @@ -0,0 +1,56 @@ +package pushplus_test + +import ( + "context" + "flag" + "fmt" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus" +) + +const ( + mockSubject = "test_subject" + mockMessage = "test_message" +) + +var fToken string + +func init() { + argsPrefix := "CERTIMATE_NOTIFIER_PUSHPLUS_" + flag.StringVar(&fToken, argsPrefix+"TOKEN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./pushplus_test.go -args \ + --CERTIMATE_NOTIFIER_PUSHPLUS_TOKEN="your-pushplus-token" \ +*/ +func TestNotify(t *testing.T) { + flag.Parse() + + t.Run("Notify", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("TOKEN: %v", fToken), + }, "\n")) + + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ + Token: fToken, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + res, err := notifier.Notify(context.Background(), mockSubject, mockMessage) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/notifier/providers/serverchan/serverchan.go b/internal/pkg/core/notifier/providers/serverchan/serverchan.go index ac4d9fe6..89724b08 100644 --- a/internal/pkg/core/notifier/providers/serverchan/serverchan.go +++ b/internal/pkg/core/notifier/providers/serverchan/serverchan.go @@ -1,4 +1,4 @@ -package serverchan +package serverchan import ( "context" @@ -29,6 +29,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { return &NotifierProvider{ config: config, + logger: slog.Default(), }, nil } diff --git a/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go b/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go index 6f9c61a3..991b4050 100644 --- a/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go +++ b/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go @@ -1,4 +1,4 @@ -package serverchan_test +package serverchan_test import ( "context" diff --git a/internal/pkg/core/notifier/providers/telegram/telegram.go b/internal/pkg/core/notifier/providers/telegram/telegram.go index c3a2e973..218f7ee3 100644 --- a/internal/pkg/core/notifier/providers/telegram/telegram.go +++ b/internal/pkg/core/notifier/providers/telegram/telegram.go @@ -1,4 +1,4 @@ -package telegram +package telegram import ( "context" @@ -10,8 +10,8 @@ import ( ) type NotifierConfig struct { - // Telegram API Token。 - ApiToken string `json:"apiToken"` + // Telegram Bot API Token。 + BotToken string `json:"botToken"` // Telegram Chat ID。 ChatId int64 `json:"chatId"` } @@ -30,6 +30,7 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { return &NotifierProvider{ config: config, + logger: slog.Default(), }, nil } @@ -43,7 +44,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { } func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { - srv, err := telegram.New(n.config.ApiToken) + srv, err := telegram.New(n.config.BotToken) if err != nil { return nil, err } diff --git a/internal/pkg/core/notifier/providers/telegram/telegram_test.go b/internal/pkg/core/notifier/providers/telegram/telegram_test.go index 10e93547..e9a7d10b 100644 --- a/internal/pkg/core/notifier/providers/telegram/telegram_test.go +++ b/internal/pkg/core/notifier/providers/telegram/telegram_test.go @@ -1,4 +1,4 @@ -package telegram_test +package telegram_test import ( "context" @@ -45,7 +45,7 @@ func TestNotify(t *testing.T) { }, "\n")) notifier, err := provider.NewNotifier(&provider.NotifierConfig{ - ApiToken: fApiToken, + BotToken: fApiToken, ChatId: fChartId, }) if err != nil { diff --git a/internal/pkg/core/notifier/providers/webhook/webhook.go b/internal/pkg/core/notifier/providers/webhook/webhook.go index e11e5487..0e7caaa5 100644 --- a/internal/pkg/core/notifier/providers/webhook/webhook.go +++ b/internal/pkg/core/notifier/providers/webhook/webhook.go @@ -1,26 +1,39 @@ -package webhook +package webhook import ( "context" "crypto/tls" + "encoding/json" + "fmt" "log/slog" "net/http" + "net/url" + "strings" + "time" - webhook "github.com/nikoksr/notify/service/http" + "github.com/go-resty/resty/v2" "github.com/usual2970/certimate/internal/pkg/core/notifier" ) type NotifierConfig struct { // Webhook URL。 - Url string `json:"url"` + WebhookUrl string `json:"webhookUrl"` + // Webhook 回调数据(application/json 或 application/x-www-form-urlencoded 格式)。 + WebhookData string `json:"webhookData,omitempty"` + // 请求谓词。 + // 零值时默认为 "POST"。 + Method string `json:"method,omitempty"` + // 请求标头。 + Headers map[string]string `json:"headers,omitempty"` // 是否允许不安全的连接。 AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type NotifierProvider struct { - config *NotifierConfig - logger *slog.Logger + config *NotifierConfig + logger *slog.Logger + httpClient *resty.Client } var _ notifier.Notifier = (*NotifierProvider)(nil) @@ -30,8 +43,18 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { panic("config is nil") } + client := resty.New(). + SetTimeout(30 * time.Second). + SetRetryCount(3). + SetRetryWaitTime(5 * time.Second) + if config.AllowInsecureConnections { + client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) + } + return &NotifierProvider{ - config: config, + config: config, + logger: slog.Default(), + httpClient: client, }, nil } @@ -45,20 +68,120 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { } func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { - srv := webhook.New() - srv.AddReceiversURLs(n.config.Url) - - if n.config.AllowInsecureConnections { - tlsConfig := &tls.Config{InsecureSkipVerify: true} - transport := &http.Transport{TLSClientConfig: tlsConfig} - client := &http.Client{Transport: transport} - srv.WithClient(client) - } - - err = srv.Send(ctx, subject, message) + // 处理 Webhook URL + webhookUrl, err := url.Parse(n.config.WebhookUrl) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to parse webhook url: %w", err) + } else if webhookUrl.Scheme != "http" && webhookUrl.Scheme != "https" { + return nil, fmt.Errorf("unsupported webhook url scheme '%s'", webhookUrl.Scheme) } + // 处理 Webhook 请求谓词 + webhookMethod := strings.ToUpper(n.config.Method) + if webhookMethod == "" { + webhookMethod = http.MethodPost + } else if webhookMethod != http.MethodGet && + webhookMethod != http.MethodPost && + webhookMethod != http.MethodPut && + webhookMethod != http.MethodPatch && + webhookMethod != http.MethodDelete { + return nil, fmt.Errorf("unsupported webhook request method '%s'", webhookMethod) + } + + // 处理 Webhook 请求标头 + webhookHeaders := make(http.Header) + for k, v := range n.config.Headers { + webhookHeaders.Set(k, v) + } + + // 处理 Webhook 请求内容类型 + const CONTENT_TYPE_JSON = "application/json" + const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded" + const CONTENT_TYPE_MULTIPART = "multipart/form-data" + webhookContentType := webhookHeaders.Get("Content-Type") + if webhookContentType == "" { + webhookContentType = CONTENT_TYPE_JSON + webhookHeaders.Set("Content-Type", CONTENT_TYPE_JSON) + } else if strings.HasPrefix(webhookContentType, CONTENT_TYPE_JSON) && + strings.HasPrefix(webhookContentType, CONTENT_TYPE_FORM) && + strings.HasPrefix(webhookContentType, CONTENT_TYPE_MULTIPART) { + return nil, fmt.Errorf("unsupported webhook content type '%s'", webhookContentType) + } + + // 处理 Webhook 请求数据 + var webhookData interface{} + if n.config.WebhookData == "" { + webhookData = map[string]string{ + "subject": subject, + "message": message, + } + } else { + err = json.Unmarshal([]byte(n.config.WebhookData), &webhookData) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err) + } + + replaceJsonValueRecursively(webhookData, "${SUBJECT}", subject) + replaceJsonValueRecursively(webhookData, "${MESSAGE}", message) + + if webhookMethod == http.MethodGet || webhookContentType == CONTENT_TYPE_FORM || webhookContentType == CONTENT_TYPE_MULTIPART { + temp := make(map[string]string) + jsonb, err := json.Marshal(webhookData) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err) + } else if err := json.Unmarshal(jsonb, &temp); err != nil { + return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err) + } else { + webhookData = temp + } + } + } + + // 生成请求 + // 其中 GET 请求需转换为查询参数 + req := n.httpClient.R(). + SetContext(ctx). + SetHeaderMultiValues(webhookHeaders) + req.URL = webhookUrl.String() + req.Method = webhookMethod + if webhookMethod == http.MethodGet { + req.SetQueryParams(webhookData.(map[string]string)) + } else { + switch webhookContentType { + case CONTENT_TYPE_JSON: + req.SetBody(webhookData) + case CONTENT_TYPE_FORM: + req.SetFormData(webhookData.(map[string]string)) + case CONTENT_TYPE_MULTIPART: + req.SetMultipartFormData(webhookData.(map[string]string)) + } + } + + // 发送请求 + resp, err := req.Send() + if err != nil { + return nil, fmt.Errorf("failed to send webhook request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("unexpected webhook response status code: %d", resp.StatusCode()) + } + + n.logger.Debug("webhook responded", slog.Any("response", resp.String())) + return ¬ifier.NotifyResult{}, nil } + +func replaceJsonValueRecursively(data interface{}, oldStr, newStr string) interface{} { + switch v := data.(type) { + case map[string]any: + for k, val := range v { + v[k] = replaceJsonValueRecursively(val, oldStr, newStr) + } + case []any: + for i, val := range v { + v[i] = replaceJsonValueRecursively(val, oldStr, newStr) + } + case string: + return strings.ReplaceAll(v, oldStr, newStr) + } + return data +} diff --git a/internal/pkg/core/notifier/providers/webhook/webhook_test.go b/internal/pkg/core/notifier/providers/webhook/webhook_test.go index 8210358b..c416b3c9 100644 --- a/internal/pkg/core/notifier/providers/webhook/webhook_test.go +++ b/internal/pkg/core/notifier/providers/webhook/webhook_test.go @@ -1,4 +1,4 @@ -package webhook_test +package webhook_test import ( "context" @@ -15,19 +15,24 @@ const ( mockMessage = "test_message" ) -var fUrl string +var ( + fWebhookUrl string + fWebhookContentType string +) func init() { argsPrefix := "CERTIMATE_NOTIFIER_WEBHOOK_" - flag.StringVar(&fUrl, argsPrefix+"URL", "", "") + flag.StringVar(&fWebhookUrl, argsPrefix+"URL", "", "") + flag.StringVar(&fWebhookContentType, argsPrefix+"CONTENTTYPE", "application/json", "") } /* Shell command to run this test: go test -v ./webhook_test.go -args \ - --CERTIMATE_NOTIFIER_WEBHOOK_URL="https://example.com/your-webhook-url" + --CERTIMATE_NOTIFIER_WEBHOOK_URL="https://example.com/your-webhook-url" \ + --CERTIMATE_NOTIFIER_WEBHOOK_CONTENTTYPE="application/json" */ func TestNotify(t *testing.T) { flag.Parse() @@ -35,11 +40,15 @@ func TestNotify(t *testing.T) { t.Run("Notify", func(t *testing.T) { t.Log(strings.Join([]string{ "args:", - fmt.Sprintf("URL: %v", fUrl), + fmt.Sprintf("URL: %v", fWebhookUrl), }, "\n")) notifier, err := provider.NewNotifier(&provider.NotifierConfig{ - Url: fUrl, + WebhookUrl: fWebhookUrl, + Method: "POST", + Headers: map[string]string{ + "Content-Type": fWebhookContentType, + }, AllowInsecureConnections: true, }) if err != nil { diff --git a/internal/pkg/core/notifier/providers/wecom/wecom.go b/internal/pkg/core/notifier/providers/wecom/wecom.go index ef76e97f..413f0d8d 100644 --- a/internal/pkg/core/notifier/providers/wecom/wecom.go +++ b/internal/pkg/core/notifier/providers/wecom/wecom.go @@ -1,4 +1,4 @@ -package serverchan +package serverchan import ( "context" diff --git a/internal/pkg/core/notifier/providers/wecom/wecom_test.go b/internal/pkg/core/notifier/providers/wecom/wecom_test.go index b823adea..01646121 100644 --- a/internal/pkg/core/notifier/providers/wecom/wecom_test.go +++ b/internal/pkg/core/notifier/providers/wecom/wecom_test.go @@ -1,4 +1,4 @@ -package serverchan_test +package serverchan_test import ( "context" diff --git a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go index ee00c06a..e5a0b0ba 100644 --- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go +++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go @@ -1,4 +1,4 @@ -package onepanelssl +package onepanelssl import ( "context" @@ -9,10 +9,8 @@ import ( "strings" "time" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk" + opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel" ) type UploaderConfig struct { @@ -37,7 +35,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.ApiUrl, config.ApiKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -56,9 +54,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 遍历证书列表,避免重复上传 - if res, err := u.getExistCert(ctx, certPem, privkeyPem); err != nil { + if res, err := u.getCertIfExists(ctx, certPEM, privkeyPEM); err != nil { return nil, err } else if res != nil { u.logger.Info("ssl certificate already exists") @@ -72,17 +70,17 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{ Type: "paste", Description: certName, - Certificate: certPem, - PrivateKey: privkeyPem, + Certificate: certPEM, + PrivateKey: privkeyPEM, } uploadWebsiteSSLResp, err := u.sdkClient.UploadWebsiteSSL(uploadWebsiteSSLReq) u.logger.Debug("sdk request '1panel.UploadWebsiteSSL'", slog.Any("request", uploadWebsiteSSLReq), slog.Any("response", uploadWebsiteSSLResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.UploadWebsiteSSL'") + return nil, fmt.Errorf("failed to execute sdk request '1panel.UploadWebsiteSSL': %w", err) } // 遍历证书列表,获取刚刚上传证书 ID - if res, err := u.getExistCert(ctx, certPem, privkeyPem); err != nil { + if res, err := u.getCertIfExists(ctx, certPEM, privkeyPEM); err != nil { return nil, err } else if res == nil { return nil, fmt.Errorf("no ssl certificate found, may be upload failed (code: %d, message: %s)", uploadWebsiteSSLResp.GetCode(), uploadWebsiteSSLResp.GetMessage()) @@ -91,10 +89,16 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe } } -func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { searchWebsiteSSLPageNumber := int32(1) searchWebsiteSSLPageSize := int32(100) for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + searchWebsiteSSLReq := &opsdk.SearchWebsiteSSLRequest{ Page: searchWebsiteSSLPageNumber, PageSize: searchWebsiteSSLPageSize, @@ -102,12 +106,12 @@ func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string, pri searchWebsiteSSLResp, err := u.sdkClient.SearchWebsiteSSL(searchWebsiteSSLReq) u.logger.Debug("sdk request '1panel.SearchWebsiteSSL'", slog.Any("request", searchWebsiteSSLReq), slog.Any("response", searchWebsiteSSLResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.SearchWebsiteSSL'") + return nil, fmt.Errorf("failed to execute sdk request '1panel.SearchWebsiteSSL': %w", err) } for _, sslItem := range searchWebsiteSSLResp.Data.Items { - if strings.TrimSpace(sslItem.PEM) == strings.TrimSpace(certPem) && - strings.TrimSpace(sslItem.PrivateKey) == strings.TrimSpace(privkeyPem) { + if strings.TrimSpace(sslItem.PEM) == strings.TrimSpace(certPEM) && + strings.TrimSpace(sslItem.PrivateKey) == strings.TrimSpace(privkeyPEM) { // 如果已存在相同证书,直接返回 return &uploader.UploadResult{ CertId: fmt.Sprintf("%d", sslItem.ID), diff --git a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go index 5f146dd1..257030f5 100644 --- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go +++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go @@ -1,4 +1,4 @@ -package onepanelssl_test +package onepanelssl_test import ( "context" diff --git a/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go b/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go index 9f3e9212..9d7be223 100644 --- a/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go +++ b/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go @@ -1,4 +1,4 @@ -package aliyuncas +package aliyuncas import ( "context" @@ -10,10 +10,9 @@ import ( alicas "github.com/alibabacloud-go/cas-20200407/v3/client" aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -40,7 +39,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -59,9 +58,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -72,6 +71,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe listUserCertificateOrderPage := int64(1) listUserCertificateOrderLimit := int64(50) for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + listUserCertificateOrderReq := &alicas.ListUserCertificateOrderRequest{ CurrentPage: tea.Int64(listUserCertificateOrderPage), ShowSize: tea.Int64(listUserCertificateOrderLimit), @@ -80,7 +85,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe listUserCertificateOrderResp, err := u.sdkClient.ListUserCertificateOrder(listUserCertificateOrderReq) u.logger.Debug("sdk request 'cas.ListUserCertificateOrder'", slog.Any("request", listUserCertificateOrderReq), slog.Any("response", listUserCertificateOrderResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cas.ListUserCertificateOrder'") + return nil, fmt.Errorf("failed to execute sdk request 'cas.ListUserCertificateOrder': %w", err) } if listUserCertificateOrderResp.Body.CertificateOrderList != nil { @@ -95,11 +100,11 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe getUserCertificateDetailResp, err := u.sdkClient.GetUserCertificateDetail(getUserCertificateDetailReq) u.logger.Debug("sdk request 'cas.GetUserCertificateDetail'", slog.Any("request", getUserCertificateDetailReq), slog.Any("response", getUserCertificateDetailResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cas.GetUserCertificateDetail'") + return nil, fmt.Errorf("failed to execute sdk request 'cas.GetUserCertificateDetail': %w", err) } var isSameCert bool - if *getUserCertificateDetailResp.Body.Cert == certPem { + if *getUserCertificateDetailResp.Body.Cert == certPEM { isSameCert = true } else { oldCertX509, err := certutil.ParseCertificateFromPEM(*getUserCertificateDetailResp.Body.Cert) @@ -116,6 +121,10 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe return &uploader.UploadResult{ CertId: fmt.Sprintf("%d", tea.Int64Value(certDetail.CertificateId)), CertName: *certDetail.Name, + ExtendedData: map[string]any{ + "instanceId": tea.StringValue(getUserCertificateDetailResp.Body.InstanceId), + "certIdentifier": tea.StringValue(getUserCertificateDetailResp.Body.CertIdentifier), + }, }, nil } } @@ -129,26 +138,40 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe } // 生成新证书名(需符合阿里云命名规则) - var certId, certName string - certName = fmt.Sprintf("certimate_%d", time.Now().UnixMilli()) + certName := fmt.Sprintf("certimate_%d", time.Now().UnixMilli()) // 上传新证书 // REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-uploadusercertificate uploadUserCertificateReq := &alicas.UploadUserCertificateRequest{ Name: tea.String(certName), - Cert: tea.String(certPem), - Key: tea.String(privkeyPem), + Cert: tea.String(certPEM), + Key: tea.String(privkeyPEM), } uploadUserCertificateResp, err := u.sdkClient.UploadUserCertificate(uploadUserCertificateReq) u.logger.Debug("sdk request 'cas.UploadUserCertificate'", slog.Any("request", uploadUserCertificateReq), slog.Any("response", uploadUserCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cas.UploadUserCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'cas.UploadUserCertificate': %w", err) + } + + // 获取证书详情 + // REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-getusercertificatedetail + getUserCertificateDetailReq := &alicas.GetUserCertificateDetailRequest{ + CertId: uploadUserCertificateResp.Body.CertId, + CertFilter: tea.Bool(true), + } + getUserCertificateDetailResp, err := u.sdkClient.GetUserCertificateDetail(getUserCertificateDetailReq) + u.logger.Debug("sdk request 'cas.GetUserCertificateDetail'", slog.Any("request", getUserCertificateDetailReq), slog.Any("response", getUserCertificateDetailResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cas.GetUserCertificateDetail': %w", err) } - certId = fmt.Sprintf("%d", tea.Int64Value(uploadUserCertificateResp.Body.CertId)) return &uploader.UploadResult{ - CertId: certId, + CertId: fmt.Sprintf("%d", tea.Int64Value(getUserCertificateDetailResp.Body.Id)), CertName: certName, + ExtendedData: map[string]any{ + "instanceId": tea.StringValue(getUserCertificateDetailResp.Body.InstanceId), + "certIdentifier": tea.StringValue(getUserCertificateDetailResp.Body.CertIdentifier), + }, }, nil } diff --git a/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go b/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go index 59607ade..cc1544c1 100644 --- a/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go +++ b/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go @@ -1,4 +1,4 @@ -package aliyunslb +package aliyunslb import ( "context" @@ -13,10 +13,9 @@ import ( aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" alislb "github.com/alibabacloud-go/slb-20140515/v4/client" "github.com/alibabacloud-go/tea/tea" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -43,7 +42,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -62,9 +61,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -77,7 +76,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe describeServerCertificatesResp, err := u.sdkClient.DescribeServerCertificates(describeServerCertificatesReq) u.logger.Debug("sdk request 'slb.DescribeServerCertificates'", slog.Any("request", describeServerCertificatesReq), slog.Any("response", describeServerCertificatesResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeServerCertificates'") + return nil, fmt.Errorf("failed to execute sdk request 'slb.DescribeServerCertificates': %w", err) } if describeServerCertificatesResp.Body.ServerCertificates != nil && describeServerCertificatesResp.Body.ServerCertificates.ServerCertificate != nil { @@ -105,21 +104,21 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // 去除证书和私钥内容中的空白行,以符合阿里云 API 要求 // REF: https://github.com/usual2970/certimate/issues/326 re := regexp.MustCompile(`(?m)^\s*$\n?`) - certPem = strings.TrimSpace(re.ReplaceAllString(certPem, "")) - privkeyPem = strings.TrimSpace(re.ReplaceAllString(privkeyPem, "")) + certPEM = strings.TrimSpace(re.ReplaceAllString(certPEM, "")) + privkeyPEM = strings.TrimSpace(re.ReplaceAllString(privkeyPEM, "")) // 上传新证书 // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-uploadservercertificate uploadServerCertificateReq := &alislb.UploadServerCertificateRequest{ RegionId: tea.String(u.config.Region), ServerCertificateName: tea.String(certName), - ServerCertificate: tea.String(certPem), - PrivateKey: tea.String(privkeyPem), + ServerCertificate: tea.String(certPEM), + PrivateKey: tea.String(privkeyPEM), } uploadServerCertificateResp, err := u.sdkClient.UploadServerCertificate(uploadServerCertificateReq) u.logger.Debug("sdk request 'slb.UploadServerCertificate'", slog.Any("request", uploadServerCertificateReq), slog.Any("response", uploadServerCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'slb.UploadServerCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'slb.UploadServerCertificate': %w", err) } certId = *uploadServerCertificateResp.Body.ServerCertificateId diff --git a/internal/pkg/core/uploader/providers/aws-acm/aws_acm.go b/internal/pkg/core/uploader/providers/aws-acm/aws_acm.go index f0a3ccb5..f808083c 100644 --- a/internal/pkg/core/uploader/providers/aws-acm/aws_acm.go +++ b/internal/pkg/core/uploader/providers/aws-acm/aws_acm.go @@ -1,18 +1,18 @@ -package awsacm +package awsacm import ( "context" + "fmt" "log/slog" aws "github.com/aws/aws-sdk-go-v2/aws" awscfg "github.com/aws/aws-sdk-go-v2/config" awscred "github.com/aws/aws-sdk-go-v2/credentials" awsacm "github.com/aws/aws-sdk-go-v2/service/acm" - xerrors "github.com/pkg/errors" "golang.org/x/exp/slices" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -39,7 +39,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -58,22 +58,28 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } // 生成 AWS 业务参数 - scertPem, _ := certutil.ConvertCertificateToPEM(certX509) - bcertPem := certPem + scertPEM, _ := certutil.ConvertCertificateToPEM(certX509) + bcertPEM := certPEM // 获取证书列表,避免重复上传 // REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListCertificates.html - listCertificatesNextToken := new(string) + var listCertificatesNextToken *string = nil listCertificatesMaxItems := int32(1000) for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + listCertificatesReq := &awsacm.ListCertificatesInput{ NextToken: listCertificatesNextToken, MaxItems: aws.Int32(listCertificatesMaxItems), @@ -81,7 +87,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe listCertificatesResp, err := u.sdkClient.ListCertificates(context.TODO(), listCertificatesReq) u.logger.Debug("sdk request 'acm.ListCertificates'", slog.Any("request", listCertificatesReq), slog.Any("response", listCertificatesResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'acm.ListCertificates'") + return nil, fmt.Errorf("failed to execute sdk request 'acm.ListCertificates': %w", err) } for _, certSummary := range listCertificatesResp.CertificateSummaryList { @@ -105,14 +111,14 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe } getCertificateResp, err := u.sdkClient.GetCertificate(context.TODO(), getCertificateReq) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'acm.GetCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'acm.GetCertificate': %w", err) } else { - oldCertPem := aws.ToString(getCertificateResp.CertificateChain) - if oldCertPem == "" { - oldCertPem = aws.ToString(getCertificateResp.Certificate) + oldCertPEM := aws.ToString(getCertificateResp.CertificateChain) + if oldCertPEM == "" { + oldCertPEM = aws.ToString(getCertificateResp.Certificate) } - oldCertX509, err := certutil.ParseCertificateFromPEM(oldCertPem) + oldCertX509, err := certutil.ParseCertificateFromPEM(oldCertPEM) if err != nil { continue } @@ -139,14 +145,14 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // 导入证书 // REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ImportCertificate.html importCertificateReq := &awsacm.ImportCertificateInput{ - Certificate: ([]byte)(scertPem), - CertificateChain: ([]byte)(bcertPem), - PrivateKey: ([]byte)(privkeyPem), + Certificate: ([]byte)(scertPEM), + CertificateChain: ([]byte)(bcertPEM), + PrivateKey: ([]byte)(privkeyPEM), } importCertificateResp, err := u.sdkClient.ImportCertificate(context.TODO(), importCertificateReq) u.logger.Debug("sdk request 'acm.ImportCertificate'", slog.Any("request", importCertificateReq), slog.Any("response", importCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'acm.ImportCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'acm.ImportCertificate': %w", err) } return &uploader.UploadResult{ diff --git a/internal/pkg/core/uploader/providers/azure-keyvault/azure_keyvault.go b/internal/pkg/core/uploader/providers/azure-keyvault/azure_keyvault.go index 5f6f998a..5ac68d69 100644 --- a/internal/pkg/core/uploader/providers/azure-keyvault/azure_keyvault.go +++ b/internal/pkg/core/uploader/providers/azure-keyvault/azure_keyvault.go @@ -1,8 +1,9 @@ -package azurekeyvault +package azurekeyvault import ( "context" "crypto/x509" + "encoding/base64" "fmt" "log/slog" "time" @@ -10,12 +11,11 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates" - xerrors "github.com/pkg/errors" + "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" - azcommon "github.com/usual2970/certimate/internal/pkg/vendors/azure-sdk/common" + azcommon "github.com/usual2970/certimate/internal/pkg/sdk3rd/azure/common" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -46,7 +46,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.TenantId, config.ClientId, config.ClientSecret, config.CloudName, config.KeyVaultName) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -65,9 +65,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -80,44 +80,44 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // 获取证书列表,避免重复上传 // REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/get-certificates/get-certificates - listCertificatesPager := u.sdkClient.NewListCertificatesPager(nil) + listCertificatesPager := u.sdkClient.NewListCertificatePropertiesPager(nil) for listCertificatesPager.More() { page, err := listCertificatesPager.NextPage(context.TODO()) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'keyvault.GetCertificates'") + return nil, fmt.Errorf("failed to execute sdk request 'keyvault.GetCertificates': %w", err) } - for _, certItem := range page.Value { + for _, certProp := range page.Value { // 先对比证书有效期 - if certItem.Attributes == nil { + if certProp.Attributes == nil { continue } - if certItem.Attributes.NotBefore == nil || !certItem.Attributes.NotBefore.Equal(certX509.NotBefore) { + if certProp.Attributes.NotBefore == nil || !certProp.Attributes.NotBefore.Equal(certX509.NotBefore) { continue } - if certItem.Attributes.Expires == nil || !certItem.Attributes.Expires.Equal(certX509.NotAfter) { + if certProp.Attributes.Expires == nil || !certProp.Attributes.Expires.Equal(certX509.NotAfter) { continue } // 再对比 Tag 中的通用名称 - if v, ok := certItem.Tags[TAG_CERTCN]; !ok || v == nil { + if v, ok := certProp.Tags[TAG_CERTCN]; !ok || v == nil { continue } else if *v != certCN { continue } // 再对比 Tag 中的序列号 - if v, ok := certItem.Tags[TAG_CERTSN]; !ok || v == nil { + if v, ok := certProp.Tags[TAG_CERTSN]; !ok || v == nil { continue } else if *v != certSN { continue } // 最后对比证书内容 - getCertificateResp, err := u.sdkClient.GetCertificate(context.TODO(), certItem.ID.Name(), certItem.ID.Version(), nil) - u.logger.Debug("sdk request 'keyvault.GetCertificate'", slog.String("request.certificateName", certItem.ID.Name()), slog.String("request.certificateVersion", certItem.ID.Version()), slog.Any("response", getCertificateResp)) + getCertificateResp, err := u.sdkClient.GetCertificate(context.TODO(), certProp.ID.Name(), certProp.ID.Version(), nil) + u.logger.Debug("sdk request 'keyvault.GetCertificate'", slog.String("request.certificateName", certProp.ID.Name()), slog.String("request.certificateVersion", certProp.ID.Version()), slog.Any("response", getCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'keyvault.GetCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'keyvault.GetCertificate': %w", err) } else { oldCertX509, err := x509.ParseCertificate(getCertificateResp.CER) if err != nil { @@ -132,8 +132,8 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // 如果以上信息都一致,则视为已存在相同证书,直接返回 u.logger.Info("ssl certificate already exists") return &uploader.UploadResult{ - CertId: string(*certItem.ID), - CertName: certItem.ID.Name(), + CertId: string(*certProp.ID), + CertName: certProp.ID.Name(), }, nil } } @@ -141,13 +141,21 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // 生成新证书名(需符合 Azure 命名规则) certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) + // Azure Key Vault 不支持导入带有 Certificiate Chain 的 PEM 证书。 + // Issue Link: https://github.com/Azure/azure-cli/issues/19017 + // 暂时的解决方法是,将 PEM 证书转换成 PFX 格式,然后再导入。 + certPFX, err := certutil.TransformCertificateFromPEMToPFX(certPEM, privkeyPEM, "") + if err != nil { + return nil, fmt.Errorf("failed to transform certificate from PEM to PFX: %w", err) + } + // 导入证书 // REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/import-certificate/import-certificate importCertificateParams := azcertificates.ImportCertificateParameters{ - Base64EncodedCertificate: to.Ptr(certPem), + Base64EncodedCertificate: to.Ptr(base64.StdEncoding.EncodeToString(certPFX)), CertificatePolicy: &azcertificates.CertificatePolicy{ SecretProperties: &azcertificates.SecretProperties{ - ContentType: to.Ptr("application/x-pem-file"), + ContentType: to.Ptr("application/x-pkcs12"), }, }, Tags: map[string]*string{ @@ -158,7 +166,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe importCertificateResp, err := u.sdkClient.ImportCertificate(context.TODO(), certName, importCertificateParams, nil) u.logger.Debug("sdk request 'keyvault.ImportCertificate'", slog.String("request.certificateName", certName), slog.Any("request.parameters", importCertificateParams), slog.Any("response", importCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'keyvault.ImportCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'keyvault.ImportCertificate': %w", err) } return &uploader.UploadResult{ diff --git a/internal/pkg/core/uploader/providers/azure-keyvault/azure_keyvault_test.go b/internal/pkg/core/uploader/providers/azure-keyvault/azure_keyvault_test.go new file mode 100644 index 00000000..492f7e5c --- /dev/null +++ b/internal/pkg/core/uploader/providers/azure-keyvault/azure_keyvault_test.go @@ -0,0 +1,87 @@ +package azurekeyvault_test + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/azure-keyvault" +) + +var ( + fInputCertPath string + fInputKeyPath string + fTenantId string + fClientId string + fClientSecret string + fCloudName string + fKeyVaultName string +) + +func init() { + argsPrefix := "CERTIMATE_UPLOADER_AZUREKEYVAULT_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fTenantId, argsPrefix+"TENANTID", "", "") + flag.StringVar(&fClientId, argsPrefix+"CLIENTID", "", "") + flag.StringVar(&fClientSecret, argsPrefix+"CLIENTSECRET", "", "") + flag.StringVar(&fCloudName, argsPrefix+"CLOUDNAME", "", "") + flag.StringVar(&fKeyVaultName, argsPrefix+"KEYVAULTNAME", "", "") +} + +/* +Shell command to run this test: + + go test -v ./azure_keyvault_test.go -args \ + --CERTIMATE_UPLOADER_AZUREKEYVAULT_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_UPLOADER_AZUREKEYVAULT_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_UPLOADER_AZUREKEYVAULT_TENANTID="your-tenant-id" \ + --CERTIMATE_UPLOADER_AZUREKEYVAULT_CLIENTID="your-app-registration-client-id" \ + --CERTIMATE_UPLOADER_AZUREKEYVAULT_CLIENTSECRET="your-app-registration-client-secret" \ + --CERTIMATE_UPLOADER_AZUREKEYVAULT_CLOUDNAME="china" \ + --CERTIMATE_UPLOADER_AZUREKEYVAULT_KEYVAULTNAME="your-keyvault-name" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("TENANTID: %v", fTenantId), + fmt.Sprintf("CLIENTID: %v", fClientId), + fmt.Sprintf("CLIENTSECRET: %v", fClientSecret), + fmt.Sprintf("CLOUDNAME: %v", fCloudName), + fmt.Sprintf("KEYVAULTNAME: %v", fKeyVaultName), + }, "\n")) + + uploader, err := provider.NewUploader(&provider.UploaderConfig{ + TenantId: fTenantId, + ClientId: fClientId, + ClientSecret: fClientSecret, + CloudName: fCloudName, + KeyVaultName: fKeyVaultName, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := uploader.Upload(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + sres, _ := json.Marshal(res) + t.Logf("ok: %s", string(sres)) + }) +} diff --git a/internal/pkg/core/uploader/providers/baiducloud-cert/baiducloud_cert.go b/internal/pkg/core/uploader/providers/baiducloud-cert/baiducloud_cert.go index 1f311f87..727aa03f 100644 --- a/internal/pkg/core/uploader/providers/baiducloud-cert/baiducloud_cert.go +++ b/internal/pkg/core/uploader/providers/baiducloud-cert/baiducloud_cert.go @@ -1,4 +1,4 @@ -package baiducloudcert +package baiducloudcert import ( "context" @@ -7,11 +7,9 @@ import ( "strings" "time" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" - bdsdk "github.com/usual2970/certimate/internal/pkg/vendors/baiducloud-sdk/cert" + bdsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/baiducloud/cert" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -36,7 +34,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -55,9 +53,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -67,7 +65,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe listCertDetail, err := u.sdkClient.ListCertDetail() u.logger.Debug("sdk request 'cert.ListCertDetail'", slog.Any("response", listCertDetail)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cert.ListCertDetail'") + return nil, fmt.Errorf("failed to execute sdk request 'cert.ListCertDetail': %w", err) } else { for _, certDetail := range listCertDetail.Certs { // 先对比证书通用名称 @@ -91,7 +89,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe getCertDetailResp, err := u.sdkClient.GetCertRawData(certDetail.CertId) u.logger.Debug("sdk request 'cert.GetCertRawData'", slog.Any("certId", certDetail.CertId), slog.Any("response", getCertDetailResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cert.GetCertRawData'") + return nil, fmt.Errorf("failed to execute sdk request 'cert.GetCertRawData': %w", err) } else { oldCertX509, err := certutil.ParseCertificateFromPEM(getCertDetailResp.CertServerData) if err != nil { @@ -115,12 +113,12 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // REF: https://cloud.baidu.com/doc/Reference/s/Gjwvz27xu#31-%E5%88%9B%E5%BB%BA%E8%AF%81%E4%B9%A6 createCertReq := &bdsdk.CreateCertArgs{} createCertReq.CertName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) - createCertReq.CertServerData = certPem - createCertReq.CertPrivateData = privkeyPem + createCertReq.CertServerData = certPEM + createCertReq.CertPrivateData = privkeyPEM createCertResp, err := u.sdkClient.CreateCert(createCertReq) u.logger.Debug("sdk request 'cert.CreateCert'", slog.Any("request", createCertReq), slog.Any("response", createCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cert.CreateCert'") + return nil, fmt.Errorf("failed to execute sdk request 'cert.CreateCert': %w", err) } return &uploader.UploadResult{ diff --git a/internal/pkg/core/uploader/providers/baiducloud-cert/baiducloud_cert_test.go b/internal/pkg/core/uploader/providers/baiducloud-cert/baiducloud_cert_test.go index 55de1576..80b2a7ca 100644 --- a/internal/pkg/core/uploader/providers/baiducloud-cert/baiducloud_cert_test.go +++ b/internal/pkg/core/uploader/providers/baiducloud-cert/baiducloud_cert_test.go @@ -1,4 +1,4 @@ -package baiducloudcert_test +package baiducloudcert_test import ( "context" diff --git a/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go b/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go index cdba9d4f..1235893c 100644 --- a/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go +++ b/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go @@ -11,10 +11,9 @@ import ( "time" bytepluscdn "github.com/byteplus-sdk/byteplus-sdk-golang/service/cdn" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -57,9 +56,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -75,10 +74,16 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe Source: bytepluscdn.GetStrPtr("cert_center"), } for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + listCertInfoResp, err := u.sdkClient.ListCertInfo(listCertInfoReq) u.logger.Debug("sdk request 'cdn.ListCertInfo'", slog.Any("request", listCertInfoReq), slog.Any("response", listCertInfoResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ListCertInfo'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.ListCertInfo': %w", err) } if listCertInfoResp.Result.CertInfo != nil { @@ -114,15 +119,15 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // 上传新证书 // REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-addcertificate addCertificateReq := &bytepluscdn.AddCertificateRequest{ - Certificate: certPem, - PrivateKey: privkeyPem, + Certificate: certPEM, + PrivateKey: privkeyPEM, Source: bytepluscdn.GetStrPtr("cert_center"), Desc: bytepluscdn.GetStrPtr(certName), } addCertificateResp, err := u.sdkClient.AddCertificate(addCertificateReq) u.logger.Debug("sdk request 'cdn.AddCertificate'", slog.Any("request", addCertificateReq), slog.Any("response", addCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.AddCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.AddCertificate': %w", err) } certId = addCertificateResp.Result.CertId diff --git a/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go b/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go index 420f93bc..ca98fc90 100644 --- a/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go +++ b/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go @@ -1,4 +1,4 @@ -package dogecloud +package dogecloud import ( "context" @@ -6,10 +6,8 @@ import ( "log/slog" "time" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - dogesdk "github.com/usual2970/certimate/internal/pkg/vendors/dogecloud-sdk" + dogesdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/dogecloud" ) type UploaderConfig struct { @@ -34,7 +32,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKey, config.SecretKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -53,17 +51,17 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 生成新证书名(需符合多吉云命名规则) var certId, certName string certName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) // 上传新证书 // REF: https://docs.dogecloud.com/cdn/api-cert-upload - uploadSslCertResp, err := u.sdkClient.UploadCdnCert(certName, certPem, privkeyPem) + uploadSslCertResp, err := u.sdkClient.UploadCdnCert(certName, certPEM, privkeyPEM) u.logger.Debug("sdk request 'cdn.UploadCdnCert'", slog.Any("response", uploadSslCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.UploadCdnCert'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.UploadCdnCert': %w", err) } certId = fmt.Sprintf("%d", uploadSslCertResp.Data.Id) diff --git a/internal/pkg/core/uploader/providers/gcore-cdn/gcore_cdn.go b/internal/pkg/core/uploader/providers/gcore-cdn/gcore_cdn.go index 7f0a6580..c5dc5296 100644 --- a/internal/pkg/core/uploader/providers/gcore-cdn/gcore_cdn.go +++ b/internal/pkg/core/uploader/providers/gcore-cdn/gcore_cdn.go @@ -1,4 +1,4 @@ -package gcorecdn +package gcorecdn import ( "context" @@ -9,10 +9,9 @@ import ( gprovider "github.com/G-Core/gcorelabscdn-go/gcore/provider" gsslcerts "github.com/G-Core/gcorelabscdn-go/sslcerts" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - gcoresdk "github.com/usual2970/certimate/internal/pkg/vendors/gcore-sdk/common" + gcoresdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/gcore/common" ) type UploaderConfig struct { @@ -35,7 +34,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.ApiToken) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -54,7 +53,7 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 生成新证书名(需符合 Gcore 命名规则) var certId, certName string certName = fmt.Sprintf("certimate_%d", time.Now().UnixMilli()) @@ -63,15 +62,15 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // REF: https://api.gcore.com/docs/cdn#tag/CA-certificates/operation/ca_certificates-add createCertificateReq := &gsslcerts.CreateRequest{ Name: certName, - Cert: certPem, - PrivateKey: privkeyPem, + Cert: certPEM, + PrivateKey: privkeyPEM, Automated: false, ValidateRootCA: false, } createCertificateResp, err := u.sdkClient.Create(context.TODO(), createCertificateReq) u.logger.Debug("sdk request 'sslcerts.Create'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'sslcerts.Create'") + return nil, fmt.Errorf("failed to execute sdk request 'sslcerts.Create': %w", err) } certId = fmt.Sprintf("%d", createCertificateResp.ID) diff --git a/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go b/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go index 50d716a1..9369144e 100644 --- a/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go +++ b/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go @@ -1,4 +1,4 @@ -package huaweicloudelb +package huaweicloudelb import ( "context" @@ -15,11 +15,10 @@ import ( hciam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3" hciammodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model" hciamregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" - hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" ) type UploaderConfig struct { @@ -46,7 +45,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -65,9 +64,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -77,21 +76,27 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe listCertificatesLimit := int32(2000) var listCertificatesMarker *string = nil for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + listCertificatesReq := &hcelbmodel.ListCertificatesRequest{ - Limit: hwsdk.Int32Ptr(listCertificatesLimit), + Limit: typeutil.ToPtr(listCertificatesLimit), Marker: listCertificatesMarker, Type: &[]string{"server"}, } listCertificatesResp, err := u.sdkClient.ListCertificates(listCertificatesReq) u.logger.Debug("sdk request 'elb.ListCertificates'", slog.Any("request", listCertificatesReq), slog.Any("response", listCertificatesResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'elb.ListCertificates'") + return nil, fmt.Errorf("failed to execute sdk request 'elb.ListCertificates': %w", err) } if listCertificatesResp.Certificates != nil { for _, certDetail := range *listCertificatesResp.Certificates { var isSameCert bool - if certDetail.Certificate == certPem { + if certDetail.Certificate == certPEM { isSameCert = true } else { oldCertX509, err := certutil.ParseCertificateFromPEM(certDetail.Certificate) @@ -124,7 +129,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // REF: https://support.huaweicloud.com/api-iam/iam_06_0001.html projectId, err := getSdkProjectId(u.config.AccessKeyId, u.config.SecretAccessKey, u.config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to get SDK project id") + return nil, fmt.Errorf("failed to get SDK project id: %w", err) } // 生成新证书名(需符合华为云命名规则) @@ -136,17 +141,17 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe createCertificateReq := &hcelbmodel.CreateCertificateRequest{ Body: &hcelbmodel.CreateCertificateRequestBody{ Certificate: &hcelbmodel.CreateCertificateOption{ - ProjectId: hwsdk.StringPtr(projectId), - Name: hwsdk.StringPtr(certName), - Certificate: hwsdk.StringPtr(certPem), - PrivateKey: hwsdk.StringPtr(privkeyPem), + ProjectId: typeutil.ToPtr(projectId), + Name: typeutil.ToPtr(certName), + Certificate: typeutil.ToPtr(certPEM), + PrivateKey: typeutil.ToPtr(privkeyPEM), }, }, } createCertificateResp, err := u.sdkClient.CreateCertificate(createCertificateReq) u.logger.Debug("sdk request 'elb.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'elb.CreateCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'elb.CreateCertificate': %w", err) } certId = createCertificateResp.Certificate.Id diff --git a/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go b/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go index 94413993..f8435733 100644 --- a/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go +++ b/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go @@ -1,4 +1,4 @@ -package huaweicloudscm +package huaweicloudscm import ( "context" @@ -10,11 +10,10 @@ import ( hcscm "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/scm/v3" hcscmmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/scm/v3/model" hcscmregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/scm/v3/region" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" - hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" ) type UploaderConfig struct { @@ -41,7 +40,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -60,9 +59,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -73,16 +72,22 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe listCertificatesLimit := int32(50) listCertificatesOffset := int32(0) for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + listCertificatesReq := &hcscmmodel.ListCertificatesRequest{ - Limit: hwsdk.Int32Ptr(listCertificatesLimit), - Offset: hwsdk.Int32Ptr(listCertificatesOffset), - SortDir: hwsdk.StringPtr("DESC"), - SortKey: hwsdk.StringPtr("certExpiredTime"), + Limit: typeutil.ToPtr(listCertificatesLimit), + Offset: typeutil.ToPtr(listCertificatesOffset), + SortDir: typeutil.ToPtr("DESC"), + SortKey: typeutil.ToPtr("certExpiredTime"), } listCertificatesResp, err := u.sdkClient.ListCertificates(listCertificatesReq) u.logger.Debug("sdk request 'scm.ListCertificates'", slog.Any("request", listCertificatesReq), slog.Any("response", listCertificatesResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'scm.ListCertificates'") + return nil, fmt.Errorf("failed to execute sdk request 'scm.ListCertificates': %w", err) } if listCertificatesResp.Certificates != nil { @@ -96,11 +101,11 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe if exportCertificateResp != nil && exportCertificateResp.HttpStatusCode == 404 { continue } - return nil, xerrors.Wrap(err, "failed to execute sdk request 'scm.ExportCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'scm.ExportCertificate': %w", err) } var isSameCert bool - if *exportCertificateResp.Certificate == certPem { + if *exportCertificateResp.Certificate == certPEM { isSameCert = true } else { oldCertX509, err := certutil.ParseCertificateFromPEM(*exportCertificateResp.Certificate) @@ -138,14 +143,14 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe importCertificateReq := &hcscmmodel.ImportCertificateRequest{ Body: &hcscmmodel.ImportCertificateRequestBody{ Name: certName, - Certificate: certPem, - PrivateKey: privkeyPem, + Certificate: certPEM, + PrivateKey: privkeyPEM, }, } importCertificateResp, err := u.sdkClient.ImportCertificate(importCertificateReq) u.logger.Debug("sdk request 'scm.ImportCertificate'", slog.Any("request", importCertificateReq), slog.Any("response", importCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'scm.ImportCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'scm.ImportCertificate': %w", err) } certId = *importCertificateResp.CertificateId diff --git a/internal/pkg/core/uploader/providers/huaweicloud-waf/huaweicloud_waf.go b/internal/pkg/core/uploader/providers/huaweicloud-waf/huaweicloud_waf.go index 86d25426..d0c61775 100644 --- a/internal/pkg/core/uploader/providers/huaweicloud-waf/huaweicloud_waf.go +++ b/internal/pkg/core/uploader/providers/huaweicloud-waf/huaweicloud_waf.go @@ -1,4 +1,4 @@ -package huaweicloudwaf +package huaweicloudwaf import ( "context" @@ -15,11 +15,10 @@ import ( hcwaf "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1" hcwafmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/model" hcwafregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/region" - xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" - hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" ) type UploaderConfig struct { @@ -46,7 +45,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -65,9 +64,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -78,14 +77,20 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe listCertificatesPage := int32(1) listCertificatesPageSize := int32(100) for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + listCertificatesReq := &hcwafmodel.ListCertificatesRequest{ - Page: hwsdk.Int32Ptr(listCertificatesPage), - Pagesize: hwsdk.Int32Ptr(listCertificatesPageSize), + Page: typeutil.ToPtr(listCertificatesPage), + Pagesize: typeutil.ToPtr(listCertificatesPageSize), } listCertificatesResp, err := u.sdkClient.ListCertificates(listCertificatesReq) u.logger.Debug("sdk request 'waf.ShowCertificate'", slog.Any("request", listCertificatesReq), slog.Any("response", listCertificatesResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.ListCertificates'") + return nil, fmt.Errorf("failed to execute sdk request 'waf.ListCertificates': %w", err) } if listCertificatesResp.Items != nil { @@ -96,11 +101,11 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe showCertificateResp, err := u.sdkClient.ShowCertificate(showCertificateReq) u.logger.Debug("sdk request 'waf.ShowCertificate'", slog.Any("request", showCertificateReq), slog.Any("response", showCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.ShowCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'waf.ShowCertificate': %w", err) } var isSameCert bool - if *showCertificateResp.Content == certPem { + if *showCertificateResp.Content == certPEM { isSameCert = true } else { oldCertX509, err := certutil.ParseCertificateFromPEM(*showCertificateResp.Content) @@ -138,14 +143,14 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe createCertificateReq := &hcwafmodel.CreateCertificateRequest{ Body: &hcwafmodel.CreateCertificateRequestBody{ Name: certName, - Content: certPem, - Key: privkeyPem, + Content: certPEM, + Key: privkeyPEM, }, } createCertificateResp, err := u.sdkClient.CreateCertificate(createCertificateReq) u.logger.Debug("sdk request 'waf.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.CreateCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'waf.CreateCertificate': %w", err) } certId = *createCertificateResp.Id diff --git a/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl.go b/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl.go index 10f7c203..b26755a6 100644 --- a/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl.go +++ b/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl.go @@ -1,4 +1,4 @@ -package jdcloudssl +package jdcloudssl import ( "context" @@ -12,11 +12,10 @@ import ( jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core" jdsslapi "github.com/jdcloud-api/jdcloud-sdk-go/services/ssl/apis" jdsslclient "github.com/jdcloud-api/jdcloud-sdk-go/services/ssl/client" - xerrors "github.com/pkg/errors" "golang.org/x/exp/slices" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -41,7 +40,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -60,24 +59,30 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } // 格式化私钥内容,以便后续计算私钥摘要 - privkeyPem = strings.TrimSpace(privkeyPem) - privkeyPem = strings.ReplaceAll(privkeyPem, "\r", "") - privkeyPem = strings.ReplaceAll(privkeyPem, "\n", "\r\n") - privkeyPem = privkeyPem + "\r\n" + privkeyPEM = strings.TrimSpace(privkeyPEM) + privkeyPEM = strings.ReplaceAll(privkeyPEM, "\r", "") + privkeyPEM = strings.ReplaceAll(privkeyPEM, "\n", "\r\n") + privkeyPEM = privkeyPEM + "\r\n" // 遍历查看证书列表,避免重复上传 // REF: https://docs.jdcloud.com/cn/ssl-certificate/api/describecerts describeCertsPageNumber := 1 describeCertsPageSize := 10 for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + describeCertsReq := jdsslapi.NewDescribeCertsRequest() describeCertsReq.SetDomainName(certX509.Subject.CommonName) describeCertsReq.SetPageNumber(describeCertsPageNumber) @@ -85,7 +90,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe describeCertsResp, err := u.sdkClient.DescribeCerts(describeCertsReq) u.logger.Debug("sdk request 'ssl.DescribeCerts'", slog.Any("request", describeCertsReq), slog.Any("response", describeCertsResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.DescribeCerts'") + return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeCerts': %w", err) } for _, certDetail := range describeCertsResp.Result.CertListDetails { @@ -107,7 +112,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe } // 最后对比私钥摘要 - newKeyDigest := sha256.Sum256([]byte(privkeyPem)) + newKeyDigest := sha256.Sum256([]byte(privkeyPEM)) newKeyDigestHex := hex.EncodeToString(newKeyDigest[:]) if !strings.EqualFold(newKeyDigestHex, certDetail.Digest) { continue @@ -133,11 +138,11 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // 上传证书 // REF: https://docs.jdcloud.com/cn/ssl-certificate/api/uploadcert - uploadCertReq := jdsslapi.NewUploadCertRequest(certName, privkeyPem, certPem) + uploadCertReq := jdsslapi.NewUploadCertRequest(certName, privkeyPEM, certPEM) uploadCertResp, err := u.sdkClient.UploadCert(uploadCertReq) u.logger.Debug("sdk request 'ssl.UploadCertificate'", slog.Any("request", uploadCertReq), slog.Any("response", uploadCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.UploadCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'ssl.UploadCertificate': %w", err) } return &uploader.UploadResult{ diff --git a/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl_test.go b/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl_test.go index ec02ce49..273310e0 100644 --- a/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl_test.go +++ b/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl_test.go @@ -1,4 +1,4 @@ -package jdcloudssl_test +package jdcloudssl_test import ( "context" diff --git a/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go b/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go index 6bc71c3f..8dc2fefe 100644 --- a/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go +++ b/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go @@ -1,4 +1,4 @@ -package qiniusslcert +package qiniusslcert import ( "context" @@ -7,12 +7,11 @@ import ( "log/slog" "time" - xerrors "github.com/pkg/errors" "github.com/qiniu/go-sdk/v7/auth" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" - qiniusdk "github.com/usual2970/certimate/internal/pkg/vendors/qiniu-sdk" + qiniusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/qiniu" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -37,7 +36,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKey, config.SecretKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -56,9 +55,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -69,10 +68,10 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // 上传新证书 // REF: https://developer.qiniu.com/fusion/8593/interface-related-certificate - uploadSslCertResp, err := u.sdkClient.UploadSslCert(context.TODO(), certName, certX509.Subject.CommonName, certPem, privkeyPem) + uploadSslCertResp, err := u.sdkClient.UploadSslCert(context.TODO(), certName, certX509.Subject.CommonName, certPEM, privkeyPEM) u.logger.Debug("sdk request 'cdn.UploadSslCert'", slog.Any("response", uploadSslCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.UploadSslCert'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.UploadSslCert': %w", err) } certId = uploadSslCertResp.CertID diff --git a/internal/pkg/core/uploader/providers/rainyun-sslcenter/rainyun_sslcenter.go b/internal/pkg/core/uploader/providers/rainyun-sslcenter/rainyun_sslcenter.go new file mode 100644 index 00000000..cb493110 --- /dev/null +++ b/internal/pkg/core/uploader/providers/rainyun-sslcenter/rainyun_sslcenter.go @@ -0,0 +1,173 @@ +package rainyunsslcenter + +import ( + "context" + "errors" + "fmt" + "log/slog" + "strings" + + "github.com/usual2970/certimate/internal/pkg/core/uploader" + rainyunsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/rainyun" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" +) + +type UploaderConfig struct { + // 雨云 API 密钥。 + ApiKey string `json:"ApiKey"` +} + +type UploaderProvider struct { + config *UploaderConfig + logger *slog.Logger + sdkClient *rainyunsdk.Client +} + +var _ uploader.Uploader = (*UploaderProvider)(nil) + +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiKey) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &UploaderProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { + if logger == nil { + u.logger = slog.Default() + } else { + u.logger = logger + } + return u +} + +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { + if res, err := u.getCertIfExists(ctx, certPEM); err != nil { + return nil, err + } else if res != nil { + u.logger.Info("ssl certificate already exists") + return res, nil + } + + // SSL 证书上传 + // REF: https://apifox.com/apidoc/shared/a4595cc8-44c5-4678-a2a3-eed7738dab03/api-69943046 + sslCenterCreateReq := &rainyunsdk.SslCenterCreateRequest{ + Cert: certPEM, + Key: privkeyPEM, + } + sslCenterCreateResp, err := u.sdkClient.SslCenterCreate(sslCenterCreateReq) + u.logger.Debug("sdk request 'sslcenter.Create'", slog.Any("request", sslCenterCreateReq), slog.Any("response", sslCenterCreateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'sslcenter.Create': %w", err) + } + + if res, err := u.getCertIfExists(ctx, certPEM); err != nil { + return nil, err + } else if res == nil { + return nil, errors.New("rainyun sslcenter: no certificate found") + } else { + return res, nil + } +} + +func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string) (res *uploader.UploadResult, err error) { + // 解析证书内容 + certX509, err := certutil.ParseCertificateFromPEM(certPEM) + if err != nil { + return nil, err + } + + // 遍历 SSL 证书列表,避免重复上传 + // REF: https://apifox.com/apidoc/shared/a4595cc8-44c5-4678-a2a3-eed7738dab03/api-69943046 + // REF: https://apifox.com/apidoc/shared/a4595cc8-44c5-4678-a2a3-eed7738dab03/api-69943048 + sslCenterListPage := int32(1) + sslCenterListPerPage := int32(100) + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + sslCenterListReq := &rainyunsdk.SslCenterListRequest{ + Filters: &rainyunsdk.SslCenterListFilters{ + Domain: &certX509.Subject.CommonName, + }, + Page: &sslCenterListPage, + PerPage: &sslCenterListPerPage, + } + sslCenterListResp, err := u.sdkClient.SslCenterList(sslCenterListReq) + u.logger.Debug("sdk request 'sslcenter.List'", slog.Any("request", sslCenterListReq), slog.Any("response", sslCenterListResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'sslcenter.List': %w", err) + } + + if sslCenterListResp.Data != nil && sslCenterListResp.Data.Records != nil { + for _, sslItem := range sslCenterListResp.Data.Records { + // 先对比证书的多域名 + if sslItem.Domain != strings.Join(certX509.DNSNames, ", ") { + continue + } + + // 再对比证书的有效期 + if sslItem.StartDate != certX509.NotBefore.Unix() || sslItem.ExpireDate != certX509.NotAfter.Unix() { + continue + } + + // 最后对比证书内容 + sslCenterGetResp, err := u.sdkClient.SslCenterGet(sslItem.ID) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'sslcenter.Get': %w", err) + } + + var isSameCert bool + if sslCenterGetResp.Data != nil { + if sslCenterGetResp.Data.Cert == certPEM { + isSameCert = true + } else { + oldCertX509, err := certutil.ParseCertificateFromPEM(sslCenterGetResp.Data.Cert) + if err != nil { + continue + } + + isSameCert = certutil.EqualCertificate(certX509, oldCertX509) + } + } + + // 如果已存在相同证书,直接返回 + if isSameCert { + return &uploader.UploadResult{ + CertId: fmt.Sprintf("%d", sslItem.ID), + }, nil + } + } + } + + if sslCenterListResp.Data == nil || len(sslCenterListResp.Data.Records) < int(sslCenterListPerPage) { + break + } else { + sslCenterListPage++ + } + } + + return nil, nil +} + +func createSdkClient(apiKey string) (*rainyunsdk.Client, error) { + if apiKey == "" { + return nil, errors.New("invalid rainyun api key") + } + + client := rainyunsdk.NewClient(apiKey) + return client, nil +} diff --git a/internal/pkg/core/uploader/providers/rainyun-sslcenter/rainyun_sslcenter_test.go b/internal/pkg/core/uploader/providers/rainyun-sslcenter/rainyun_sslcenter_test.go new file mode 100644 index 00000000..e6f801a1 --- /dev/null +++ b/internal/pkg/core/uploader/providers/rainyun-sslcenter/rainyun_sslcenter_test.go @@ -0,0 +1,67 @@ +package rainyunsslcenter_test + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/rainyun-sslcenter" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiKey string +) + +func init() { + argsPrefix := "CERTIMATE_UPLOADER_RAINYUNSSLCENTER_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") +} + +/* +Shell command to run this test: + + go test -v ./rainyun_sslcenter_test.go -args \ + --CERTIMATE_UPLOADER_RAINYUNSSLCENTER_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_UPLOADER_RAINYUNSSLCENTER_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_UPLOADER_RAINYUNSSLCENTER_APIKEY="your-api-key" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIKEY: %v", fApiKey), + }, "\n")) + + uploader, err := provider.NewUploader(&provider.UploaderConfig{ + ApiKey: fApiKey, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := uploader.Upload(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + sres, _ := json.Marshal(res) + t.Logf("ok: %s", string(sres)) + }) +} diff --git a/internal/pkg/core/uploader/providers/tencentcloud-ssl/tencentcloud_ssl.go b/internal/pkg/core/uploader/providers/tencentcloud-ssl/tencentcloud_ssl.go index 45c7ba9e..59067de4 100644 --- a/internal/pkg/core/uploader/providers/tencentcloud-ssl/tencentcloud_ssl.go +++ b/internal/pkg/core/uploader/providers/tencentcloud-ssl/tencentcloud_ssl.go @@ -1,10 +1,10 @@ -package tencentcloudssl +package tencentcloudssl import ( "context" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" tcssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" @@ -34,7 +34,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.SecretId, config.SecretKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -53,17 +53,17 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 上传新证书 // REF: https://cloud.tencent.com/document/product/400/41665 uploadCertificateReq := tcssl.NewUploadCertificateRequest() - uploadCertificateReq.CertificatePublicKey = common.StringPtr(certPem) - uploadCertificateReq.CertificatePrivateKey = common.StringPtr(privkeyPem) + uploadCertificateReq.CertificatePublicKey = common.StringPtr(certPEM) + uploadCertificateReq.CertificatePrivateKey = common.StringPtr(privkeyPEM) uploadCertificateReq.Repeatable = common.BoolPtr(false) uploadCertificateResp, err := u.sdkClient.UploadCertificate(uploadCertificateReq) u.logger.Debug("sdk request 'ssl.UploadCertificate'", slog.Any("request", uploadCertificateReq), slog.Any("response", uploadCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.UploadCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'ssl.UploadCertificate': %w", err) } certId := *uploadCertificateResp.Response.CertificateId diff --git a/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go b/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go index b8639bf3..90eb1683 100644 --- a/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go +++ b/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go @@ -12,13 +12,12 @@ import ( "strings" "time" - xerrors "github.com/pkg/errors" "github.com/ucloud/ucloud-sdk-go/ucloud" ucloudauth "github.com/ucloud/ucloud-sdk-go/ucloud/auth" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" - usslsdk "github.com/usual2970/certimate/internal/pkg/vendors/ucloud-sdk/ussl" + usslsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/ucloud/ussl" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -45,7 +44,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.PrivateKey, config.PublicKey) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -64,23 +63,23 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 生成新证书名(需符合优刻得命名规则) var certId, certName string certName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) // 生成优刻得所需的证书参数 - certPemBase64 := base64.StdEncoding.EncodeToString([]byte(certPem)) - privkeyPemBase64 := base64.StdEncoding.EncodeToString([]byte(privkeyPem)) - certMd5 := md5.Sum([]byte(certPemBase64 + privkeyPemBase64)) + certPEMBase64 := base64.StdEncoding.EncodeToString([]byte(certPEM)) + privkeyPEMBase64 := base64.StdEncoding.EncodeToString([]byte(privkeyPEM)) + certMd5 := md5.Sum([]byte(certPEMBase64 + privkeyPEMBase64)) certMd5Hex := hex.EncodeToString(certMd5[:]) // 上传托管证书 // REF: https://docs.ucloud.cn/api/usslcertificate-api/upload_normal_certificate uploadNormalCertificateReq := u.sdkClient.NewUploadNormalCertificateRequest() uploadNormalCertificateReq.CertificateName = ucloud.String(certName) - uploadNormalCertificateReq.SslPublicKey = ucloud.String(certPemBase64) - uploadNormalCertificateReq.SslPrivateKey = ucloud.String(privkeyPemBase64) + uploadNormalCertificateReq.SslPublicKey = ucloud.String(certPEMBase64) + uploadNormalCertificateReq.SslPrivateKey = ucloud.String(privkeyPEMBase64) uploadNormalCertificateReq.SslMD5 = ucloud.String(certMd5Hex) if u.config.ProjectId != "" { uploadNormalCertificateReq.ProjectId = ucloud.String(u.config.ProjectId) @@ -89,17 +88,17 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe u.logger.Debug("sdk request 'ussl.UploadNormalCertificate'", slog.Any("request", uploadNormalCertificateReq), slog.Any("response", uploadNormalCertificateResp)) if err != nil { if uploadNormalCertificateResp != nil && uploadNormalCertificateResp.GetRetCode() == 80035 { - if res, err := u.getExistCert(ctx, certPem); err != nil { + if res, err := u.getCertIfExists(ctx, certPEM); err != nil { return nil, err } else if res == nil { - return nil, errors.New("no certificate found") + return nil, errors.New("ucloud ssl: no certificate found") } else { u.logger.Info("ssl certificate already exists") return res, nil } } - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ussl.UploadNormalCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'ussl.UploadNormalCertificate': %w", err) } certId = fmt.Sprintf("%d", uploadNormalCertificateResp.CertificateID) @@ -112,9 +111,9 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe }, nil } -func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -125,6 +124,12 @@ func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string) (re getCertificateListPage := int(1) getCertificateListLimit := int(1000) for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + getCertificateListReq := u.sdkClient.NewGetCertificateListRequest() getCertificateListReq.Mode = ucloud.String("trust") getCertificateListReq.Domain = ucloud.String(certX509.Subject.CommonName) @@ -137,7 +142,7 @@ func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string) (re getCertificateListResp, err := u.sdkClient.GetCertificateList(getCertificateListReq) u.logger.Debug("sdk request 'ussl.GetCertificateList'", slog.Any("request", getCertificateListReq), slog.Any("response", getCertificateListResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ussl.GetCertificateList'") + return nil, fmt.Errorf("failed to execute sdk request 'ussl.GetCertificateList': %w", err) } if getCertificateListResp.CertificateList != nil { @@ -164,7 +169,7 @@ func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string) (re } getCertificateDetailInfoResp, err := u.sdkClient.GetCertificateDetailInfo(getCertificateDetailInfoReq) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'ussl.GetCertificateDetailInfo'") + return nil, fmt.Errorf("failed to execute sdk request 'ussl.GetCertificateDetailInfo': %w", err) } switch certX509.SignatureAlgorithm { diff --git a/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl_test.go b/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl_test.go index b6324fd5..b459e005 100644 --- a/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl_test.go +++ b/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl_test.go @@ -1,4 +1,4 @@ -package ucloudussl_test +package ucloudussl_test import ( "context" diff --git a/internal/pkg/core/uploader/providers/upyun-ssl/upyun_ssl.go b/internal/pkg/core/uploader/providers/upyun-ssl/upyun_ssl.go index dbbe41fa..7a8bd3a0 100644 --- a/internal/pkg/core/uploader/providers/upyun-ssl/upyun_ssl.go +++ b/internal/pkg/core/uploader/providers/upyun-ssl/upyun_ssl.go @@ -1,14 +1,13 @@ -package upyunssl +package upyunssl import ( "context" "errors" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" - "github.com/usual2970/certimate/internal/pkg/core/uploader" - upyunsdk "github.com/usual2970/certimate/internal/pkg/vendors/upyun-sdk/console" + upyunsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/upyun/console" ) type UploaderConfig struct { @@ -33,7 +32,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.Username, config.Password) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -52,16 +51,16 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 上传证书 uploadHttpsCertificateReq := &upyunsdk.UploadHttpsCertificateRequest{ - Certificate: certPem, - PrivateKey: privkeyPem, + Certificate: certPEM, + PrivateKey: privkeyPEM, } uploadHttpsCertificateResp, err := u.sdkClient.UploadHttpsCertificate(uploadHttpsCertificateReq) u.logger.Debug("sdk request 'console.UploadHttpsCertificate'", slog.Any("response", uploadHttpsCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'console.UploadHttpsCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'console.UploadHttpsCertificate': %w", err) } return &uploader.UploadResult{ diff --git a/internal/pkg/core/uploader/providers/upyun-ssl/upyun_ssl_test.go b/internal/pkg/core/uploader/providers/upyun-ssl/upyun_ssl_test.go index 1e6d81ec..baf53d74 100644 --- a/internal/pkg/core/uploader/providers/upyun-ssl/upyun_ssl_test.go +++ b/internal/pkg/core/uploader/providers/upyun-ssl/upyun_ssl_test.go @@ -1,4 +1,4 @@ -package upyunssl_test +package upyunssl_test import ( "context" diff --git a/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go b/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go index 2fe52472..b529e84a 100644 --- a/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go +++ b/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go @@ -10,12 +10,11 @@ import ( "strings" "time" - xerrors "github.com/pkg/errors" vecdn "github.com/volcengine/volc-sdk-golang/service/cdn" ve "github.com/volcengine/volcengine-go-sdk/volcengine" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -58,9 +57,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -76,10 +75,16 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe Source: "volc_cert_center", } for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + listCertInfoResp, err := u.sdkClient.ListCertInfo(listCertInfoReq) u.logger.Debug("sdk request 'cdn.ListCertInfo'", slog.Any("request", listCertInfoReq), slog.Any("response", listCertInfoResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ListCertInfo'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.ListCertInfo': %w", err) } if listCertInfoResp.Result.CertInfo != nil { @@ -115,15 +120,15 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe // 上传新证书 // REF: https://www.volcengine.com/docs/6454/1245763 addCertificateReq := &vecdn.AddCertificateRequest{ - Certificate: certPem, - PrivateKey: privkeyPem, + Certificate: certPEM, + PrivateKey: privkeyPEM, Source: ve.String("volc_cert_center"), Desc: ve.String(certName), } addCertificateResp, err := u.sdkClient.AddCertificate(addCertificateReq) u.logger.Debug("sdk request 'cdn.AddCertificate'", slog.Any("request", addCertificateResp), slog.Any("response", addCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.AddCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'cdn.AddCertificate': %w", err) } certId = addCertificateResp.Result.CertId diff --git a/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter.go b/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter.go index 990d9550..99511ebf 100644 --- a/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter.go +++ b/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter.go @@ -2,14 +2,15 @@ package volcenginecertcenter import ( "context" + "errors" + "fmt" "log/slog" - xerrors "github.com/pkg/errors" ve "github.com/volcengine/volcengine-go-sdk/volcengine" vesession "github.com/volcengine/volcengine-go-sdk/volcengine/session" "github.com/usual2970/certimate/internal/pkg/core/uploader" - veccsdk "github.com/usual2970/certimate/internal/pkg/vendors/volcengine-sdk/certcenter" + veccsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/volcengine/certcenter" ) type UploaderConfig struct { @@ -36,7 +37,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) if err != nil { - return nil, xerrors.Wrap(err, "failed to create sdk client") + return nil, fmt.Errorf("failed to create sdk client: %w", err) } return &UploaderProvider{ @@ -55,29 +56,34 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 上传证书 // REF: https://www.volcengine.com/docs/6638/1365580 importCertificateReq := &veccsdk.ImportCertificateInput{ CertificateInfo: &veccsdk.ImportCertificateInputCertificateInfo{ - CertificateChain: ve.String(certPem), - PrivateKey: ve.String(privkeyPem), + CertificateChain: ve.String(certPEM), + PrivateKey: ve.String(privkeyPEM), }, Repeatable: ve.Bool(false), } importCertificateResp, err := u.sdkClient.ImportCertificate(importCertificateReq) u.logger.Debug("sdk request 'certcenter.ImportCertificate'", slog.Any("request", importCertificateReq), slog.Any("response", importCertificateResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'certcenter.ImportCertificate'") + return nil, fmt.Errorf("failed to execute sdk request 'certcenter.ImportCertificate': %w", err) } var certId string - if importCertificateResp.InstanceId != nil { + if importCertificateResp.InstanceId != nil && *importCertificateResp.InstanceId != "" { certId = *importCertificateResp.InstanceId } - if importCertificateResp.RepeatId != nil { + if importCertificateResp.RepeatId != nil && *importCertificateResp.RepeatId != "" { certId = *importCertificateResp.RepeatId } + + if certId == "" { + return nil, errors.New("failed to get certificate id from response, both `InstanceId` and `RepeatId` are empty") + } + return &uploader.UploadResult{ CertId: certId, }, nil diff --git a/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter_test.go b/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter_test.go index 5c312707..1cfa15fe 100644 --- a/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter_test.go +++ b/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter_test.go @@ -1,4 +1,4 @@ -package volcenginecertcenter_test +package volcenginecertcenter_test import ( "context" diff --git a/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go b/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go index 46031c61..de5ec27d 100644 --- a/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go +++ b/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go @@ -7,12 +7,11 @@ import ( "strings" "time" - xerrors "github.com/pkg/errors" velive "github.com/volcengine/volc-sdk-golang/service/live/v20230101" ve "github.com/volcengine/volcengine-go-sdk/volcengine" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) type UploaderConfig struct { @@ -55,9 +54,9 @@ func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { return u } -func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := certutil.ParseCertificateFromPEM(certPem) + certX509, err := certutil.ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } @@ -68,7 +67,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe listCertResp, err := u.sdkClient.ListCertV2(ctx, listCertReq) u.logger.Debug("sdk request 'live.ListCertV2'", slog.Any("request", listCertReq), slog.Any("response", listCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.ListCertV2'") + return nil, fmt.Errorf("failed to execute sdk request 'live.ListCertV2': %w", err) } if listCertResp.Result.CertList != nil { for _, certDetail := range listCertResp.Result.CertList { @@ -85,7 +84,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe var isSameCert bool certificate := strings.Join(describeCertDetailSecretResp.Result.SSL.Chain, "\n\n") - if certificate == certPem { + if certificate == certPEM { isSameCert = true } else { oldCertX509, err := certutil.ParseCertificateFromPEM(certificate) @@ -118,14 +117,14 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe UseWay: "https", ProjectName: ve.String("default"), Rsa: velive.CreateCertBodyRsa{ - Prikey: privkeyPem, - Pubkey: certPem, + Prikey: privkeyPEM, + Pubkey: certPEM, }, } createCertResp, err := u.sdkClient.CreateCert(ctx, createCertReq) u.logger.Debug("sdk request 'live.CreateCert'", slog.Any("request", createCertReq), slog.Any("response", createCertResp)) if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.CreateCert'") + return nil, fmt.Errorf("failed to execute sdk request 'live.CreateCert': %w", err) } certId = *createCertResp.Result.ChainID diff --git a/internal/pkg/core/uploader/uploader.go b/internal/pkg/core/uploader/uploader.go index 5edcdce4..34d2813a 100644 --- a/internal/pkg/core/uploader/uploader.go +++ b/internal/pkg/core/uploader/uploader.go @@ -1,4 +1,4 @@ -package uploader +package uploader import ( "context" @@ -15,13 +15,13 @@ type Uploader interface { // // 入参: // - ctx:上下文。 - // - certPem:证书 PEM 内容。 - // - privkeyPem:私钥 PEM 内容。 + // - certPEM:证书 PEM 内容。 + // - privkeyPEM:私钥 PEM 内容。 // // 出参: // - res:上传结果。 // - err: 错误。 - Upload(ctx context.Context, certPem string, privkeyPem string) (res *UploadResult, err error) + Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *UploadResult, err error) } // 表示证书上传结果的数据结构,包含上传后的证书 ID、名称和其他数据。 diff --git a/internal/pkg/logging/handler.go b/internal/pkg/logging/handler.go index 36174b45..7cdacdd7 100644 --- a/internal/pkg/logging/handler.go +++ b/internal/pkg/logging/handler.go @@ -1,4 +1,4 @@ -package logging +package logging import ( "context" diff --git a/internal/pkg/logging/level.go b/internal/pkg/logging/level.go index 2af1c2f4..1aefe84d 100644 --- a/internal/pkg/logging/level.go +++ b/internal/pkg/logging/level.go @@ -1,4 +1,4 @@ -package logging +package logging import "log/slog" diff --git a/internal/pkg/logging/record.go b/internal/pkg/logging/record.go index 9980489b..38208535 100644 --- a/internal/pkg/logging/record.go +++ b/internal/pkg/logging/record.go @@ -1,4 +1,4 @@ -package logging +package logging import ( "time" diff --git a/internal/pkg/vendors/1panel-sdk/api.go b/internal/pkg/sdk3rd/1panel/api.go similarity index 70% rename from internal/pkg/vendors/1panel-sdk/api.go rename to internal/pkg/sdk3rd/1panel/api.go index 8b0a6d09..baab2313 100644 --- a/internal/pkg/vendors/1panel-sdk/api.go +++ b/internal/pkg/sdk3rd/1panel/api.go @@ -17,6 +17,16 @@ func (c *Client) SearchWebsiteSSL(req *SearchWebsiteSSLRequest) (*SearchWebsiteS return resp, err } +func (c *Client) GetWebsiteSSL(req *GetWebsiteSSLRequest) (*GetWebsiteSSLResponse, error) { + if req.SSLID == 0 { + return nil, fmt.Errorf("1panel api error: invalid parameter: SSLID") + } + + resp := &GetWebsiteSSLResponse{} + err := c.sendRequestWithResult(http.MethodGet, fmt.Sprintf("/websites/ssl/%d", req.SSLID), req, resp) + return resp, err +} + func (c *Client) UploadWebsiteSSL(req *UploadWebsiteSSLRequest) (*UploadWebsiteSSLResponse, error) { resp := &UploadWebsiteSSLResponse{} err := c.sendRequestWithResult(http.MethodPost, "/websites/ssl/upload", req, resp) @@ -24,12 +34,20 @@ func (c *Client) UploadWebsiteSSL(req *UploadWebsiteSSLRequest) (*UploadWebsiteS } func (c *Client) GetHttpsConf(req *GetHttpsConfRequest) (*GetHttpsConfResponse, error) { + if req.WebsiteID == 0 { + return nil, fmt.Errorf("1panel api error: invalid parameter: WebsiteID") + } + resp := &GetHttpsConfResponse{} err := c.sendRequestWithResult(http.MethodGet, fmt.Sprintf("/websites/%d/https", req.WebsiteID), req, resp) return resp, err } func (c *Client) UpdateHttpsConf(req *UpdateHttpsConfRequest) (*UpdateHttpsConfResponse, error) { + if req.WebsiteID == 0 { + return nil, fmt.Errorf("1panel api error: invalid parameter: WebsiteID") + } + resp := &UpdateHttpsConfResponse{} err := c.sendRequestWithResult(http.MethodPost, fmt.Sprintf("/websites/%d/https", req.WebsiteID), req, resp) return resp, err diff --git a/internal/pkg/vendors/1panel-sdk/client.go b/internal/pkg/sdk3rd/1panel/client.go similarity index 97% rename from internal/pkg/vendors/1panel-sdk/client.go rename to internal/pkg/sdk3rd/1panel/client.go index 653e3a69..02dc8f58 100644 --- a/internal/pkg/vendors/1panel-sdk/client.go +++ b/internal/pkg/sdk3rd/1panel/client.go @@ -79,7 +79,7 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r if err != nil { return resp, fmt.Errorf("1panel api error: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("1panel api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return resp, fmt.Errorf("1panel api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) } return resp, nil diff --git a/internal/pkg/vendors/1panel-sdk/models.go b/internal/pkg/sdk3rd/1panel/models.go similarity index 80% rename from internal/pkg/vendors/1panel-sdk/models.go rename to internal/pkg/sdk3rd/1panel/models.go index af1bdbe9..13f144d9 100644 --- a/internal/pkg/vendors/1panel-sdk/models.go +++ b/internal/pkg/sdk3rd/1panel/models.go @@ -59,6 +59,28 @@ type SearchWebsiteSSLResponse struct { } `json:"data,omitempty"` } +type GetWebsiteSSLRequest struct { + SSLID int64 `json:"-"` +} + +type GetWebsiteSSLResponse struct { + baseResponse + Data *struct { + ID int64 `json:"id"` + Provider string `json:"provider"` + Description string `json:"description"` + PrimaryDomain string `json:"primaryDomain"` + Domains string `json:"domains"` + Type string `json:"type"` + Organization string `json:"organization"` + Status string `json:"status"` + StartDate string `json:"startDate"` + ExpireDate string `json:"expireDate"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + } `json:"data,omitempty"` +} + type UploadWebsiteSSLRequest struct { Type string `json:"type"` SSLID int64 `json:"sslID"` diff --git a/internal/pkg/vendors/azure-sdk/common/config.go b/internal/pkg/sdk3rd/azure/common/config.go similarity index 98% rename from internal/pkg/vendors/azure-sdk/common/config.go rename to internal/pkg/sdk3rd/azure/common/config.go index eca082f9..45a1e490 100644 --- a/internal/pkg/vendors/azure-sdk/common/config.go +++ b/internal/pkg/sdk3rd/azure/common/config.go @@ -1,4 +1,4 @@ -package common +package common import ( "fmt" diff --git a/internal/pkg/vendors/baiducloud-sdk/cert/api.go b/internal/pkg/sdk3rd/baiducloud/cert/api.go similarity index 100% rename from internal/pkg/vendors/baiducloud-sdk/cert/api.go rename to internal/pkg/sdk3rd/baiducloud/cert/api.go diff --git a/internal/pkg/vendors/baiducloud-sdk/cert/client.go b/internal/pkg/sdk3rd/baiducloud/cert/client.go similarity index 100% rename from internal/pkg/vendors/baiducloud-sdk/cert/client.go rename to internal/pkg/sdk3rd/baiducloud/cert/client.go diff --git a/internal/pkg/vendors/baiducloud-sdk/cert/models.go b/internal/pkg/sdk3rd/baiducloud/cert/models.go similarity index 100% rename from internal/pkg/vendors/baiducloud-sdk/cert/models.go rename to internal/pkg/sdk3rd/baiducloud/cert/models.go diff --git a/internal/pkg/vendors/baishan-sdk/api.go b/internal/pkg/sdk3rd/baishan/api.go similarity index 100% rename from internal/pkg/vendors/baishan-sdk/api.go rename to internal/pkg/sdk3rd/baishan/api.go diff --git a/internal/pkg/vendors/baishan-sdk/client.go b/internal/pkg/sdk3rd/baishan/client.go similarity index 97% rename from internal/pkg/vendors/baishan-sdk/client.go rename to internal/pkg/sdk3rd/baishan/client.go index 400e1ae1..ad906cbe 100644 --- a/internal/pkg/vendors/baishan-sdk/client.go +++ b/internal/pkg/sdk3rd/baishan/client.go @@ -75,7 +75,7 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r if err != nil { return resp, fmt.Errorf("baishan api error: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("baishan api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return resp, fmt.Errorf("baishan api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) } return resp, nil diff --git a/internal/pkg/vendors/baishan-sdk/models.go b/internal/pkg/sdk3rd/baishan/models.go similarity index 90% rename from internal/pkg/vendors/baishan-sdk/models.go rename to internal/pkg/sdk3rd/baishan/models.go index b7dfd3f5..397061d4 100644 --- a/internal/pkg/vendors/baishan-sdk/models.go +++ b/internal/pkg/sdk3rd/baishan/models.go @@ -27,9 +27,10 @@ func (r *baseResponse) GetMessage() string { } type CreateCertificateRequest struct { - Certificate string `json:"certificate"` - Key string `json:"key"` - Name string `json:"name"` + CertificateId *string `json:"cert_id,omitempty"` + Certificate string `json:"certificate"` + Key string `json:"key"` + Name string `json:"name"` } type CreateCertificateResponse struct { diff --git a/internal/pkg/vendors/btpanel-sdk/api.go b/internal/pkg/sdk3rd/btpanel/api.go similarity index 100% rename from internal/pkg/vendors/btpanel-sdk/api.go rename to internal/pkg/sdk3rd/btpanel/api.go diff --git a/internal/pkg/vendors/btpanel-sdk/client.go b/internal/pkg/sdk3rd/btpanel/client.go similarity index 98% rename from internal/pkg/vendors/btpanel-sdk/client.go rename to internal/pkg/sdk3rd/btpanel/client.go index 8fb4ad32..1e48f734 100644 --- a/internal/pkg/vendors/btpanel-sdk/client.go +++ b/internal/pkg/sdk3rd/btpanel/client.go @@ -86,7 +86,7 @@ func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, if err != nil { return resp, fmt.Errorf("baota api error: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("baota api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return resp, fmt.Errorf("baota api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) } return resp, nil diff --git a/internal/pkg/vendors/btpanel-sdk/models.go b/internal/pkg/sdk3rd/btpanel/models.go similarity index 100% rename from internal/pkg/vendors/btpanel-sdk/models.go rename to internal/pkg/sdk3rd/btpanel/models.go diff --git a/internal/pkg/sdk3rd/bunny/api.go b/internal/pkg/sdk3rd/bunny/api.go new file mode 100644 index 00000000..9c6aa285 --- /dev/null +++ b/internal/pkg/sdk3rd/bunny/api.go @@ -0,0 +1,16 @@ +package bunnysdk + +import ( + "fmt" + "net/http" + "net/url" +) + +func (c *Client) AddCustomCertificate(req *AddCustomCertificateRequest) ([]byte, error) { + if req.PullZoneId == "" { + return nil, fmt.Errorf("bunny api error: invalid parameter: PullZoneId") + } + + resp, err := c.sendRequest(http.MethodPost, fmt.Sprintf("/pullzone/%s/addCertificate", url.PathEscape(req.PullZoneId)), req) + return resp.Body(), err +} diff --git a/internal/pkg/sdk3rd/bunny/client.go b/internal/pkg/sdk3rd/bunny/client.go new file mode 100644 index 00000000..90b43f39 --- /dev/null +++ b/internal/pkg/sdk3rd/bunny/client.go @@ -0,0 +1,66 @@ +package bunnysdk + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + apiToken string + + client *resty.Client +} + +func NewClient(apiToken string) *Client { + client := resty.New() + + return &Client{ + apiToken: apiToken, + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R() + req.Method = method + req.URL = "https://api.bunny.net" + path + req = req.SetHeader("AccessKey", c.apiToken) + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req.SetQueryParams(qs) + } else { + req = req. + SetHeader("Content-Type", "application/json"). + SetBody(params) + } + + resp, err := req.Send() + if err != nil { + return resp, fmt.Errorf("bunny api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("bunny api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} diff --git a/internal/pkg/sdk3rd/bunny/models.go b/internal/pkg/sdk3rd/bunny/models.go new file mode 100644 index 00000000..3306cf5b --- /dev/null +++ b/internal/pkg/sdk3rd/bunny/models.go @@ -0,0 +1,8 @@ +package bunnysdk + +type AddCustomCertificateRequest struct { + Hostname string `json:"Hostname"` + PullZoneId string `json:"-"` + Certificate string `json:"Certificate"` + CertificateKey string `json:"CertificateKey"` +} diff --git a/internal/pkg/vendors/cachefly-sdk/api.go b/internal/pkg/sdk3rd/cachefly/api.go similarity index 100% rename from internal/pkg/vendors/cachefly-sdk/api.go rename to internal/pkg/sdk3rd/cachefly/api.go diff --git a/internal/pkg/vendors/cachefly-sdk/client.go b/internal/pkg/sdk3rd/cachefly/client.go similarity index 97% rename from internal/pkg/vendors/cachefly-sdk/client.go rename to internal/pkg/sdk3rd/cachefly/client.go index 0b11f6d2..a460ae96 100644 --- a/internal/pkg/vendors/cachefly-sdk/client.go +++ b/internal/pkg/sdk3rd/cachefly/client.go @@ -59,7 +59,7 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r if err != nil { return resp, fmt.Errorf("cachefly api error: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("cachefly api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return resp, fmt.Errorf("cachefly api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) } return resp, nil diff --git a/internal/pkg/vendors/cachefly-sdk/models.go b/internal/pkg/sdk3rd/cachefly/models.go similarity index 100% rename from internal/pkg/vendors/cachefly-sdk/models.go rename to internal/pkg/sdk3rd/cachefly/models.go diff --git a/internal/pkg/vendors/cdnfly-sdk/api.go b/internal/pkg/sdk3rd/cdnfly/api.go similarity index 67% rename from internal/pkg/vendors/cdnfly-sdk/api.go rename to internal/pkg/sdk3rd/cdnfly/api.go index 2387f6d8..4091a84d 100644 --- a/internal/pkg/vendors/cdnfly-sdk/api.go +++ b/internal/pkg/sdk3rd/cdnfly/api.go @@ -3,17 +3,26 @@ package cdnflysdk import ( "fmt" "net/http" + "net/url" ) func (c *Client) GetSite(req *GetSiteRequest) (*GetSiteResponse, error) { + if req.Id == "" { + return nil, fmt.Errorf("cdnfly api error: invalid parameter: Id") + } + resp := &GetSiteResponse{} - err := c.sendRequestWithResult(http.MethodGet, fmt.Sprintf("/v1/sites/%s", req.Id), req, resp) + err := c.sendRequestWithResult(http.MethodGet, fmt.Sprintf("/v1/sites/%s", url.PathEscape(req.Id)), req, resp) return resp, err } func (c *Client) UpdateSite(req *UpdateSiteRequest) (*UpdateSiteResponse, error) { + if req.Id == "" { + return nil, fmt.Errorf("cdnfly api error: invalid parameter: Id") + } + resp := &UpdateSiteResponse{} - err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/v1/sites/%s", req.Id), req, resp) + err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/v1/sites/%s", url.PathEscape(req.Id)), req, resp) return resp, err } @@ -24,7 +33,11 @@ func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertif } func (c *Client) UpdateCertificate(req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { + if req.Id == "" { + return nil, fmt.Errorf("cdnfly api error: invalid parameter: Id") + } + resp := &UpdateCertificateResponse{} - err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/v1/certs/%s", req.Id), req, resp) + err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/v1/certs/%s", url.PathEscape(req.Id)), req, resp) return resp, err } diff --git a/internal/pkg/vendors/cdnfly-sdk/client.go b/internal/pkg/sdk3rd/cdnfly/client.go similarity index 92% rename from internal/pkg/vendors/cdnfly-sdk/client.go rename to internal/pkg/sdk3rd/cdnfly/client.go index b43a04db..c1a810d9 100644 --- a/internal/pkg/vendors/cdnfly-sdk/client.go +++ b/internal/pkg/sdk3rd/cdnfly/client.go @@ -1,6 +1,7 @@ package cdnflysdk import ( + "crypto/tls" "encoding/json" "fmt" "net/http" @@ -34,6 +35,11 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client { return c } +func (c *Client) WithTLSConfig(config *tls.Config) *Client { + c.client.SetTLSClientConfig(config) + return c +} + func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { req := c.client.R() req.Method = method @@ -65,7 +71,7 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r if err != nil { return resp, fmt.Errorf("cdnfly api error: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("cdnfly api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return resp, fmt.Errorf("cdnfly api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) } return resp, nil diff --git a/internal/pkg/vendors/cdnfly-sdk/models.go b/internal/pkg/sdk3rd/cdnfly/models.go similarity index 85% rename from internal/pkg/vendors/cdnfly-sdk/models.go rename to internal/pkg/sdk3rd/cdnfly/models.go index 07497cd3..9622627a 100644 --- a/internal/pkg/vendors/cdnfly-sdk/models.go +++ b/internal/pkg/sdk3rd/cdnfly/models.go @@ -1,6 +1,6 @@ package cdnflysdk -import "encoding/json" +import "fmt" type BaseResponse interface { GetCode() string @@ -8,12 +8,24 @@ type BaseResponse interface { } type baseResponse struct { - Code json.Number `json:"code"` - Message string `json:"msg"` + Code any `json:"code"` + Message string `json:"msg"` } func (r *baseResponse) GetCode() string { - return r.Code.String() + if r.Code == nil { + return "" + } + + if code, ok := r.Code.(int); ok { + return fmt.Sprintf("%d", code) + } + + if code, ok := r.Code.(string); ok { + return code + } + + return "" } func (r *baseResponse) GetMessage() string { diff --git a/internal/pkg/vendors/cmcc-sdk/README.md b/internal/pkg/sdk3rd/cmcc/README.md similarity index 100% rename from internal/pkg/vendors/cmcc-sdk/README.md rename to internal/pkg/sdk3rd/cmcc/README.md diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/client.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/client.go similarity index 100% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/client.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/client.go diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/go.mod b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/go.mod similarity index 100% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/go.mod rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/go.mod diff --git a/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_body.go new file mode 100644 index 00000000..37d0457d --- /dev/null +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_body.go @@ -0,0 +1,55 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) + +type CreateRecordBodyTypeEnum string + +// List of Type +const ( + CreateRecordBodyTypeEnumA CreateRecordBodyTypeEnum = "A" + CreateRecordBodyTypeEnumAaaa CreateRecordBodyTypeEnum = "AAAA" + CreateRecordBodyTypeEnumCaa CreateRecordBodyTypeEnum = "CAA" + CreateRecordBodyTypeEnumCmauth CreateRecordBodyTypeEnum = "CMAUTH" + CreateRecordBodyTypeEnumCname CreateRecordBodyTypeEnum = "CNAME" + CreateRecordBodyTypeEnumMx CreateRecordBodyTypeEnum = "MX" + CreateRecordBodyTypeEnumNs CreateRecordBodyTypeEnum = "NS" + CreateRecordBodyTypeEnumPtr CreateRecordBodyTypeEnum = "PTR" + CreateRecordBodyTypeEnumRp CreateRecordBodyTypeEnum = "RP" + CreateRecordBodyTypeEnumSpf CreateRecordBodyTypeEnum = "SPF" + CreateRecordBodyTypeEnumSrv CreateRecordBodyTypeEnum = "SRV" + CreateRecordBodyTypeEnumTxt CreateRecordBodyTypeEnum = "TXT" + CreateRecordBodyTypeEnumUrl CreateRecordBodyTypeEnum = "URL" +) + +type CreateRecordBody struct { + position.Body + // 主机头 + Rr string `json:"rr"` + + // 域名名称 + DomainName string `json:"domainName"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId"` + + // MX优先级,若“记录类型”选择”MX”,则需要配置该参数,默认是5 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type CreateRecordBodyTypeEnum `json:"type"` + + // 缓存的生命周期,默认可配置600s + Ttl *int32 `json:"ttl,omitempty"` + + // 记录值 + Value string `json:"value"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_body.go similarity index 50% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_body.go index 3db9f0f7..896d7dc3 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_body.go @@ -5,26 +5,27 @@ package model import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" ) + type CreateRecordOpenapiBodyTypeEnum string // List of Type const ( - CreateRecordOpenapiBodyTypeEnumA CreateRecordOpenapiBodyTypeEnum = "A" - CreateRecordOpenapiBodyTypeEnumAaaa CreateRecordOpenapiBodyTypeEnum = "AAAA" - CreateRecordOpenapiBodyTypeEnumCname CreateRecordOpenapiBodyTypeEnum = "CNAME" - CreateRecordOpenapiBodyTypeEnumMx CreateRecordOpenapiBodyTypeEnum = "MX" - CreateRecordOpenapiBodyTypeEnumTxt CreateRecordOpenapiBodyTypeEnum = "TXT" - CreateRecordOpenapiBodyTypeEnumNs CreateRecordOpenapiBodyTypeEnum = "NS" - CreateRecordOpenapiBodyTypeEnumSpf CreateRecordOpenapiBodyTypeEnum = "SPF" - CreateRecordOpenapiBodyTypeEnumSrv CreateRecordOpenapiBodyTypeEnum = "SRV" - CreateRecordOpenapiBodyTypeEnumCaa CreateRecordOpenapiBodyTypeEnum = "CAA" - CreateRecordOpenapiBodyTypeEnumCmauth CreateRecordOpenapiBodyTypeEnum = "CMAUTH" + CreateRecordOpenapiBodyTypeEnumA CreateRecordOpenapiBodyTypeEnum = "A" + CreateRecordOpenapiBodyTypeEnumAaaa CreateRecordOpenapiBodyTypeEnum = "AAAA" + CreateRecordOpenapiBodyTypeEnumCname CreateRecordOpenapiBodyTypeEnum = "CNAME" + CreateRecordOpenapiBodyTypeEnumMx CreateRecordOpenapiBodyTypeEnum = "MX" + CreateRecordOpenapiBodyTypeEnumTxt CreateRecordOpenapiBodyTypeEnum = "TXT" + CreateRecordOpenapiBodyTypeEnumNs CreateRecordOpenapiBodyTypeEnum = "NS" + CreateRecordOpenapiBodyTypeEnumSpf CreateRecordOpenapiBodyTypeEnum = "SPF" + CreateRecordOpenapiBodyTypeEnumSrv CreateRecordOpenapiBodyTypeEnum = "SRV" + CreateRecordOpenapiBodyTypeEnumCaa CreateRecordOpenapiBodyTypeEnum = "CAA" + CreateRecordOpenapiBodyTypeEnumCmauth CreateRecordOpenapiBodyTypeEnum = "CMAUTH" ) type CreateRecordOpenapiBody struct { - position.Body + position.Body // 主机头 Rr string `json:"rr"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_request.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_request.go index d43fded1..888a66aa 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_request.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_request.go @@ -4,9 +4,6 @@ package model - - type CreateRecordOpenapiRequest struct { - CreateRecordOpenapiBody *CreateRecordOpenapiBody `json:"createRecordOpenapiBody,omitempty"` } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response.go similarity index 57% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response.go index a33b47c7..01c3294f 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response.go @@ -4,19 +4,17 @@ package model - type CreateRecordOpenapiResponseStateEnum string // List of State const ( - CreateRecordOpenapiResponseStateEnumError CreateRecordOpenapiResponseStateEnum = "ERROR" - CreateRecordOpenapiResponseStateEnumException CreateRecordOpenapiResponseStateEnum = "EXCEPTION" - CreateRecordOpenapiResponseStateEnumForbidden CreateRecordOpenapiResponseStateEnum = "FORBIDDEN" - CreateRecordOpenapiResponseStateEnumOk CreateRecordOpenapiResponseStateEnum = "OK" + CreateRecordOpenapiResponseStateEnumError CreateRecordOpenapiResponseStateEnum = "ERROR" + CreateRecordOpenapiResponseStateEnumException CreateRecordOpenapiResponseStateEnum = "EXCEPTION" + CreateRecordOpenapiResponseStateEnumForbidden CreateRecordOpenapiResponseStateEnum = "FORBIDDEN" + CreateRecordOpenapiResponseStateEnumOk CreateRecordOpenapiResponseStateEnum = "OK" ) type CreateRecordOpenapiResponse struct { - RequestId string `json:"requestId,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_body.go similarity index 58% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_body.go index e7c62769..5bbdb75e 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_body.go @@ -4,32 +4,31 @@ package model - type CreateRecordOpenapiResponseBodyTypeEnum string // List of Type const ( - CreateRecordOpenapiResponseBodyTypeEnumA CreateRecordOpenapiResponseBodyTypeEnum = "A" - CreateRecordOpenapiResponseBodyTypeEnumAaaa CreateRecordOpenapiResponseBodyTypeEnum = "AAAA" - CreateRecordOpenapiResponseBodyTypeEnumCname CreateRecordOpenapiResponseBodyTypeEnum = "CNAME" - CreateRecordOpenapiResponseBodyTypeEnumMx CreateRecordOpenapiResponseBodyTypeEnum = "MX" - CreateRecordOpenapiResponseBodyTypeEnumTxt CreateRecordOpenapiResponseBodyTypeEnum = "TXT" - CreateRecordOpenapiResponseBodyTypeEnumNs CreateRecordOpenapiResponseBodyTypeEnum = "NS" - CreateRecordOpenapiResponseBodyTypeEnumSpf CreateRecordOpenapiResponseBodyTypeEnum = "SPF" - CreateRecordOpenapiResponseBodyTypeEnumSrv CreateRecordOpenapiResponseBodyTypeEnum = "SRV" - CreateRecordOpenapiResponseBodyTypeEnumCaa CreateRecordOpenapiResponseBodyTypeEnum = "CAA" - CreateRecordOpenapiResponseBodyTypeEnumCmauth CreateRecordOpenapiResponseBodyTypeEnum = "CMAUTH" + CreateRecordOpenapiResponseBodyTypeEnumA CreateRecordOpenapiResponseBodyTypeEnum = "A" + CreateRecordOpenapiResponseBodyTypeEnumAaaa CreateRecordOpenapiResponseBodyTypeEnum = "AAAA" + CreateRecordOpenapiResponseBodyTypeEnumCname CreateRecordOpenapiResponseBodyTypeEnum = "CNAME" + CreateRecordOpenapiResponseBodyTypeEnumMx CreateRecordOpenapiResponseBodyTypeEnum = "MX" + CreateRecordOpenapiResponseBodyTypeEnumTxt CreateRecordOpenapiResponseBodyTypeEnum = "TXT" + CreateRecordOpenapiResponseBodyTypeEnumNs CreateRecordOpenapiResponseBodyTypeEnum = "NS" + CreateRecordOpenapiResponseBodyTypeEnumSpf CreateRecordOpenapiResponseBodyTypeEnum = "SPF" + CreateRecordOpenapiResponseBodyTypeEnumSrv CreateRecordOpenapiResponseBodyTypeEnum = "SRV" + CreateRecordOpenapiResponseBodyTypeEnumCaa CreateRecordOpenapiResponseBodyTypeEnum = "CAA" + CreateRecordOpenapiResponseBodyTypeEnumCmauth CreateRecordOpenapiResponseBodyTypeEnum = "CMAUTH" ) + type CreateRecordOpenapiResponseBodyStateEnum string // List of State const ( - CreateRecordOpenapiResponseBodyStateEnumDisabled CreateRecordOpenapiResponseBodyStateEnum = "DISABLED" - CreateRecordOpenapiResponseBodyStateEnumEnabled CreateRecordOpenapiResponseBodyStateEnum = "ENABLED" + CreateRecordOpenapiResponseBodyStateEnumDisabled CreateRecordOpenapiResponseBodyStateEnum = "DISABLED" + CreateRecordOpenapiResponseBodyStateEnumEnabled CreateRecordOpenapiResponseBodyStateEnum = "ENABLED" ) type CreateRecordOpenapiResponseBody struct { - // 主机头 Rr string `json:"rr,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_tags.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_tags.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_tags.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_tags.go index a4bda62c..6cc5a35b 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_tags.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_tags.go @@ -4,10 +4,7 @@ package model - - type CreateRecordOpenapiResponseTags struct { - // 标签ID TagId string `json:"tagId,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_request.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_request.go index 715f03ff..d73c37b2 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_request.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_request.go @@ -4,9 +4,6 @@ package model - - type CreateRecordRequest struct { - CreateRecordBody *CreateRecordBody `json:"createRecordBody,omitempty"` } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_response.go similarity index 59% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_response.go index bd277c0c..017f02b1 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_response.go @@ -4,19 +4,17 @@ package model - type CreateRecordResponseStateEnum string // List of State const ( - CreateRecordResponseStateEnumError CreateRecordResponseStateEnum = "ERROR" - CreateRecordResponseStateEnumException CreateRecordResponseStateEnum = "EXCEPTION" - CreateRecordResponseStateEnumForbidden CreateRecordResponseStateEnum = "FORBIDDEN" - CreateRecordResponseStateEnumOk CreateRecordResponseStateEnum = "OK" + CreateRecordResponseStateEnumError CreateRecordResponseStateEnum = "ERROR" + CreateRecordResponseStateEnumException CreateRecordResponseStateEnum = "EXCEPTION" + CreateRecordResponseStateEnumForbidden CreateRecordResponseStateEnum = "FORBIDDEN" + CreateRecordResponseStateEnumOk CreateRecordResponseStateEnum = "OK" ) type CreateRecordResponse struct { - RequestId string `json:"requestId,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_response_body.go similarity index 53% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_response_body.go index 64660b91..8e604bd4 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_response_body.go @@ -4,43 +4,43 @@ package model - type CreateRecordResponseBodyTypeEnum string // List of Type const ( - CreateRecordResponseBodyTypeEnumA CreateRecordResponseBodyTypeEnum = "A" - CreateRecordResponseBodyTypeEnumAaaa CreateRecordResponseBodyTypeEnum = "AAAA" - CreateRecordResponseBodyTypeEnumCaa CreateRecordResponseBodyTypeEnum = "CAA" - CreateRecordResponseBodyTypeEnumCmauth CreateRecordResponseBodyTypeEnum = "CMAUTH" - CreateRecordResponseBodyTypeEnumCname CreateRecordResponseBodyTypeEnum = "CNAME" - CreateRecordResponseBodyTypeEnumMx CreateRecordResponseBodyTypeEnum = "MX" - CreateRecordResponseBodyTypeEnumNs CreateRecordResponseBodyTypeEnum = "NS" - CreateRecordResponseBodyTypeEnumPtr CreateRecordResponseBodyTypeEnum = "PTR" - CreateRecordResponseBodyTypeEnumRp CreateRecordResponseBodyTypeEnum = "RP" - CreateRecordResponseBodyTypeEnumSpf CreateRecordResponseBodyTypeEnum = "SPF" - CreateRecordResponseBodyTypeEnumSrv CreateRecordResponseBodyTypeEnum = "SRV" - CreateRecordResponseBodyTypeEnumTxt CreateRecordResponseBodyTypeEnum = "TXT" - CreateRecordResponseBodyTypeEnumUrl CreateRecordResponseBodyTypeEnum = "URL" + CreateRecordResponseBodyTypeEnumA CreateRecordResponseBodyTypeEnum = "A" + CreateRecordResponseBodyTypeEnumAaaa CreateRecordResponseBodyTypeEnum = "AAAA" + CreateRecordResponseBodyTypeEnumCaa CreateRecordResponseBodyTypeEnum = "CAA" + CreateRecordResponseBodyTypeEnumCmauth CreateRecordResponseBodyTypeEnum = "CMAUTH" + CreateRecordResponseBodyTypeEnumCname CreateRecordResponseBodyTypeEnum = "CNAME" + CreateRecordResponseBodyTypeEnumMx CreateRecordResponseBodyTypeEnum = "MX" + CreateRecordResponseBodyTypeEnumNs CreateRecordResponseBodyTypeEnum = "NS" + CreateRecordResponseBodyTypeEnumPtr CreateRecordResponseBodyTypeEnum = "PTR" + CreateRecordResponseBodyTypeEnumRp CreateRecordResponseBodyTypeEnum = "RP" + CreateRecordResponseBodyTypeEnumSpf CreateRecordResponseBodyTypeEnum = "SPF" + CreateRecordResponseBodyTypeEnumSrv CreateRecordResponseBodyTypeEnum = "SRV" + CreateRecordResponseBodyTypeEnumTxt CreateRecordResponseBodyTypeEnum = "TXT" + CreateRecordResponseBodyTypeEnumUrl CreateRecordResponseBodyTypeEnum = "URL" ) + type CreateRecordResponseBodyTimedStatusEnum string // List of TimedStatus const ( - CreateRecordResponseBodyTimedStatusEnumDisabled CreateRecordResponseBodyTimedStatusEnum = "DISABLED" - CreateRecordResponseBodyTimedStatusEnumEnabled CreateRecordResponseBodyTimedStatusEnum = "ENABLED" - CreateRecordResponseBodyTimedStatusEnumTimed CreateRecordResponseBodyTimedStatusEnum = "TIMED" + CreateRecordResponseBodyTimedStatusEnumDisabled CreateRecordResponseBodyTimedStatusEnum = "DISABLED" + CreateRecordResponseBodyTimedStatusEnumEnabled CreateRecordResponseBodyTimedStatusEnum = "ENABLED" + CreateRecordResponseBodyTimedStatusEnumTimed CreateRecordResponseBodyTimedStatusEnum = "TIMED" ) + type CreateRecordResponseBodyStateEnum string // List of State const ( - CreateRecordResponseBodyStateEnumDisabled CreateRecordResponseBodyStateEnum = "DISABLED" - CreateRecordResponseBodyStateEnumEnabled CreateRecordResponseBodyStateEnum = "ENABLED" + CreateRecordResponseBodyStateEnumDisabled CreateRecordResponseBodyStateEnum = "DISABLED" + CreateRecordResponseBodyStateEnumEnabled CreateRecordResponseBodyStateEnum = "ENABLED" ) type CreateRecordResponseBody struct { - // 主机头 Rr string `json:"rr,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_tags.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_response_tags.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_tags.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_response_tags.go index 003680d5..d225dfaf 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_tags.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/create_record_response_tags.go @@ -4,10 +4,7 @@ package model - - type CreateRecordResponseTags struct { - // 标签ID TagId string `json:"tagId,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_body.go similarity index 76% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_body.go index 326b9abb..86974b24 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_body.go @@ -5,11 +5,11 @@ package model import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" ) type DeleteRecordBody struct { - position.Body + position.Body // 解析记录ID列表 RecordIdList []string `json:"recordIdList"` } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_body.go similarity index 77% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_body.go index e5614ac5..220b5929 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_body.go @@ -5,11 +5,11 @@ package model import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" ) type DeleteRecordOpenapiBody struct { - position.Body + position.Body // 待删除的解析记录ID请求体 RecordIdList []string `json:"recordIdList"` } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_request.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_request.go index 1cb684d6..33e89735 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_request.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_request.go @@ -4,9 +4,6 @@ package model - - type DeleteRecordOpenapiRequest struct { - DeleteRecordOpenapiBody *DeleteRecordOpenapiBody `json:"deleteRecordOpenapiBody,omitempty"` } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response.go similarity index 57% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response.go index ca528148..469afa87 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response.go @@ -4,19 +4,17 @@ package model - type DeleteRecordOpenapiResponseStateEnum string // List of State const ( - DeleteRecordOpenapiResponseStateEnumError DeleteRecordOpenapiResponseStateEnum = "ERROR" - DeleteRecordOpenapiResponseStateEnumException DeleteRecordOpenapiResponseStateEnum = "EXCEPTION" - DeleteRecordOpenapiResponseStateEnumForbidden DeleteRecordOpenapiResponseStateEnum = "FORBIDDEN" - DeleteRecordOpenapiResponseStateEnumOk DeleteRecordOpenapiResponseStateEnum = "OK" + DeleteRecordOpenapiResponseStateEnumError DeleteRecordOpenapiResponseStateEnum = "ERROR" + DeleteRecordOpenapiResponseStateEnumException DeleteRecordOpenapiResponseStateEnum = "EXCEPTION" + DeleteRecordOpenapiResponseStateEnumForbidden DeleteRecordOpenapiResponseStateEnum = "FORBIDDEN" + DeleteRecordOpenapiResponseStateEnumOk DeleteRecordOpenapiResponseStateEnum = "OK" ) type DeleteRecordOpenapiResponse struct { - RequestId string `json:"requestId,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response_body.go similarity index 71% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response_body.go index 67da3ab7..fb985000 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response_body.go @@ -4,17 +4,15 @@ package model - type DeleteRecordOpenapiResponseBodyCodeEnum string // List of Code const ( - DeleteRecordOpenapiResponseBodyCodeEnumError DeleteRecordOpenapiResponseBodyCodeEnum = "ERROR" - DeleteRecordOpenapiResponseBodyCodeEnumSuccess DeleteRecordOpenapiResponseBodyCodeEnum = "SUCCESS" + DeleteRecordOpenapiResponseBodyCodeEnumError DeleteRecordOpenapiResponseBodyCodeEnum = "ERROR" + DeleteRecordOpenapiResponseBodyCodeEnumSuccess DeleteRecordOpenapiResponseBodyCodeEnum = "SUCCESS" ) type DeleteRecordOpenapiResponseBody struct { - // 结果说明 Msg string `json:"msg,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_request.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_request.go index 678fd8ef..93af08f4 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_request.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_request.go @@ -4,9 +4,6 @@ package model - - type DeleteRecordRequest struct { - DeleteRecordBody *DeleteRecordBody `json:"deleteRecordBody,omitempty"` } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_response.go similarity index 60% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_response.go index 051d7105..7c14860d 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_response.go @@ -4,19 +4,17 @@ package model - type DeleteRecordResponseStateEnum string // List of State const ( - DeleteRecordResponseStateEnumError DeleteRecordResponseStateEnum = "ERROR" - DeleteRecordResponseStateEnumException DeleteRecordResponseStateEnum = "EXCEPTION" - DeleteRecordResponseStateEnumForbidden DeleteRecordResponseStateEnum = "FORBIDDEN" - DeleteRecordResponseStateEnumOk DeleteRecordResponseStateEnum = "OK" + DeleteRecordResponseStateEnumError DeleteRecordResponseStateEnum = "ERROR" + DeleteRecordResponseStateEnumException DeleteRecordResponseStateEnum = "EXCEPTION" + DeleteRecordResponseStateEnumForbidden DeleteRecordResponseStateEnum = "FORBIDDEN" + DeleteRecordResponseStateEnumOk DeleteRecordResponseStateEnum = "OK" ) type DeleteRecordResponse struct { - RequestId string `json:"requestId,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_response_body.go similarity index 73% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_response_body.go index 45320290..40bf0af8 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/delete_record_response_body.go @@ -4,17 +4,15 @@ package model - type DeleteRecordResponseBodyCodeEnum string // List of Code const ( - DeleteRecordResponseBodyCodeEnumError DeleteRecordResponseBodyCodeEnum = "ERROR" - DeleteRecordResponseBodyCodeEnumSuccess DeleteRecordResponseBodyCodeEnum = "SUCCESS" + DeleteRecordResponseBodyCodeEnumError DeleteRecordResponseBodyCodeEnum = "ERROR" + DeleteRecordResponseBodyCodeEnumSuccess DeleteRecordResponseBodyCodeEnum = "SUCCESS" ) type DeleteRecordResponseBody struct { - // 结果说明 Msg string `json:"msg,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_body.go similarity index 82% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_body.go index 5eadce7a..5509937c 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_body.go @@ -5,11 +5,11 @@ package model import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" ) type ListRecordBody struct { - position.Body + position.Body // 域名 DomainName string `json:"domainName"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_body.go similarity index 74% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_body.go index 63f5e13a..f33462ea 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_body.go @@ -5,11 +5,11 @@ package model import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" ) type ListRecordOpenapiBody struct { - position.Body + position.Body // 域名 DomainName string `json:"domainName"` } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_query.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_query.go similarity index 79% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_query.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_query.go index ee89a7f5..5e2a9162 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_query.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_query.go @@ -5,11 +5,11 @@ package model import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" ) type ListRecordOpenapiQuery struct { - position.Query + position.Query // 页大小 PageSize *int32 `json:"pageSize,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_request.go similarity index 99% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_request.go index 34540481..107e6b1f 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_request.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_request.go @@ -4,10 +4,7 @@ package model - - type ListRecordOpenapiRequest struct { - ListRecordOpenapiQuery *ListRecordOpenapiQuery `json:"listRecordOpenapiQuery,omitempty"` ListRecordOpenapiBody *ListRecordOpenapiBody `json:"listRecordOpenapiBody,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response.go similarity index 58% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response.go index 36646938..a752b1b4 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response.go @@ -4,19 +4,17 @@ package model - type ListRecordOpenapiResponseStateEnum string // List of State const ( - ListRecordOpenapiResponseStateEnumError ListRecordOpenapiResponseStateEnum = "ERROR" - ListRecordOpenapiResponseStateEnumException ListRecordOpenapiResponseStateEnum = "EXCEPTION" - ListRecordOpenapiResponseStateEnumForbidden ListRecordOpenapiResponseStateEnum = "FORBIDDEN" - ListRecordOpenapiResponseStateEnumOk ListRecordOpenapiResponseStateEnum = "OK" + ListRecordOpenapiResponseStateEnumError ListRecordOpenapiResponseStateEnum = "ERROR" + ListRecordOpenapiResponseStateEnumException ListRecordOpenapiResponseStateEnum = "EXCEPTION" + ListRecordOpenapiResponseStateEnumForbidden ListRecordOpenapiResponseStateEnum = "FORBIDDEN" + ListRecordOpenapiResponseStateEnumOk ListRecordOpenapiResponseStateEnum = "OK" ) type ListRecordOpenapiResponse struct { - RequestId string `json:"requestId,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_body.go similarity index 99% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_body.go index 8c6f7302..e37fdbe4 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_body.go @@ -4,10 +4,7 @@ package model - - type ListRecordOpenapiResponseBody struct { - // 当前页的具体数据列表 Data *[]ListRecordOpenapiResponseData `json:"data,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_data.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_data.go similarity index 56% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_data.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_data.go index 19981aa9..2594d14c 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_data.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_data.go @@ -4,40 +4,40 @@ package model - type ListRecordOpenapiResponseDataTypeEnum string // List of Type const ( - ListRecordOpenapiResponseDataTypeEnumA ListRecordOpenapiResponseDataTypeEnum = "A" - ListRecordOpenapiResponseDataTypeEnumAaaa ListRecordOpenapiResponseDataTypeEnum = "AAAA" - ListRecordOpenapiResponseDataTypeEnumCname ListRecordOpenapiResponseDataTypeEnum = "CNAME" - ListRecordOpenapiResponseDataTypeEnumMx ListRecordOpenapiResponseDataTypeEnum = "MX" - ListRecordOpenapiResponseDataTypeEnumTxt ListRecordOpenapiResponseDataTypeEnum = "TXT" - ListRecordOpenapiResponseDataTypeEnumNs ListRecordOpenapiResponseDataTypeEnum = "NS" - ListRecordOpenapiResponseDataTypeEnumSpf ListRecordOpenapiResponseDataTypeEnum = "SPF" - ListRecordOpenapiResponseDataTypeEnumSrv ListRecordOpenapiResponseDataTypeEnum = "SRV" - ListRecordOpenapiResponseDataTypeEnumCaa ListRecordOpenapiResponseDataTypeEnum = "CAA" - ListRecordOpenapiResponseDataTypeEnumCmauth ListRecordOpenapiResponseDataTypeEnum = "CMAUTH" + ListRecordOpenapiResponseDataTypeEnumA ListRecordOpenapiResponseDataTypeEnum = "A" + ListRecordOpenapiResponseDataTypeEnumAaaa ListRecordOpenapiResponseDataTypeEnum = "AAAA" + ListRecordOpenapiResponseDataTypeEnumCname ListRecordOpenapiResponseDataTypeEnum = "CNAME" + ListRecordOpenapiResponseDataTypeEnumMx ListRecordOpenapiResponseDataTypeEnum = "MX" + ListRecordOpenapiResponseDataTypeEnumTxt ListRecordOpenapiResponseDataTypeEnum = "TXT" + ListRecordOpenapiResponseDataTypeEnumNs ListRecordOpenapiResponseDataTypeEnum = "NS" + ListRecordOpenapiResponseDataTypeEnumSpf ListRecordOpenapiResponseDataTypeEnum = "SPF" + ListRecordOpenapiResponseDataTypeEnumSrv ListRecordOpenapiResponseDataTypeEnum = "SRV" + ListRecordOpenapiResponseDataTypeEnumCaa ListRecordOpenapiResponseDataTypeEnum = "CAA" + ListRecordOpenapiResponseDataTypeEnumCmauth ListRecordOpenapiResponseDataTypeEnum = "CMAUTH" ) + type ListRecordOpenapiResponseDataTimedStatusEnum string // List of TimedStatus const ( - ListRecordOpenapiResponseDataTimedStatusEnumDisabled ListRecordOpenapiResponseDataTimedStatusEnum = "DISABLED" - ListRecordOpenapiResponseDataTimedStatusEnumEnabled ListRecordOpenapiResponseDataTimedStatusEnum = "ENABLED" - ListRecordOpenapiResponseDataTimedStatusEnumTimed ListRecordOpenapiResponseDataTimedStatusEnum = "TIMED" + ListRecordOpenapiResponseDataTimedStatusEnumDisabled ListRecordOpenapiResponseDataTimedStatusEnum = "DISABLED" + ListRecordOpenapiResponseDataTimedStatusEnumEnabled ListRecordOpenapiResponseDataTimedStatusEnum = "ENABLED" + ListRecordOpenapiResponseDataTimedStatusEnumTimed ListRecordOpenapiResponseDataTimedStatusEnum = "TIMED" ) + type ListRecordOpenapiResponseDataStateEnum string // List of State const ( - ListRecordOpenapiResponseDataStateEnumDisabled ListRecordOpenapiResponseDataStateEnum = "DISABLED" - ListRecordOpenapiResponseDataStateEnumEnabled ListRecordOpenapiResponseDataStateEnum = "ENABLED" + ListRecordOpenapiResponseDataStateEnumDisabled ListRecordOpenapiResponseDataStateEnum = "DISABLED" + ListRecordOpenapiResponseDataStateEnumEnabled ListRecordOpenapiResponseDataStateEnum = "ENABLED" ) type ListRecordOpenapiResponseData struct { - // 主机头 Rr string `json:"rr,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_tags.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_tags.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_tags.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_tags.go index 867f667f..43230238 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_tags.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_tags.go @@ -4,10 +4,7 @@ package model - - type ListRecordOpenapiResponseTags struct { - // 标签ID TagId string `json:"tagId,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_query.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_query.go similarity index 79% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_query.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_query.go index df871a76..dca4b67c 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_query.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_query.go @@ -5,11 +5,11 @@ package model import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" ) type ListRecordQuery struct { - position.Query + position.Query // 页大小 PageSize *int32 `json:"pageSize,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_request.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_request.go index 5ff9df08..37e9512e 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_request.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_request.go @@ -4,10 +4,7 @@ package model - - type ListRecordRequest struct { - ListRecordBody *ListRecordBody `json:"listRecordBody,omitempty"` ListRecordQuery *ListRecordQuery `json:"listRecordQuery,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_response.go similarity index 60% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_response.go index b11f3d21..dde07b61 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_response.go @@ -4,19 +4,17 @@ package model - type ListRecordResponseStateEnum string // List of State const ( - ListRecordResponseStateEnumError ListRecordResponseStateEnum = "ERROR" - ListRecordResponseStateEnumException ListRecordResponseStateEnum = "EXCEPTION" - ListRecordResponseStateEnumForbidden ListRecordResponseStateEnum = "FORBIDDEN" - ListRecordResponseStateEnumOk ListRecordResponseStateEnum = "OK" + ListRecordResponseStateEnumError ListRecordResponseStateEnum = "ERROR" + ListRecordResponseStateEnumException ListRecordResponseStateEnum = "EXCEPTION" + ListRecordResponseStateEnumForbidden ListRecordResponseStateEnum = "FORBIDDEN" + ListRecordResponseStateEnumOk ListRecordResponseStateEnum = "OK" ) type ListRecordResponse struct { - RequestId string `json:"requestId,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_response_body.go similarity index 99% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_response_body.go index 0acf543d..3753d1aa 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_response_body.go @@ -4,10 +4,7 @@ package model - - type ListRecordResponseBody struct { - // 总页数 TotalPages *int32 `json:"totalPages,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_results.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_response_results.go similarity index 52% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_results.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_response_results.go index 7498fca3..1020dd56 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_results.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/list_record_response_results.go @@ -4,43 +4,43 @@ package model - type ListRecordResponseResultsTypeEnum string // List of Type const ( - ListRecordResponseResultsTypeEnumA ListRecordResponseResultsTypeEnum = "A" - ListRecordResponseResultsTypeEnumAaaa ListRecordResponseResultsTypeEnum = "AAAA" - ListRecordResponseResultsTypeEnumCaa ListRecordResponseResultsTypeEnum = "CAA" - ListRecordResponseResultsTypeEnumCmauth ListRecordResponseResultsTypeEnum = "CMAUTH" - ListRecordResponseResultsTypeEnumCname ListRecordResponseResultsTypeEnum = "CNAME" - ListRecordResponseResultsTypeEnumMx ListRecordResponseResultsTypeEnum = "MX" - ListRecordResponseResultsTypeEnumNs ListRecordResponseResultsTypeEnum = "NS" - ListRecordResponseResultsTypeEnumPtr ListRecordResponseResultsTypeEnum = "PTR" - ListRecordResponseResultsTypeEnumRp ListRecordResponseResultsTypeEnum = "RP" - ListRecordResponseResultsTypeEnumSpf ListRecordResponseResultsTypeEnum = "SPF" - ListRecordResponseResultsTypeEnumSrv ListRecordResponseResultsTypeEnum = "SRV" - ListRecordResponseResultsTypeEnumTxt ListRecordResponseResultsTypeEnum = "TXT" - ListRecordResponseResultsTypeEnumUrl ListRecordResponseResultsTypeEnum = "URL" + ListRecordResponseResultsTypeEnumA ListRecordResponseResultsTypeEnum = "A" + ListRecordResponseResultsTypeEnumAaaa ListRecordResponseResultsTypeEnum = "AAAA" + ListRecordResponseResultsTypeEnumCaa ListRecordResponseResultsTypeEnum = "CAA" + ListRecordResponseResultsTypeEnumCmauth ListRecordResponseResultsTypeEnum = "CMAUTH" + ListRecordResponseResultsTypeEnumCname ListRecordResponseResultsTypeEnum = "CNAME" + ListRecordResponseResultsTypeEnumMx ListRecordResponseResultsTypeEnum = "MX" + ListRecordResponseResultsTypeEnumNs ListRecordResponseResultsTypeEnum = "NS" + ListRecordResponseResultsTypeEnumPtr ListRecordResponseResultsTypeEnum = "PTR" + ListRecordResponseResultsTypeEnumRp ListRecordResponseResultsTypeEnum = "RP" + ListRecordResponseResultsTypeEnumSpf ListRecordResponseResultsTypeEnum = "SPF" + ListRecordResponseResultsTypeEnumSrv ListRecordResponseResultsTypeEnum = "SRV" + ListRecordResponseResultsTypeEnumTxt ListRecordResponseResultsTypeEnum = "TXT" + ListRecordResponseResultsTypeEnumUrl ListRecordResponseResultsTypeEnum = "URL" ) + type ListRecordResponseResultsTimedStatusEnum string // List of TimedStatus const ( - ListRecordResponseResultsTimedStatusEnumDisabled ListRecordResponseResultsTimedStatusEnum = "DISABLED" - ListRecordResponseResultsTimedStatusEnumEnabled ListRecordResponseResultsTimedStatusEnum = "ENABLED" - ListRecordResponseResultsTimedStatusEnumTimed ListRecordResponseResultsTimedStatusEnum = "TIMED" + ListRecordResponseResultsTimedStatusEnumDisabled ListRecordResponseResultsTimedStatusEnum = "DISABLED" + ListRecordResponseResultsTimedStatusEnumEnabled ListRecordResponseResultsTimedStatusEnum = "ENABLED" + ListRecordResponseResultsTimedStatusEnumTimed ListRecordResponseResultsTimedStatusEnum = "TIMED" ) + type ListRecordResponseResultsStateEnum string // List of State const ( - ListRecordResponseResultsStateEnumDisabled ListRecordResponseResultsStateEnum = "DISABLED" - ListRecordResponseResultsStateEnumEnabled ListRecordResponseResultsStateEnum = "ENABLED" + ListRecordResponseResultsStateEnumDisabled ListRecordResponseResultsStateEnum = "DISABLED" + ListRecordResponseResultsStateEnumEnabled ListRecordResponseResultsStateEnum = "ENABLED" ) type ListRecordResponseResults struct { - // 主机头 Rr string `json:"rr,omitempty"` diff --git a/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_body.go new file mode 100644 index 00000000..7db497b4 --- /dev/null +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_body.go @@ -0,0 +1,58 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) + +type ModifyRecordBodyTypeEnum string + +// List of Type +const ( + ModifyRecordBodyTypeEnumA ModifyRecordBodyTypeEnum = "A" + ModifyRecordBodyTypeEnumAaaa ModifyRecordBodyTypeEnum = "AAAA" + ModifyRecordBodyTypeEnumCaa ModifyRecordBodyTypeEnum = "CAA" + ModifyRecordBodyTypeEnumCmauth ModifyRecordBodyTypeEnum = "CMAUTH" + ModifyRecordBodyTypeEnumCname ModifyRecordBodyTypeEnum = "CNAME" + ModifyRecordBodyTypeEnumMx ModifyRecordBodyTypeEnum = "MX" + ModifyRecordBodyTypeEnumNs ModifyRecordBodyTypeEnum = "NS" + ModifyRecordBodyTypeEnumPtr ModifyRecordBodyTypeEnum = "PTR" + ModifyRecordBodyTypeEnumRp ModifyRecordBodyTypeEnum = "RP" + ModifyRecordBodyTypeEnumSpf ModifyRecordBodyTypeEnum = "SPF" + ModifyRecordBodyTypeEnumSrv ModifyRecordBodyTypeEnum = "SRV" + ModifyRecordBodyTypeEnumTxt ModifyRecordBodyTypeEnum = "TXT" + ModifyRecordBodyTypeEnumUrl ModifyRecordBodyTypeEnum = "URL" +) + +type ModifyRecordBody struct { + position.Body + // 解析记录ID + RecordId string `json:"recordId"` + + // 主机头 + Rr string `json:"rr,omitempty"` + + // 域名名称 + DomainName string `json:"domainName"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId,omitempty"` + + // MX优先级 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type ModifyRecordBodyTypeEnum `json:"type,omitempty"` + + // 缓存的生命周期 + Ttl *int32 `json:"ttl,omitempty"` + + // 记录值 + Value string `json:"value,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_body.go similarity index 50% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_body.go index 3bde8919..e7ef4cff 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_body.go @@ -5,26 +5,27 @@ package model import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" ) + type ModifyRecordOpenapiBodyTypeEnum string // List of Type const ( - ModifyRecordOpenapiBodyTypeEnumA ModifyRecordOpenapiBodyTypeEnum = "A" - ModifyRecordOpenapiBodyTypeEnumAaaa ModifyRecordOpenapiBodyTypeEnum = "AAAA" - ModifyRecordOpenapiBodyTypeEnumCname ModifyRecordOpenapiBodyTypeEnum = "CNAME" - ModifyRecordOpenapiBodyTypeEnumMx ModifyRecordOpenapiBodyTypeEnum = "MX" - ModifyRecordOpenapiBodyTypeEnumTxt ModifyRecordOpenapiBodyTypeEnum = "TXT" - ModifyRecordOpenapiBodyTypeEnumNs ModifyRecordOpenapiBodyTypeEnum = "NS" - ModifyRecordOpenapiBodyTypeEnumSpf ModifyRecordOpenapiBodyTypeEnum = "SPF" - ModifyRecordOpenapiBodyTypeEnumSrv ModifyRecordOpenapiBodyTypeEnum = "SRV" - ModifyRecordOpenapiBodyTypeEnumCaa ModifyRecordOpenapiBodyTypeEnum = "CAA" - ModifyRecordOpenapiBodyTypeEnumCmauth ModifyRecordOpenapiBodyTypeEnum = "CMAUTH" + ModifyRecordOpenapiBodyTypeEnumA ModifyRecordOpenapiBodyTypeEnum = "A" + ModifyRecordOpenapiBodyTypeEnumAaaa ModifyRecordOpenapiBodyTypeEnum = "AAAA" + ModifyRecordOpenapiBodyTypeEnumCname ModifyRecordOpenapiBodyTypeEnum = "CNAME" + ModifyRecordOpenapiBodyTypeEnumMx ModifyRecordOpenapiBodyTypeEnum = "MX" + ModifyRecordOpenapiBodyTypeEnumTxt ModifyRecordOpenapiBodyTypeEnum = "TXT" + ModifyRecordOpenapiBodyTypeEnumNs ModifyRecordOpenapiBodyTypeEnum = "NS" + ModifyRecordOpenapiBodyTypeEnumSpf ModifyRecordOpenapiBodyTypeEnum = "SPF" + ModifyRecordOpenapiBodyTypeEnumSrv ModifyRecordOpenapiBodyTypeEnum = "SRV" + ModifyRecordOpenapiBodyTypeEnumCaa ModifyRecordOpenapiBodyTypeEnum = "CAA" + ModifyRecordOpenapiBodyTypeEnumCmauth ModifyRecordOpenapiBodyTypeEnum = "CMAUTH" ) type ModifyRecordOpenapiBody struct { - position.Body + position.Body // 解析记录ID RecordId string `json:"recordId"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_request.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_request.go index fd1ffe00..ee118695 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_request.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_request.go @@ -4,9 +4,6 @@ package model - - type ModifyRecordOpenapiRequest struct { - ModifyRecordOpenapiBody *ModifyRecordOpenapiBody `json:"modifyRecordOpenapiBody,omitempty"` } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response.go similarity index 57% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response.go index 97d7552f..1c248f9c 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response.go @@ -4,19 +4,17 @@ package model - type ModifyRecordOpenapiResponseStateEnum string // List of State const ( - ModifyRecordOpenapiResponseStateEnumError ModifyRecordOpenapiResponseStateEnum = "ERROR" - ModifyRecordOpenapiResponseStateEnumException ModifyRecordOpenapiResponseStateEnum = "EXCEPTION" - ModifyRecordOpenapiResponseStateEnumForbidden ModifyRecordOpenapiResponseStateEnum = "FORBIDDEN" - ModifyRecordOpenapiResponseStateEnumOk ModifyRecordOpenapiResponseStateEnum = "OK" + ModifyRecordOpenapiResponseStateEnumError ModifyRecordOpenapiResponseStateEnum = "ERROR" + ModifyRecordOpenapiResponseStateEnumException ModifyRecordOpenapiResponseStateEnum = "EXCEPTION" + ModifyRecordOpenapiResponseStateEnumForbidden ModifyRecordOpenapiResponseStateEnum = "FORBIDDEN" + ModifyRecordOpenapiResponseStateEnumOk ModifyRecordOpenapiResponseStateEnum = "OK" ) type ModifyRecordOpenapiResponse struct { - RequestId string `json:"requestId,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_body.go similarity index 56% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_body.go index 6dfcec71..0ba12f70 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_body.go @@ -4,40 +4,40 @@ package model - type ModifyRecordOpenapiResponseBodyTypeEnum string // List of Type const ( - ModifyRecordOpenapiResponseBodyTypeEnumA ModifyRecordOpenapiResponseBodyTypeEnum = "A" - ModifyRecordOpenapiResponseBodyTypeEnumAaaa ModifyRecordOpenapiResponseBodyTypeEnum = "AAAA" - ModifyRecordOpenapiResponseBodyTypeEnumCname ModifyRecordOpenapiResponseBodyTypeEnum = "CNAME" - ModifyRecordOpenapiResponseBodyTypeEnumMx ModifyRecordOpenapiResponseBodyTypeEnum = "MX" - ModifyRecordOpenapiResponseBodyTypeEnumTxt ModifyRecordOpenapiResponseBodyTypeEnum = "TXT" - ModifyRecordOpenapiResponseBodyTypeEnumNs ModifyRecordOpenapiResponseBodyTypeEnum = "NS" - ModifyRecordOpenapiResponseBodyTypeEnumSpf ModifyRecordOpenapiResponseBodyTypeEnum = "SPF" - ModifyRecordOpenapiResponseBodyTypeEnumSrv ModifyRecordOpenapiResponseBodyTypeEnum = "SRV" - ModifyRecordOpenapiResponseBodyTypeEnumCaa ModifyRecordOpenapiResponseBodyTypeEnum = "CAA" - ModifyRecordOpenapiResponseBodyTypeEnumCmauth ModifyRecordOpenapiResponseBodyTypeEnum = "CMAUTH" + ModifyRecordOpenapiResponseBodyTypeEnumA ModifyRecordOpenapiResponseBodyTypeEnum = "A" + ModifyRecordOpenapiResponseBodyTypeEnumAaaa ModifyRecordOpenapiResponseBodyTypeEnum = "AAAA" + ModifyRecordOpenapiResponseBodyTypeEnumCname ModifyRecordOpenapiResponseBodyTypeEnum = "CNAME" + ModifyRecordOpenapiResponseBodyTypeEnumMx ModifyRecordOpenapiResponseBodyTypeEnum = "MX" + ModifyRecordOpenapiResponseBodyTypeEnumTxt ModifyRecordOpenapiResponseBodyTypeEnum = "TXT" + ModifyRecordOpenapiResponseBodyTypeEnumNs ModifyRecordOpenapiResponseBodyTypeEnum = "NS" + ModifyRecordOpenapiResponseBodyTypeEnumSpf ModifyRecordOpenapiResponseBodyTypeEnum = "SPF" + ModifyRecordOpenapiResponseBodyTypeEnumSrv ModifyRecordOpenapiResponseBodyTypeEnum = "SRV" + ModifyRecordOpenapiResponseBodyTypeEnumCaa ModifyRecordOpenapiResponseBodyTypeEnum = "CAA" + ModifyRecordOpenapiResponseBodyTypeEnumCmauth ModifyRecordOpenapiResponseBodyTypeEnum = "CMAUTH" ) + type ModifyRecordOpenapiResponseBodyTimedStatusEnum string // List of TimedStatus const ( - ModifyRecordOpenapiResponseBodyTimedStatusEnumDisabled ModifyRecordOpenapiResponseBodyTimedStatusEnum = "DISABLED" - ModifyRecordOpenapiResponseBodyTimedStatusEnumEnabled ModifyRecordOpenapiResponseBodyTimedStatusEnum = "ENABLED" - ModifyRecordOpenapiResponseBodyTimedStatusEnumTimed ModifyRecordOpenapiResponseBodyTimedStatusEnum = "TIMED" + ModifyRecordOpenapiResponseBodyTimedStatusEnumDisabled ModifyRecordOpenapiResponseBodyTimedStatusEnum = "DISABLED" + ModifyRecordOpenapiResponseBodyTimedStatusEnumEnabled ModifyRecordOpenapiResponseBodyTimedStatusEnum = "ENABLED" + ModifyRecordOpenapiResponseBodyTimedStatusEnumTimed ModifyRecordOpenapiResponseBodyTimedStatusEnum = "TIMED" ) + type ModifyRecordOpenapiResponseBodyStateEnum string // List of State const ( - ModifyRecordOpenapiResponseBodyStateEnumDisabled ModifyRecordOpenapiResponseBodyStateEnum = "DISABLED" - ModifyRecordOpenapiResponseBodyStateEnumEnabled ModifyRecordOpenapiResponseBodyStateEnum = "ENABLED" + ModifyRecordOpenapiResponseBodyStateEnumDisabled ModifyRecordOpenapiResponseBodyStateEnum = "DISABLED" + ModifyRecordOpenapiResponseBodyStateEnumEnabled ModifyRecordOpenapiResponseBodyStateEnum = "ENABLED" ) type ModifyRecordOpenapiResponseBody struct { - // 主机头 Rr string `json:"rr,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_tags.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_tags.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_tags.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_tags.go index 62c2e780..f3e75774 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_tags.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_tags.go @@ -4,10 +4,7 @@ package model - - type ModifyRecordOpenapiResponseTags struct { - // 标签ID TagId string `json:"tagId,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_request.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_request.go index d92abb44..486f596b 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_request.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_request.go @@ -4,9 +4,6 @@ package model - - type ModifyRecordRequest struct { - ModifyRecordBody *ModifyRecordBody `json:"modifyRecordBody,omitempty"` } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_response.go similarity index 59% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_response.go index 45b1ae0f..cec7d420 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_response.go @@ -4,19 +4,17 @@ package model - type ModifyRecordResponseStateEnum string // List of State const ( - ModifyRecordResponseStateEnumError ModifyRecordResponseStateEnum = "ERROR" - ModifyRecordResponseStateEnumException ModifyRecordResponseStateEnum = "EXCEPTION" - ModifyRecordResponseStateEnumForbidden ModifyRecordResponseStateEnum = "FORBIDDEN" - ModifyRecordResponseStateEnumOk ModifyRecordResponseStateEnum = "OK" + ModifyRecordResponseStateEnumError ModifyRecordResponseStateEnum = "ERROR" + ModifyRecordResponseStateEnumException ModifyRecordResponseStateEnum = "EXCEPTION" + ModifyRecordResponseStateEnumForbidden ModifyRecordResponseStateEnum = "FORBIDDEN" + ModifyRecordResponseStateEnumOk ModifyRecordResponseStateEnum = "OK" ) type ModifyRecordResponse struct { - RequestId string `json:"requestId,omitempty"` ErrorMessage string `json:"errorMessage,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response_body.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_response_body.go similarity index 52% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response_body.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_response_body.go index 3df09342..eb373895 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response_body.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1/model/modify_record_response_body.go @@ -4,35 +4,34 @@ package model - type ModifyRecordResponseBodyTypeEnum string // List of Type const ( - ModifyRecordResponseBodyTypeEnumA ModifyRecordResponseBodyTypeEnum = "A" - ModifyRecordResponseBodyTypeEnumAaaa ModifyRecordResponseBodyTypeEnum = "AAAA" - ModifyRecordResponseBodyTypeEnumCaa ModifyRecordResponseBodyTypeEnum = "CAA" - ModifyRecordResponseBodyTypeEnumCmauth ModifyRecordResponseBodyTypeEnum = "CMAUTH" - ModifyRecordResponseBodyTypeEnumCname ModifyRecordResponseBodyTypeEnum = "CNAME" - ModifyRecordResponseBodyTypeEnumMx ModifyRecordResponseBodyTypeEnum = "MX" - ModifyRecordResponseBodyTypeEnumNs ModifyRecordResponseBodyTypeEnum = "NS" - ModifyRecordResponseBodyTypeEnumPtr ModifyRecordResponseBodyTypeEnum = "PTR" - ModifyRecordResponseBodyTypeEnumRp ModifyRecordResponseBodyTypeEnum = "RP" - ModifyRecordResponseBodyTypeEnumSpf ModifyRecordResponseBodyTypeEnum = "SPF" - ModifyRecordResponseBodyTypeEnumSrv ModifyRecordResponseBodyTypeEnum = "SRV" - ModifyRecordResponseBodyTypeEnumTxt ModifyRecordResponseBodyTypeEnum = "TXT" - ModifyRecordResponseBodyTypeEnumUrl ModifyRecordResponseBodyTypeEnum = "URL" + ModifyRecordResponseBodyTypeEnumA ModifyRecordResponseBodyTypeEnum = "A" + ModifyRecordResponseBodyTypeEnumAaaa ModifyRecordResponseBodyTypeEnum = "AAAA" + ModifyRecordResponseBodyTypeEnumCaa ModifyRecordResponseBodyTypeEnum = "CAA" + ModifyRecordResponseBodyTypeEnumCmauth ModifyRecordResponseBodyTypeEnum = "CMAUTH" + ModifyRecordResponseBodyTypeEnumCname ModifyRecordResponseBodyTypeEnum = "CNAME" + ModifyRecordResponseBodyTypeEnumMx ModifyRecordResponseBodyTypeEnum = "MX" + ModifyRecordResponseBodyTypeEnumNs ModifyRecordResponseBodyTypeEnum = "NS" + ModifyRecordResponseBodyTypeEnumPtr ModifyRecordResponseBodyTypeEnum = "PTR" + ModifyRecordResponseBodyTypeEnumRp ModifyRecordResponseBodyTypeEnum = "RP" + ModifyRecordResponseBodyTypeEnumSpf ModifyRecordResponseBodyTypeEnum = "SPF" + ModifyRecordResponseBodyTypeEnumSrv ModifyRecordResponseBodyTypeEnum = "SRV" + ModifyRecordResponseBodyTypeEnumTxt ModifyRecordResponseBodyTypeEnum = "TXT" + ModifyRecordResponseBodyTypeEnumUrl ModifyRecordResponseBodyTypeEnum = "URL" ) + type ModifyRecordResponseBodyStateEnum string // List of State const ( - ModifyRecordResponseBodyStateEnumDisabled ModifyRecordResponseBodyStateEnum = "DISABLED" - ModifyRecordResponseBodyStateEnumEnabled ModifyRecordResponseBodyStateEnum = "ENABLED" + ModifyRecordResponseBodyStateEnumDisabled ModifyRecordResponseBodyStateEnum = "DISABLED" + ModifyRecordResponseBodyStateEnumEnabled ModifyRecordResponseBodyStateEnum = "ENABLED" ) type ModifyRecordResponseBody struct { - // 主机头 Rr string `json:"rr,omitempty"` diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_client.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/api_client.go similarity index 98% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_client.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/api_client.go index 8f73b499..af08cfd1 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_client.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/api_client.go @@ -6,7 +6,6 @@ import ( "encoding/xml" "errors" "fmt" - "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" "io" "io/ioutil" "mime/multipart" @@ -20,6 +19,8 @@ import ( "strings" "time" "unicode/utf8" + + "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" ) var ( @@ -47,8 +48,10 @@ const ( HEADER HttpRequestPosition = "Header" ) -const SdkPortalUrl = "/op-apim-portal/apim/request/sdk" -const SdkPortalGatewayUrl = "/api/query/openapi/apim/request/sdk" +const ( + SdkPortalUrl = "/op-apim-portal/apim/request/sdk" + SdkPortalGatewayUrl = "/api/query/openapi/apim/request/sdk" +) // NewAPIClient creates a new API client. func NewAPIClient() *APIClient { @@ -199,7 +202,7 @@ func buildHttpRequest(httpRequest *HttpRequest, config *config.Config) *HttpRequ if v.Kind() == reflect.Ptr { v = v.Elem() } - var flag = false + flag := false for i := 0; i < reqType.NumField(); i++ { fieldType := reqType.Field(i) value := v.FieldByName(fieldType.Name) @@ -270,7 +273,7 @@ func structToMap(value interface{}) map[string]interface{} { } func buildCall(httpRequest *HttpRequest) (request *http.Request) { - var url = "" + url := "" if len(httpRequest.Url) > 0 { url = httpRequest.Url + SdkPortalUrl } else { @@ -284,12 +287,11 @@ func buildCall(httpRequest *HttpRequest) (request *http.Request) { func prepareRequest(path string, method string, postBody interface{}, ) (httpRequest *http.Request, err error) { - var body *bytes.Buffer // Detect postBody type and post. if postBody != nil { - var contentType = detectContentType(postBody) + contentType := detectContentType(postBody) body, err = setBody(postBody, contentType) if err != nil { return nil, err diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_response.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/api_response.go similarity index 99% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_response.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/api_response.go index 89110877..eef07f5c 100644 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_response.go +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/api_response.go @@ -52,13 +52,11 @@ type APIPlatformResponseBody struct { } func NewAPIResponse(r *http.Response) *APIResponse { - response := &APIResponse{Response: r} return response } func NewAPIResponseWithError(errorMessage string) *APIResponse { - response := &APIResponse{Message: errorMessage} return response } diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/config/config.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/config/config.go similarity index 100% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/config/config.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/config/config.go diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/configuration.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/configuration.go similarity index 100% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/configuration.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/configuration.go diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/go.mod b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/go.mod similarity index 100% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/go.mod rename to internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/go.mod diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/http_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/http_request.go similarity index 100% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/http_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/http_request.go diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/open_api_request.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/open_api_request.go similarity index 100% rename from internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/open_api_request.go rename to internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/open_api_request.go diff --git a/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/position/http_position.go b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/position/http_position.go new file mode 100644 index 00000000..041b8e85 --- /dev/null +++ b/internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0/position/http_position.go @@ -0,0 +1,9 @@ +package position + +type Body struct{} + +type Query struct{} + +type Path struct{} + +type Header struct{} diff --git a/internal/pkg/vendors/dnsla-sdk/api.go b/internal/pkg/sdk3rd/dnsla/api.go similarity index 92% rename from internal/pkg/vendors/dnsla-sdk/api.go rename to internal/pkg/sdk3rd/dnsla/api.go index dae745c4..4b53a2ee 100644 --- a/internal/pkg/vendors/dnsla-sdk/api.go +++ b/internal/pkg/sdk3rd/dnsla/api.go @@ -31,6 +31,10 @@ func (c *Client) UpdateRecord(req *UpdateRecordRequest) (*UpdateRecordResponse, } func (c *Client) DeleteRecord(req *DeleteRecordRequest) (*DeleteRecordResponse, error) { + if req.Id == "" { + return nil, fmt.Errorf("dnsla api error: invalid parameter: Id") + } + resp := &DeleteRecordResponse{} err := c.sendRequestWithResult(http.MethodDelete, fmt.Sprintf("/record?id=%s", url.QueryEscape(req.Id)), req, resp) return resp, err diff --git a/internal/pkg/vendors/dnsla-sdk/client.go b/internal/pkg/sdk3rd/dnsla/client.go similarity index 97% rename from internal/pkg/vendors/dnsla-sdk/client.go rename to internal/pkg/sdk3rd/dnsla/client.go index 72b0ed3d..d557635b 100644 --- a/internal/pkg/vendors/dnsla-sdk/client.go +++ b/internal/pkg/sdk3rd/dnsla/client.go @@ -60,7 +60,7 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r if err != nil { return resp, fmt.Errorf("dnsla api error: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("dnsla api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return resp, fmt.Errorf("dnsla api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) } return resp, nil diff --git a/internal/pkg/vendors/dnsla-sdk/models.go b/internal/pkg/sdk3rd/dnsla/models.go similarity index 100% rename from internal/pkg/vendors/dnsla-sdk/models.go rename to internal/pkg/sdk3rd/dnsla/models.go diff --git a/internal/pkg/vendors/dogecloud-sdk/client.go b/internal/pkg/sdk3rd/dogecloud/client.go similarity index 99% rename from internal/pkg/vendors/dogecloud-sdk/client.go rename to internal/pkg/sdk3rd/dogecloud/client.go index dcca0b1d..965580c2 100644 --- a/internal/pkg/vendors/dogecloud-sdk/client.go +++ b/internal/pkg/sdk3rd/dogecloud/client.go @@ -1,4 +1,4 @@ -package dogecloudsdk +package dogecloudsdk import ( "crypto/hmac" diff --git a/internal/pkg/vendors/dogecloud-sdk/models.go b/internal/pkg/sdk3rd/dogecloud/models.go similarity index 96% rename from internal/pkg/vendors/dogecloud-sdk/models.go rename to internal/pkg/sdk3rd/dogecloud/models.go index 06507e0d..1642ad2d 100644 --- a/internal/pkg/vendors/dogecloud-sdk/models.go +++ b/internal/pkg/sdk3rd/dogecloud/models.go @@ -1,4 +1,4 @@ -package dogecloudsdk +package dogecloudsdk type BaseResponse struct { Code *int `json:"code,omitempty"` diff --git a/internal/pkg/vendors/edgio-sdk/applications/README.md b/internal/pkg/sdk3rd/edgio/README.md similarity index 100% rename from internal/pkg/vendors/edgio-sdk/applications/README.md rename to internal/pkg/sdk3rd/edgio/README.md diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/cdn_configuration.go b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/cdn_configuration.go similarity index 100% rename from internal/pkg/vendors/edgio-sdk/applications/v7/dtos/cdn_configuration.go rename to internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/cdn_configuration.go diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/environment.go b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/environment.go similarity index 100% rename from internal/pkg/vendors/edgio-sdk/applications/v7/dtos/environment.go rename to internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/environment.go diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/property.go b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/property.go similarity index 100% rename from internal/pkg/vendors/edgio-sdk/applications/v7/dtos/property.go rename to internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/property.go diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/purge.go b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/purge.go similarity index 100% rename from internal/pkg/vendors/edgio-sdk/applications/v7/dtos/purge.go rename to internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/purge.go diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/tls_cert.go b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/tls_cert.go similarity index 100% rename from internal/pkg/vendors/edgio-sdk/applications/v7/dtos/tls_cert.go rename to internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/dtos/tls_cert.go diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client.go b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client.go similarity index 99% rename from internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client.go rename to internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client.go index a03436fc..fb7b7cf7 100644 --- a/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client.go +++ b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client.go @@ -6,9 +6,8 @@ import ( "fmt" "time" + "github.com/Edgio/edgio-api/applications/v7/dtos" "github.com/go-resty/resty/v2" - - "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7/dtos" ) // AccessTokenResponse represents the response from the token endpoint. diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client_interface.go b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client_interface.go similarity index 94% rename from internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client_interface.go rename to internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client_interface.go index ea5fa958..645d73aa 100644 --- a/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client_interface.go +++ b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client_interface.go @@ -3,7 +3,7 @@ package edgio_api import ( "context" - "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7/dtos" + "github.com/Edgio/edgio-api/applications/v7/dtos" ) type EdgioClientInterface interface { diff --git a/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/go.mod b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/go.mod new file mode 100644 index 00000000..2c127948 --- /dev/null +++ b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/go.mod @@ -0,0 +1,3 @@ +module github.com/Edgio/edgio-api + +go 1.23.0 diff --git a/internal/pkg/vendors/gcore-sdk/common/endpoint.go b/internal/pkg/sdk3rd/gcore/common/endpoint.go similarity index 70% rename from internal/pkg/vendors/gcore-sdk/common/endpoint.go rename to internal/pkg/sdk3rd/gcore/common/endpoint.go index d4032da3..5e027951 100644 --- a/internal/pkg/vendors/gcore-sdk/common/endpoint.go +++ b/internal/pkg/sdk3rd/gcore/common/endpoint.go @@ -1,3 +1,3 @@ -package common +package common const BASE_URL = "https://api.gcore.com" diff --git a/internal/pkg/vendors/gcore-sdk/common/signer.go b/internal/pkg/sdk3rd/gcore/common/signer.go similarity index 95% rename from internal/pkg/vendors/gcore-sdk/common/signer.go rename to internal/pkg/sdk3rd/gcore/common/signer.go index bc66ee09..b0aff7e6 100644 --- a/internal/pkg/vendors/gcore-sdk/common/signer.go +++ b/internal/pkg/sdk3rd/gcore/common/signer.go @@ -1,4 +1,4 @@ -package common +package common import ( "net/http" diff --git a/internal/pkg/vendors/gname-sdk/api.go b/internal/pkg/sdk3rd/gname/api.go similarity index 100% rename from internal/pkg/vendors/gname-sdk/api.go rename to internal/pkg/sdk3rd/gname/api.go diff --git a/internal/pkg/vendors/gname-sdk/client.go b/internal/pkg/sdk3rd/gname/client.go similarity index 97% rename from internal/pkg/vendors/gname-sdk/client.go rename to internal/pkg/sdk3rd/gname/client.go index 0a2238f2..017a3315 100644 --- a/internal/pkg/vendors/gname-sdk/client.go +++ b/internal/pkg/sdk3rd/gname/client.go @@ -82,7 +82,7 @@ func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, if err != nil { return resp, fmt.Errorf("gname api error: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("gname api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return resp, fmt.Errorf("gname api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) } return resp, nil diff --git a/internal/pkg/vendors/gname-sdk/models.go b/internal/pkg/sdk3rd/gname/models.go similarity index 100% rename from internal/pkg/vendors/gname-sdk/models.go rename to internal/pkg/sdk3rd/gname/models.go diff --git a/internal/pkg/sdk3rd/goedge/api.go b/internal/pkg/sdk3rd/goedge/api.go new file mode 100644 index 00000000..67ee9194 --- /dev/null +++ b/internal/pkg/sdk3rd/goedge/api.go @@ -0,0 +1,46 @@ +package goedge + +import ( + "encoding/json" + "fmt" + "net/http" + "time" +) + +func (c *Client) getAccessToken() error { + req := &getAPIAccessTokenRequest{ + Type: c.apiUserType, + AccessKeyId: c.accessKeyId, + AccessKey: c.accessKey, + } + res, err := c.sendRequest(http.MethodPost, "/APIAccessTokenService/getAPIAccessToken", req) + if err != nil { + return err + } + + resp := &getAPIAccessTokenResponse{} + if err := json.Unmarshal(res.Body(), &resp); err != nil { + return fmt.Errorf("goedge api error: failed to parse response: %w", err) + } else if resp.GetCode() != 200 { + return fmt.Errorf("goedge get access token failed: code: %d, message: %s", resp.GetCode(), resp.GetMessage()) + } + + c.accessTokenMtx.Lock() + c.accessToken = resp.Data.Token + c.accessTokenExp = time.Unix(resp.Data.ExpiresAt, 0) + c.accessTokenMtx.Unlock() + + return nil +} + +func (c *Client) UpdateSSLCert(req *UpdateSSLCertRequest) (*UpdateSSLCertResponse, error) { + if c.accessToken == "" || c.accessTokenExp.Before(time.Now()) { + if err := c.getAccessToken(); err != nil { + return nil, err + } + } + + resp := &UpdateSSLCertResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/SSLCertService/updateSSLCert", req, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/goedge/client.go b/internal/pkg/sdk3rd/goedge/client.go new file mode 100644 index 00000000..96291fb3 --- /dev/null +++ b/internal/pkg/sdk3rd/goedge/client.go @@ -0,0 +1,103 @@ +package goedge + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "net/http" + "strings" + "sync" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + apiHost string + apiUserType string + accessKeyId string + accessKey string + + accessToken string + accessTokenExp time.Time + accessTokenMtx sync.Mutex + + client *resty.Client +} + +func NewClient(apiHost, apiUserType, accessKeyId, accessKey string) *Client { + client := resty.New() + + return &Client{ + apiHost: strings.TrimRight(apiHost, "/"), + apiUserType: apiUserType, + accessKeyId: accessKeyId, + accessKey: accessKey, + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) WithTLSConfig(config *tls.Config) *Client { + c.client.SetTLSClientConfig(config) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R().SetBasicAuth(c.accessKeyId, c.accessKey) + req.Method = method + req.URL = c.apiHost + path + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req. + SetQueryParams(qs). + SetHeader("X-Edge-Access-Token", c.accessToken) + } else { + req = req. + SetHeader("Content-Type", "application/json"). + SetHeader("X-Edge-Access-Token", c.accessToken). + SetBody(params) + } + + resp, err := req.Send() + if err != nil { + return resp, fmt.Errorf("goedge api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("goedge api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + if resp != nil { + json.Unmarshal(resp.Body(), &result) + } + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("goedge api error: failed to parse response: %w", err) + } else if errcode := result.GetCode(); errcode != 200 { + return fmt.Errorf("goedge api error: %d - %s", errcode, result.GetMessage()) + } + + return nil +} diff --git a/internal/pkg/sdk3rd/goedge/models.go b/internal/pkg/sdk3rd/goedge/models.go new file mode 100644 index 00000000..d19bb558 --- /dev/null +++ b/internal/pkg/sdk3rd/goedge/models.go @@ -0,0 +1,52 @@ +package goedge + +type BaseResponse interface { + GetCode() int32 + GetMessage() string +} + +type baseResponse struct { + Code int32 `json:"code"` + Message string `json:"message"` +} + +func (r *baseResponse) GetCode() int32 { + return r.Code +} + +func (r *baseResponse) GetMessage() string { + return r.Message +} + +type getAPIAccessTokenRequest struct { + Type string `json:"type"` + AccessKeyId string `json:"accessKeyId"` + AccessKey string `json:"accessKey"` +} + +type getAPIAccessTokenResponse struct { + baseResponse + Data *struct { + Token string `json:"token"` + ExpiresAt int64 `json:"expiresAt"` + } `json:"data,omitempty"` +} + +type UpdateSSLCertRequest struct { + SSLCertId int64 `json:"sslCertId"` + IsOn bool `json:"isOn"` + Name string `json:"name"` + Description string `json:"description"` + ServerName string `json:"serverName"` + IsCA bool `json:"isCA"` + CertData string `json:"certData"` + KeyData string `json:"keyData"` + TimeBeginAt int64 `json:"timeBeginAt"` + TimeEndAt int64 `json:"timeEndAt"` + DNSNames []string `json:"dnsNames"` + CommonNames []string `json:"commonNames"` +} + +type UpdateSSLCertResponse struct { + baseResponse +} diff --git a/internal/pkg/vendors/qiniu-sdk/auth.go b/internal/pkg/sdk3rd/qiniu/auth.go similarity index 100% rename from internal/pkg/vendors/qiniu-sdk/auth.go rename to internal/pkg/sdk3rd/qiniu/auth.go diff --git a/internal/pkg/vendors/qiniu-sdk/client.go b/internal/pkg/sdk3rd/qiniu/client.go similarity index 99% rename from internal/pkg/vendors/qiniu-sdk/client.go rename to internal/pkg/sdk3rd/qiniu/client.go index 8f9d10e9..ece26b9a 100644 --- a/internal/pkg/vendors/qiniu-sdk/client.go +++ b/internal/pkg/sdk3rd/qiniu/client.go @@ -1,4 +1,4 @@ -package qiniusdk +package qiniusdk import ( "context" diff --git a/internal/pkg/vendors/qiniu-sdk/models.go b/internal/pkg/sdk3rd/qiniu/models.go similarity index 98% rename from internal/pkg/vendors/qiniu-sdk/models.go rename to internal/pkg/sdk3rd/qiniu/models.go index dceca028..1f643c35 100644 --- a/internal/pkg/vendors/qiniu-sdk/models.go +++ b/internal/pkg/sdk3rd/qiniu/models.go @@ -1,4 +1,4 @@ -package qiniusdk +package qiniusdk type BaseResponse struct { Code *int `json:"code,omitempty"` diff --git a/internal/pkg/sdk3rd/rainyun/api.go b/internal/pkg/sdk3rd/rainyun/api.go new file mode 100644 index 00000000..d4a9135e --- /dev/null +++ b/internal/pkg/sdk3rd/rainyun/api.go @@ -0,0 +1,38 @@ +package rainyunsdk + +import ( + "fmt" + "net/http" +) + +func (c *Client) SslCenterList(req *SslCenterListRequest) (*SslCenterListResponse, error) { + resp := &SslCenterListResponse{} + err := c.sendRequestWithResult(http.MethodGet, "/product/sslcenter", req, resp) + return resp, err +} + +func (c *Client) SslCenterGet(id int32) (*SslCenterGetResponse, error) { + if id == 0 { + return nil, fmt.Errorf("rainyun api error: invalid parameter: id") + } + + resp := &SslCenterGetResponse{} + err := c.sendRequestWithResult(http.MethodGet, fmt.Sprintf("/product/sslcenter/%d", id), nil, resp) + return resp, err +} + +func (c *Client) SslCenterCreate(req *SslCenterCreateRequest) (*SslCenterCreateResponse, error) { + resp := &SslCenterCreateResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/product/sslcenter/", req, resp) + return resp, err +} + +func (c *Client) RcdnInstanceSslBind(id int32, req *RcdnInstanceSslBindRequest) (*RcdnInstanceSslBindResponse, error) { + if id == 0 { + return nil, fmt.Errorf("rainyun api error: invalid parameter: id") + } + + resp := &RcdnInstanceSslBindResponse{} + err := c.sendRequestWithResult(http.MethodPost, fmt.Sprintf("/product/rcdn/instance/%d/ssl_bind", id), req, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/rainyun/client.go b/internal/pkg/sdk3rd/rainyun/client.go new file mode 100644 index 00000000..e710128b --- /dev/null +++ b/internal/pkg/sdk3rd/rainyun/client.go @@ -0,0 +1,74 @@ +package rainyunsdk + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + apiKey string + + client *resty.Client +} + +func NewClient(apiKey string) *Client { + client := resty.New() + + return &Client{ + apiKey: apiKey, + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R().SetHeader("x-api-key", c.apiKey) + req.Method = method + req.URL = "https://api.v2.rainyun.com" + path + if strings.EqualFold(method, http.MethodGet) { + if params != nil { + jsonb, _ := json.Marshal(params) + req = req.SetQueryParam("options", string(jsonb)) + } + } else { + req = req. + SetHeader("Content-Type", "application/json"). + SetBody(params) + } + + resp, err := req.Send() + if err != nil { + return resp, fmt.Errorf("rainyun api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("rainyun api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + if resp != nil { + json.Unmarshal(resp.Body(), &result) + } + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("rainyun api error: failed to parse response: %w", err) + } else if errcode := result.GetCode(); errcode/100 != 2 { + return fmt.Errorf("rainyun api error: %d - %s", errcode, result.GetMessage()) + } + + return nil +} diff --git a/internal/pkg/sdk3rd/rainyun/models.go b/internal/pkg/sdk3rd/rainyun/models.go new file mode 100644 index 00000000..bcc44963 --- /dev/null +++ b/internal/pkg/sdk3rd/rainyun/models.go @@ -0,0 +1,83 @@ +package rainyunsdk + +type BaseResponse interface { + GetCode() int32 + GetMessage() string +} + +type baseResponse struct { + Code *int32 `json:"code,omitempty"` + Message *string `json:"message,omitempty"` +} + +func (r *baseResponse) GetCode() int32 { + if r.Code != nil { + return *r.Code + } + return 0 +} + +func (r *baseResponse) GetMessage() string { + if r.Message != nil { + return *r.Message + } + return "" +} + +type SslCenterListFilters struct { + Domain *string `json:"Domain,omitempty"` +} + +type SslCenterListRequest struct { + Filters *SslCenterListFilters `json:"columnFilters,omitempty"` + Sort []*string `json:"sort,omitempty"` + Page *int32 `json:"page,omitempty"` + PerPage *int32 `json:"perPage,omitempty"` +} + +type SslCenterListResponse struct { + baseResponse + Data *struct { + TotalRecords int32 `json:"TotalRecords"` + Records []*struct { + ID int32 `json:"ID"` + UID int32 `json:"UID"` + Domain string `json:"Domain"` + Issuer string `json:"Issuer"` + StartDate int64 `json:"StartDate"` + ExpireDate int64 `json:"ExpDate"` + UploadTime int64 `json:"UploadTime"` + } `json:"Records"` + } `json:"data,omitempty"` +} + +type SslCenterGetResponse struct { + baseResponse + Data *struct { + Cert string `json:"Cert"` + Key string `json:"Key"` + Domain string `json:"DomainName"` + Issuer string `json:"Issuer"` + StartDate int64 `json:"StartDate"` + ExpireDate int64 `json:"ExpDate"` + RemainDays int32 `json:"RemainDays"` + } `json:"data,omitempty"` +} + +type SslCenterCreateRequest struct { + Cert string `json:"cert"` + Key string `json:"key"` +} + +type SslCenterCreateResponse struct { + baseResponse +} + +type RcdnInstanceSslBindRequest struct { + CertId int32 `json:"cert_id"` + Domains []string `json:"domains"` +} + +type RcdnInstanceSslBindResponse struct { + baseResponse +} diff --git a/internal/pkg/vendors/safeline-sdk/api.go b/internal/pkg/sdk3rd/safeline/api.go similarity index 100% rename from internal/pkg/vendors/safeline-sdk/api.go rename to internal/pkg/sdk3rd/safeline/api.go diff --git a/internal/pkg/vendors/safeline-sdk/client.go b/internal/pkg/sdk3rd/safeline/client.go similarity index 97% rename from internal/pkg/vendors/safeline-sdk/client.go rename to internal/pkg/sdk3rd/safeline/client.go index ade8acfa..c56e3485 100644 --- a/internal/pkg/vendors/safeline-sdk/client.go +++ b/internal/pkg/sdk3rd/safeline/client.go @@ -47,7 +47,7 @@ func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, if err != nil { return resp, fmt.Errorf("safeline api error: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("safeline api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return resp, fmt.Errorf("safeline api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) } return resp, nil diff --git a/internal/pkg/vendors/safeline-sdk/models.go b/internal/pkg/sdk3rd/safeline/models.go similarity index 100% rename from internal/pkg/vendors/safeline-sdk/models.go rename to internal/pkg/sdk3rd/safeline/models.go diff --git a/internal/pkg/vendors/ucloud-sdk/ufile/apis.go b/internal/pkg/sdk3rd/ucloud/ufile/apis.go similarity index 98% rename from internal/pkg/vendors/ucloud-sdk/ufile/apis.go rename to internal/pkg/sdk3rd/ucloud/ufile/apis.go index 009405fa..350b23a2 100644 --- a/internal/pkg/vendors/ucloud-sdk/ufile/apis.go +++ b/internal/pkg/sdk3rd/ucloud/ufile/apis.go @@ -1,4 +1,4 @@ -package ufile +package ufile import ( "github.com/ucloud/ucloud-sdk-go/ucloud/request" diff --git a/internal/pkg/vendors/ucloud-sdk/ufile/client.go b/internal/pkg/sdk3rd/ucloud/ufile/client.go similarity index 95% rename from internal/pkg/vendors/ucloud-sdk/ufile/client.go rename to internal/pkg/sdk3rd/ucloud/ufile/client.go index ab1f4d2e..aee06624 100644 --- a/internal/pkg/vendors/ucloud-sdk/ufile/client.go +++ b/internal/pkg/sdk3rd/ucloud/ufile/client.go @@ -1,4 +1,4 @@ -package ufile +package ufile import ( "github.com/ucloud/ucloud-sdk-go/ucloud" diff --git a/internal/pkg/vendors/ucloud-sdk/ussl/apis.go b/internal/pkg/sdk3rd/ucloud/ussl/apis.go similarity index 99% rename from internal/pkg/vendors/ucloud-sdk/ussl/apis.go rename to internal/pkg/sdk3rd/ucloud/ussl/apis.go index 5d0c6e32..d9ec7674 100644 --- a/internal/pkg/vendors/ucloud-sdk/ussl/apis.go +++ b/internal/pkg/sdk3rd/ucloud/ussl/apis.go @@ -1,4 +1,4 @@ -package ussl +package ussl import ( "github.com/ucloud/ucloud-sdk-go/ucloud/request" diff --git a/internal/pkg/vendors/ucloud-sdk/ussl/client.go b/internal/pkg/sdk3rd/ucloud/ussl/client.go similarity index 95% rename from internal/pkg/vendors/ucloud-sdk/ussl/client.go rename to internal/pkg/sdk3rd/ucloud/ussl/client.go index 6d92a204..95876543 100644 --- a/internal/pkg/vendors/ucloud-sdk/ussl/client.go +++ b/internal/pkg/sdk3rd/ucloud/ussl/client.go @@ -1,4 +1,4 @@ -package ussl +package ussl import ( "github.com/ucloud/ucloud-sdk-go/ucloud" diff --git a/internal/pkg/vendors/ucloud-sdk/ussl/models.go b/internal/pkg/sdk3rd/ucloud/ussl/models.go similarity index 98% rename from internal/pkg/vendors/ucloud-sdk/ussl/models.go rename to internal/pkg/sdk3rd/ucloud/ussl/models.go index 50343d9f..2826f2cb 100644 --- a/internal/pkg/vendors/ucloud-sdk/ussl/models.go +++ b/internal/pkg/sdk3rd/ucloud/ussl/models.go @@ -1,4 +1,4 @@ -package ussl +package ussl type CertificateListItem struct { CertificateID int diff --git a/internal/pkg/vendors/upyun-sdk/console/api.go b/internal/pkg/sdk3rd/upyun/console/api.go similarity index 100% rename from internal/pkg/vendors/upyun-sdk/console/api.go rename to internal/pkg/sdk3rd/upyun/console/api.go diff --git a/internal/pkg/vendors/upyun-sdk/console/client.go b/internal/pkg/sdk3rd/upyun/console/client.go similarity index 96% rename from internal/pkg/vendors/upyun-sdk/console/client.go rename to internal/pkg/sdk3rd/upyun/console/client.go index 7b968f53..56e7a86e 100644 --- a/internal/pkg/vendors/upyun-sdk/console/client.go +++ b/internal/pkg/sdk3rd/upyun/console/client.go @@ -11,8 +11,9 @@ import ( ) type Client struct { - username string - password string + username string + password string + loginCookie string client *resty.Client @@ -64,7 +65,7 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r if err != nil { return resp, fmt.Errorf("upyun api error: failed to send request: %w", err) } else if resp.IsError() { - return resp, fmt.Errorf("upyun api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return resp, fmt.Errorf("upyun api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) } return resp, nil diff --git a/internal/pkg/vendors/upyun-sdk/console/models.go b/internal/pkg/sdk3rd/upyun/console/models.go similarity index 100% rename from internal/pkg/vendors/upyun-sdk/console/models.go rename to internal/pkg/sdk3rd/upyun/console/models.go diff --git a/internal/pkg/vendors/volcengine-sdk/certcenter/api_import_certificate.go b/internal/pkg/sdk3rd/volcengine/certcenter/api_import_certificate.go similarity index 99% rename from internal/pkg/vendors/volcengine-sdk/certcenter/api_import_certificate.go rename to internal/pkg/sdk3rd/volcengine/certcenter/api_import_certificate.go index ece842ed..6212508a 100644 --- a/internal/pkg/vendors/volcengine-sdk/certcenter/api_import_certificate.go +++ b/internal/pkg/sdk3rd/volcengine/certcenter/api_import_certificate.go @@ -1,4 +1,4 @@ -package certcenter +package certcenter import ( "github.com/volcengine/volcengine-go-sdk/volcengine" diff --git a/internal/pkg/vendors/volcengine-sdk/certcenter/interface.go b/internal/pkg/sdk3rd/volcengine/certcenter/interface.go similarity index 95% rename from internal/pkg/vendors/volcengine-sdk/certcenter/interface.go rename to internal/pkg/sdk3rd/volcengine/certcenter/interface.go index cebe2b73..75ba3729 100644 --- a/internal/pkg/vendors/volcengine-sdk/certcenter/interface.go +++ b/internal/pkg/sdk3rd/volcengine/certcenter/interface.go @@ -1,4 +1,4 @@ -package certcenter +package certcenter import ( "github.com/volcengine/volcengine-go-sdk/volcengine" diff --git a/internal/pkg/vendors/volcengine-sdk/certcenter/service.go b/internal/pkg/sdk3rd/volcengine/certcenter/service.go similarity index 98% rename from internal/pkg/vendors/volcengine-sdk/certcenter/service.go rename to internal/pkg/sdk3rd/volcengine/certcenter/service.go index 2feb8c28..494e2995 100644 --- a/internal/pkg/vendors/volcengine-sdk/certcenter/service.go +++ b/internal/pkg/sdk3rd/volcengine/certcenter/service.go @@ -1,4 +1,4 @@ -package certcenter +package certcenter import ( "github.com/volcengine/volcengine-go-sdk/volcengine" diff --git a/internal/pkg/sdk3rd/wangsu/cdn/api.go b/internal/pkg/sdk3rd/wangsu/cdn/api.go new file mode 100644 index 00000000..0da647c8 --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/cdn/api.go @@ -0,0 +1,70 @@ +package cdn + +import ( + "fmt" + "net/http" + "net/url" + + "github.com/go-resty/resty/v2" +) + +func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) { + resp := &CreateCertificateResponse{} + r, err := c.client.SendRequestWithResult(http.MethodPost, "/cdn/certificates", req, resp, func(r *resty.Request) { + r.SetHeader("x-cnc-timestamp", fmt.Sprintf("%d", req.Timestamp)) + }) + if err != nil { + return resp, err + } + + resp.CertificateUrl = r.Header().Get("Location") + return resp, err +} + +func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { + if certificateId == "" { + return nil, fmt.Errorf("wangsu api error: invalid parameter: certificateId") + } + + resp := &UpdateCertificateResponse{} + r, err := c.client.SendRequestWithResult(http.MethodPatch, fmt.Sprintf("/cdn/certificates/%s", url.PathEscape(certificateId)), req, resp, func(r *resty.Request) { + r.SetHeader("x-cnc-timestamp", fmt.Sprintf("%d", req.Timestamp)) + }) + if err != nil { + return resp, err + } + + resp.CertificateUrl = r.Header().Get("Location") + return resp, err +} + +func (c *Client) GetHostnameDetail(hostname string) (*GetHostnameDetailResponse, error) { + if hostname == "" { + return nil, fmt.Errorf("wangsu api error: invalid parameter: hostname") + } + + resp := &GetHostnameDetailResponse{} + _, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/hostnames/%s", url.PathEscape(hostname)), nil, resp) + return resp, err +} + +func (c *Client) CreateDeploymentTask(req *CreateDeploymentTaskRequest) (*CreateDeploymentTaskResponse, error) { + resp := &CreateDeploymentTaskResponse{} + r, err := c.client.SendRequestWithResult(http.MethodPost, "/cdn/deploymentTasks", req, resp) + if err != nil { + return resp, err + } + + resp.DeploymentTaskUrl = r.Header().Get("Location") + return resp, err +} + +func (c *Client) GetDeploymentTaskDetail(deploymentTaskId string) (*GetDeploymentTaskDetailResponse, error) { + if deploymentTaskId == "" { + return nil, fmt.Errorf("wangsu api error: invalid parameter: deploymentTaskId") + } + + resp := &GetDeploymentTaskDetailResponse{} + _, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/deploymentTasks/%s", url.PathEscape(deploymentTaskId)), nil, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/wangsu/cdn/client.go b/internal/pkg/sdk3rd/wangsu/cdn/client.go new file mode 100644 index 00000000..ac53e171 --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/cdn/client.go @@ -0,0 +1,20 @@ +package cdn + +import ( + "time" + + "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/openapi" +) + +type Client struct { + client *openapi.Client +} + +func NewClient(accessKey, secretKey string) *Client { + return &Client{client: openapi.NewClient(accessKey, secretKey)} +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.WithTimeout(timeout) + return c +} diff --git a/internal/pkg/sdk3rd/wangsu/cdn/models.go b/internal/pkg/sdk3rd/wangsu/cdn/models.go new file mode 100644 index 00000000..a9a9ec74 --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/cdn/models.go @@ -0,0 +1,108 @@ +package cdn + +import ( + "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/openapi" +) + +type baseResponse struct { + RequestId *string `json:"-"` + Code *string `json:"code,omitempty"` + Message *string `json:"message,omitempty"` +} + +var _ openapi.Result = (*baseResponse)(nil) + +func (r *baseResponse) SetRequestId(requestId string) { + r.RequestId = &requestId +} + +type CertificateVersion struct { + Comments *string `json:"comments,omitempty"` + PrivateKey *string `json:"privateKey,omitempty"` + Certificate *string `json:"certificate,omitempty"` + ChainCert *string `json:"chainCert,omitempty"` + IdentificationInfo *CertificateVersionIdentificationInfo `json:"identificationInfo,omitempty"` +} + +type CertificateVersionIdentificationInfo struct { + Country *string `json:"country,omitempty"` + State *string `json:"state,omitempty"` + City *string `json:"city,omitempty"` + Company *string `json:"company,omitempty"` + Department *string `json:"department,omitempty"` + CommonName *string `json:"commonName,omitempty" required:"true"` + Email *string `json:"email,omitempty"` + SubjectAlternativeNames *[]string `json:"subjectAlternativeNames,omitempty" required:"true"` +} + +type CreateCertificateRequest struct { + Timestamp int64 `json:"-"` + Name *string `json:"name,omitempty" required:"true"` + Description *string `json:"description,omitempty"` + AutoRenew *string `json:"autoRenew,omitempty"` + ForceRenew *bool `json:"forceRenew,omitempty"` + NewVersion *CertificateVersion `json:"newVersion,omitempty" required:"true"` +} + +type CreateCertificateResponse struct { + baseResponse + CertificateUrl string `json:"location,omitempty"` +} + +type UpdateCertificateRequest struct { + Timestamp int64 `json:"-"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + AutoRenew *string `json:"autoRenew,omitempty"` + ForceRenew *bool `json:"forceRenew,omitempty"` + NewVersion *CertificateVersion `json:"newVersion,omitempty" required:"true"` +} + +type UpdateCertificateResponse struct { + baseResponse + CertificateUrl string `json:"location,omitempty"` +} + +type HostnameProperty struct { + PropertyId string `json:"propertyId"` + Version int32 `json:"version"` + CertificateId *string `json:"certificateId,omitempty"` +} + +type GetHostnameDetailResponse struct { + baseResponse + Hostname string `json:"hostname"` + PropertyInProduction *HostnameProperty `json:"propertyInProduction,omitempty"` + PropertyInStaging *HostnameProperty `json:"propertyInStaging,omitempty"` +} + +type DeploymentTaskAction struct { + Action *string `json:"action,omitempty" required:"true"` + PropertyId *string `json:"propertyId,omitempty"` + CertificateId *string `json:"certificateId,omitempty"` + Version *int32 `json:"version,omitempty"` +} + +type CreateDeploymentTaskRequest struct { + Name *string `json:"name,omitempty"` + Target *string `json:"target,omitempty" required:"true"` + Actions *[]DeploymentTaskAction `json:"actions,omitempty" required:"true"` + Webhook *string `json:"webhook,omitempty"` +} + +type CreateDeploymentTaskResponse struct { + baseResponse + DeploymentTaskUrl string `json:"location,omitempty"` +} + +type GetDeploymentTaskDetailResponse struct { + baseResponse + Name string `json:"name"` + Target string `json:"target"` + Actions []DeploymentTaskAction `json:"actions"` + Status string `json:"status"` + StatusDetails string `json:"statusDetails"` + SubmissionTime string `json:"submissionTime"` + FinishTime string `json:"finishTime"` + ApiRequestId string `json:"apiRequestId"` +} diff --git a/internal/pkg/sdk3rd/wangsu/openapi/client.go b/internal/pkg/sdk3rd/wangsu/openapi/client.go new file mode 100644 index 00000000..d63c20c7 --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/openapi/client.go @@ -0,0 +1,190 @@ +package openapi + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + accessKey string + secretKey string + + client *resty.Client +} + +type Result interface { + SetRequestId(requestId string) +} + +func NewClient(accessKey, secretKey string) *Client { + client := resty.New(). + SetBaseURL("https://open.chinanetcenter.com"). + SetHeader("Host", "open.chinanetcenter.com"). + SetHeader("Accept", "application/json"). + SetHeader("Content-Type", "application/json"). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + // Step 1: Get request method + method := req.Method + method = strings.ToUpper(method) + + // Step 2: Get request path + path := "/" + if req.URL != nil { + path = req.URL.Path + } + + // Step 3: Get unencoded query string + queryString := "" + if method != http.MethodPost && req.URL != nil { + queryString = req.URL.RawQuery + + s, err := url.QueryUnescape(queryString) + if err != nil { + return err + } + + queryString = s + } + + // Step 4: Get canonical headers & signed headers + canonicalHeaders := "" + + "content-type:" + strings.TrimSpace(strings.ToLower(req.Header.Get("Content-Type"))) + "\n" + + "host:" + strings.TrimSpace(strings.ToLower(req.Header.Get("Host"))) + "\n" + signedHeaders := "content-type;host" + + // Step 5: Get request payload + payload := "" + if method != http.MethodGet && req.Body != nil { + reader, err := req.GetBody() + if err != nil { + return err + } + + defer reader.Close() + + payloadb, err := io.ReadAll(reader) + if err != nil { + return err + } + + payload = string(payloadb) + } + hashedPayload := sha256.Sum256([]byte(payload)) + hashedPayloadHex := strings.ToLower(hex.EncodeToString(hashedPayload[:])) + + // Step 6: Get timestamp + var reqtime time.Time + timestampString := req.Header.Get("x-cnc-timestamp") + if timestampString == "" { + reqtime = time.Now().UTC() + timestampString = fmt.Sprintf("%d", reqtime.Unix()) + } else { + timestamp, err := strconv.ParseInt(timestampString, 10, 64) + if err != nil { + return err + } + reqtime = time.Unix(timestamp, 0).UTC() + } + + // Step 7: Get canonical request string + canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", method, path, queryString, canonicalHeaders, signedHeaders, hashedPayloadHex) + hashedCanonicalRequest := sha256.Sum256([]byte(canonicalRequest)) + hashedCanonicalRequestHex := strings.ToLower(hex.EncodeToString(hashedCanonicalRequest[:])) + + // Step 8: String to sign + const SignAlgorithmHeader = "CNC-HMAC-SHA256" + stringToSign := fmt.Sprintf("%s\n%s\n%s", SignAlgorithmHeader, timestampString, hashedCanonicalRequestHex) + hmac := hmac.New(sha256.New, []byte(secretKey)) + hmac.Write([]byte(stringToSign)) + sign := hmac.Sum(nil) + signHex := strings.ToLower(hex.EncodeToString(sign)) + + // Step 9: Add headers to request + req.Header.Set("x-cnc-accesskey", accessKey) + req.Header.Set("x-cnc-timestamp", timestampString) + req.Header.Set("x-cnc-auth-method", "AKSK") + req.Header.Set("Authorization", fmt.Sprintf("%s Credential=%s, SignedHeaders=%s, Signature=%s", SignAlgorithmHeader, accessKey, signedHeaders, signHex)) + req.Header.Set("Date", reqtime.Format("Mon, 02 Jan 2006 15:04:05 GMT")) + + return nil + }) + + return &Client{ + accessKey: accessKey, + secretKey: secretKey, + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}, configureReq ...func(req *resty.Request)) (*resty.Response, error) { + req := c.client.R() + req.Method = method + req.URL = path + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req.SetQueryParams(qs) + } else { + req = req.SetBody(params) + } + + for _, fn := range configureReq { + fn(req) + } + + resp, err := req.Send() + if err != nil { + return resp, fmt.Errorf("wangsu api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("wangsu api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} + +func (c *Client) SendRequestWithResult(method string, path string, params interface{}, result Result, configureReq ...func(req *resty.Request)) (*resty.Response, error) { + resp, err := c.sendRequest(method, path, params, configureReq...) + if err != nil { + if resp != nil { + json.Unmarshal(resp.Body(), &result) + result.SetRequestId(resp.Header().Get("x-cnc-request-id")) + } + return resp, err + } + + respBody := resp.Body() + if len(respBody) != 0 { + if err := json.Unmarshal(respBody, &result); err != nil { + return resp, fmt.Errorf("wangsu api error: failed to parse response: %w", err) + } + } + + result.SetRequestId(resp.Header().Get("x-cnc-request-id")) + return resp, nil +} diff --git a/internal/pkg/utils/certutil/common.go b/internal/pkg/utils/cert/common.go similarity index 97% rename from internal/pkg/utils/certutil/common.go rename to internal/pkg/utils/cert/common.go index 409233b6..08a7cea2 100644 --- a/internal/pkg/utils/certutil/common.go +++ b/internal/pkg/utils/cert/common.go @@ -1,4 +1,4 @@ -package certutil +package certutil import ( "crypto/x509" diff --git a/internal/pkg/utils/certutil/converter.go b/internal/pkg/utils/cert/converter.go similarity index 75% rename from internal/pkg/utils/certutil/converter.go rename to internal/pkg/utils/cert/converter.go index ccbe6360..b726e86d 100644 --- a/internal/pkg/utils/certutil/converter.go +++ b/internal/pkg/utils/cert/converter.go @@ -1,12 +1,11 @@ -package certutil +package certutil import ( "crypto/ecdsa" "crypto/x509" "encoding/pem" "errors" - - xerrors "github.com/pkg/errors" + "fmt" ) // 将 x509.Certificate 对象转换为 PEM 编码的字符串。 @@ -15,9 +14,9 @@ import ( // - cert: x509.Certificate 对象。 // // 出参: -// - certPem: 证书 PEM 内容。 +// - certPEM: 证书 PEM 内容。 // - err: 错误。 -func ConvertCertificateToPEM(cert *x509.Certificate) (certPem string, err error) { +func ConvertCertificateToPEM(cert *x509.Certificate) (certPEM string, err error) { if cert == nil { return "", errors.New("`cert` is nil") } @@ -36,16 +35,16 @@ func ConvertCertificateToPEM(cert *x509.Certificate) (certPem string, err error) // - privkey: ecdsa.PrivateKey 对象。 // // 出参: -// - privkeyPem: 私钥 PEM 内容。 +// - privkeyPEM: 私钥 PEM 内容。 // - err: 错误。 -func ConvertECPrivateKeyToPEM(privkey *ecdsa.PrivateKey) (privkeyPem string, err error) { +func ConvertECPrivateKeyToPEM(privkey *ecdsa.PrivateKey) (privkeyPEM string, err error) { if privkey == nil { return "", errors.New("`privkey` is nil") } data, err := x509.MarshalECPrivateKey(privkey) if err != nil { - return "", xerrors.Wrap(err, "failed to marshal EC private key") + return "", fmt.Errorf("failed to marshal EC private key: %w", err) } block := &pem.Block{ diff --git a/internal/pkg/utils/certutil/extractor.go b/internal/pkg/utils/cert/extractor.go similarity index 53% rename from internal/pkg/utils/certutil/extractor.go rename to internal/pkg/utils/cert/extractor.go index d4e7ee6e..110f4772 100644 --- a/internal/pkg/utils/certutil/extractor.go +++ b/internal/pkg/utils/cert/extractor.go @@ -1,4 +1,4 @@ -package certutil +package certutil import ( "encoding/pem" @@ -8,15 +8,15 @@ import ( // 从 PEM 编码的证书字符串解析并提取服务器证书和中间证书。 // // 入参: -// - certPem: 证书 PEM 内容。 +// - certPEM: 证书 PEM 内容。 // // 出参: -// - serverCertPem: 服务器证书的 PEM 内容。 -// - interCertPem: 中间证书的 PEM 内容。 +// - serverCertPEM: 服务器证书的 PEM 内容。 +// - interCertPEM: 中间证书的 PEM 内容。 // - err: 错误。 -func ExtractCertificatesFromPEM(certPem string) (serverCertPem string, interCertPem string, err error) { +func ExtractCertificatesFromPEM(certPEM string) (serverCertPEM string, interCertPEM string, err error) { pemBlocks := make([]*pem.Block, 0) - pemData := []byte(certPem) + pemData := []byte(certPEM) for { block, rest := pem.Decode(pemData) if block == nil || block.Type != "CERTIFICATE" { @@ -27,22 +27,22 @@ func ExtractCertificatesFromPEM(certPem string) (serverCertPem string, interCert pemData = rest } - serverCertPem = "" - interCertPem = "" + serverCertPEM = "" + interCertPEM = "" if len(pemBlocks) == 0 { return "", "", errors.New("failed to decode PEM block") } if len(pemBlocks) > 0 { - serverCertPem = string(pem.EncodeToMemory(pemBlocks[0])) + serverCertPEM = string(pem.EncodeToMemory(pemBlocks[0])) } if len(pemBlocks) > 1 { for i := 1; i < len(pemBlocks); i++ { - interCertPem += string(pem.EncodeToMemory(pemBlocks[i])) + interCertPEM += string(pem.EncodeToMemory(pemBlocks[i])) } } - return serverCertPem, interCertPem, nil + return serverCertPEM, interCertPEM, nil } diff --git a/internal/pkg/utils/certutil/parser.go b/internal/pkg/utils/cert/parser.go similarity index 69% rename from internal/pkg/utils/certutil/parser.go rename to internal/pkg/utils/cert/parser.go index e93ce583..eb743f78 100644 --- a/internal/pkg/utils/certutil/parser.go +++ b/internal/pkg/utils/cert/parser.go @@ -1,4 +1,4 @@ -package certutil +package certutil import ( "crypto" @@ -7,22 +7,22 @@ import ( "crypto/x509" "encoding/pem" "errors" + "fmt" "github.com/go-acme/lego/v4/certcrypto" - xerrors "github.com/pkg/errors" ) // 从 PEM 编码的证书字符串解析并返回一个 x509.Certificate 对象。 // PEM 内容可能是包含多张证书的证书链,但只返回第一个证书(即服务器证书)。 // // 入参: -// - certPem: 证书 PEM 内容。 +// - certPEM: 证书 PEM 内容。 // // 出参: // - cert: x509.Certificate 对象。 // - err: 错误。 -func ParseCertificateFromPEM(certPem string) (cert *x509.Certificate, err error) { - pemData := []byte(certPem) +func ParseCertificateFromPEM(certPEM string) (cert *x509.Certificate, err error) { + pemData := []byte(certPEM) block, _ := pem.Decode(pemData) if block == nil { @@ -31,7 +31,7 @@ func ParseCertificateFromPEM(certPem string) (cert *x509.Certificate, err error) cert, err = x509.ParseCertificate(block.Bytes) if err != nil { - return nil, xerrors.Wrap(err, "failed to parse certificate") + return nil, fmt.Errorf("failed to parse certificate: %w", err) } return cert, nil @@ -40,26 +40,26 @@ func ParseCertificateFromPEM(certPem string) (cert *x509.Certificate, err error) // 从 PEM 编码的私钥字符串解析并返回一个 crypto.PrivateKey 对象。 // // 入参: -// - privkeyPem: 私钥 PEM 内容。 +// - privkeyPEM: 私钥 PEM 内容。 // // 出参: // - privkey: crypto.PrivateKey 对象,可能是 rsa.PrivateKey、ecdsa.PrivateKey 或 ed25519.PrivateKey。 // - err: 错误。 -func ParsePrivateKeyFromPEM(privkeyPem string) (privkey crypto.PrivateKey, err error) { - pemData := []byte(privkeyPem) +func ParsePrivateKeyFromPEM(privkeyPEM string) (privkey crypto.PrivateKey, err error) { + pemData := []byte(privkeyPEM) return certcrypto.ParsePEMPrivateKey(pemData) } // 从 PEM 编码的私钥字符串解析并返回一个 ecdsa.PrivateKey 对象。 // // 入参: -// - privkeyPem: 私钥 PEM 内容。 +// - privkeyPEM: 私钥 PEM 内容。 // // 出参: // - privkey: ecdsa.PrivateKey 对象。 // - err: 错误。 -func ParseECPrivateKeyFromPEM(privkeyPem string) (privkey *ecdsa.PrivateKey, err error) { - pemData := []byte(privkeyPem) +func ParseECPrivateKeyFromPEM(privkeyPEM string) (privkey *ecdsa.PrivateKey, err error) { + pemData := []byte(privkeyPEM) block, _ := pem.Decode(pemData) if block == nil { @@ -68,7 +68,7 @@ func ParseECPrivateKeyFromPEM(privkeyPem string) (privkey *ecdsa.PrivateKey, err privkey, err = x509.ParseECPrivateKey(block.Bytes) if err != nil { - return nil, xerrors.Wrap(err, "failed to parse private key") + return nil, fmt.Errorf("failed to parse private key: %w", err) } return privkey, nil @@ -77,13 +77,13 @@ func ParseECPrivateKeyFromPEM(privkeyPem string) (privkey *ecdsa.PrivateKey, err // 从 PEM 编码的私钥字符串解析并返回一个 rsa.PrivateKey 对象。 // // 入参: -// - privkeyPem: 私钥 PEM 内容。 +// - privkeyPEM: 私钥 PEM 内容。 // // 出参: // - privkey: rsa.PrivateKey 对象。 // - err: 错误。 -func ParsePKCS1PrivateKeyFromPEM(privkeyPem string) (privkey *rsa.PrivateKey, err error) { - pemData := []byte(privkeyPem) +func ParsePKCS1PrivateKeyFromPEM(privkeyPEM string) (privkey *rsa.PrivateKey, err error) { + pemData := []byte(privkeyPEM) block, _ := pem.Decode(pemData) if block == nil { @@ -92,7 +92,7 @@ func ParsePKCS1PrivateKeyFromPEM(privkeyPem string) (privkey *rsa.PrivateKey, er privkey, err = x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { - return nil, xerrors.Wrap(err, "failed to parse private key") + return nil, fmt.Errorf("failed to parse private key: %w", err) } return privkey, nil diff --git a/internal/pkg/utils/certutil/transformer.go b/internal/pkg/utils/cert/transformer.go similarity index 76% rename from internal/pkg/utils/certutil/transformer.go rename to internal/pkg/utils/cert/transformer.go index f48641ae..74690013 100644 --- a/internal/pkg/utils/certutil/transformer.go +++ b/internal/pkg/utils/cert/transformer.go @@ -1,4 +1,4 @@ -package certutil +package certutil import ( "bytes" @@ -13,20 +13,20 @@ import ( // 将 PEM 编码的证书字符串转换为 PFX 格式。 // // 入参: -// - certPem: 证书 PEM 内容。 -// - privkeyPem: 私钥 PEM 内容。 +// - certPEM: 证书 PEM 内容。 +// - privkeyPEM: 私钥 PEM 内容。 // - pfxPassword: PFX 导出密码。 // // 出参: // - data: PFX 格式的证书数据。 // - err: 错误。 -func TransformCertificateFromPEMToPFX(certPem string, privkeyPem string, pfxPassword string) ([]byte, error) { - cert, err := ParseCertificateFromPEM(certPem) +func TransformCertificateFromPEMToPFX(certPEM string, privkeyPEM string, pfxPassword string) ([]byte, error) { + cert, err := ParseCertificateFromPEM(certPEM) if err != nil { return nil, err } - privkey, err := ParsePrivateKeyFromPEM(privkeyPem) + privkey, err := ParsePrivateKeyFromPEM(privkeyPEM) if err != nil { return nil, err } @@ -42,8 +42,8 @@ func TransformCertificateFromPEMToPFX(certPem string, privkeyPem string, pfxPass // 将 PEM 编码的证书字符串转换为 JKS 格式。 // // 入参: -// - certPem: 证书 PEM 内容。 -// - privkeyPem: 私钥 PEM 内容。 +// - certPEM: 证书 PEM 内容。 +// - privkeyPEM: 私钥 PEM 内容。 // - jksAlias: JKS 别名。 // - jksKeypass: JKS 密钥密码。 // - jksStorepass: JKS 存储密码。 @@ -51,13 +51,13 @@ func TransformCertificateFromPEMToPFX(certPem string, privkeyPem string, pfxPass // 出参: // - data: JKS 格式的证书数据。 // - err: 错误。 -func TransformCertificateFromPEMToJKS(certPem string, privkeyPem string, jksAlias string, jksKeypass string, jksStorepass string) ([]byte, error) { - certBlock, _ := pem.Decode([]byte(certPem)) +func TransformCertificateFromPEMToJKS(certPEM string, privkeyPEM string, jksAlias string, jksKeypass string, jksStorepass string) ([]byte, error) { + certBlock, _ := pem.Decode([]byte(certPEM)) if certBlock == nil { return nil, errors.New("failed to decode certificate PEM") } - privkeyBlock, _ := pem.Decode([]byte(privkeyPem)) + privkeyBlock, _ := pem.Decode([]byte(privkeyPEM)) if privkeyBlock == nil { return nil, errors.New("failed to decode private key PEM") } diff --git a/internal/pkg/utils/fileutil/io.go b/internal/pkg/utils/file/io.go similarity index 80% rename from internal/pkg/utils/fileutil/io.go rename to internal/pkg/utils/file/io.go index 336f524d..58b5b970 100644 --- a/internal/pkg/utils/fileutil/io.go +++ b/internal/pkg/utils/file/io.go @@ -1,10 +1,9 @@ -package fileutil +package fileutil import ( + "fmt" "os" "path/filepath" - - xerrors "github.com/pkg/errors" ) // 与 [Write] 类似,但写入的是字符串内容。 @@ -34,18 +33,18 @@ func Write(path string, data []byte) error { err := os.MkdirAll(dir, os.ModePerm) if err != nil { - return xerrors.Wrap(err, "failed to create directory") + return fmt.Errorf("failed to create directory: %w", err) } file, err := os.Create(path) if err != nil { - return xerrors.Wrap(err, "failed to create file") + return fmt.Errorf("failed to create file: %w", err) } defer file.Close() _, err = file.Write(data) if err != nil { - return xerrors.Wrap(err, "failed to write file") + return fmt.Errorf("failed to write file: %w", err) } return nil diff --git a/internal/pkg/utils/http/parser.go b/internal/pkg/utils/http/parser.go new file mode 100644 index 00000000..872fb6b5 --- /dev/null +++ b/internal/pkg/utils/http/parser.go @@ -0,0 +1,33 @@ +package httputil + +import ( + "bufio" + "net/http" + "net/textproto" + "strings" +) + +// 从表示 HTTP 标头的字符串解析并返回一个 http.Header 对象。 +// +// 入参: +// - headers: 表示 HTTP 标头的字符串。 +// +// 出参: +// - header: http.Header 对象。 +// - err: 错误。 +func ParseHeaders(headers string) (http.Header, error) { + str := strings.TrimSpace(headers) + "\r\n\r\n" + if len(str) == 4 { + return make(http.Header), nil + } + + br := bufio.NewReader(strings.NewReader(str)) + tp := textproto.NewReader(br) + + mimeHeader, err := tp.ReadMIMEHeader() + if err != nil { + return nil, err + } + + return http.Header(mimeHeader), err +} diff --git a/internal/pkg/utils/maputil/getter.go b/internal/pkg/utils/map/getter.go similarity index 80% rename from internal/pkg/utils/maputil/getter.go rename to internal/pkg/utils/map/getter.go index 36561381..f30f6d33 100644 --- a/internal/pkg/utils/maputil/getter.go +++ b/internal/pkg/utils/map/getter.go @@ -1,4 +1,4 @@ -package maputil +package maputil import ( "strconv" @@ -74,6 +74,18 @@ func GetOrDefaultInt32(dict map[string]any, key string, defaultValue int32) int3 } } + if result, ok := value.(int64); ok { + if result != 0 { + return int32(result) + } + } + + if result, ok := value.(int); ok { + if result != 0 { + return int32(result) + } + } + // 兼容字符串类型的值 if str, ok := value.(string); ok { if result, err := strconv.ParseInt(str, 10, 32); err == nil { @@ -126,6 +138,12 @@ func GetOrDefaultInt64(dict map[string]any, key string, defaultValue int64) int6 } } + if result, ok := value.(int); ok { + if result != 0 { + return int64(result) + } + } + // 兼容字符串类型的值 if str, ok := value.(string); ok { if result, err := strconv.ParseInt(str, 10, 64); err == nil { @@ -180,3 +198,37 @@ func GetOrDefaultBool(dict map[string]any, key string, defaultValue bool) bool { return defaultValue } + +// 以 `map[string]V` 形式从字典中获取指定键的值。 +// +// 入参: +// - dict: 字典。 +// - key: 键。 +// +// 出参: +// - 字典中键对应的 `map[string]V` 对象。 +func GetKVMap[V any](dict map[string]any, key string) map[string]V { + if dict == nil { + return make(map[string]V) + } + + if val, ok := dict[key]; ok { + if result, ok := val.(map[string]V); ok { + return result + } + } + + return make(map[string]V) +} + +// 以 `map[string]any` 形式从字典中获取指定键的值。 +// +// 入参: +// - dict: 字典。 +// - key: 键。 +// +// 出参: +// - 字典中键对应的 `map[string]any` 对象。 +func GetKVMapAny(dict map[string]any, key string) map[string]any { + return GetKVMap[any](dict, key) +} diff --git a/internal/pkg/utils/maputil/marshal.go b/internal/pkg/utils/map/marshal.go similarity index 97% rename from internal/pkg/utils/maputil/marshal.go rename to internal/pkg/utils/map/marshal.go index 64bd97e6..236b32e2 100644 --- a/internal/pkg/utils/maputil/marshal.go +++ b/internal/pkg/utils/map/marshal.go @@ -1,4 +1,4 @@ -package maputil +package maputil import ( mapstructure "github.com/go-viper/mapstructure/v2" diff --git a/internal/pkg/utils/sliceutil/slices.go b/internal/pkg/utils/slice/slice.go similarity index 98% rename from internal/pkg/utils/sliceutil/slices.go rename to internal/pkg/utils/slice/slice.go index ba63b82a..f1c7fc0f 100644 --- a/internal/pkg/utils/sliceutil/slices.go +++ b/internal/pkg/utils/slice/slice.go @@ -1,4 +1,4 @@ -package sliceutil +package sliceutil // 创建给定切片一部分的浅拷贝,其包含通过所提供函数实现的测试的所有元素。 // diff --git a/internal/pkg/utils/typeutil/types.go b/internal/pkg/utils/type/assert.go similarity index 54% rename from internal/pkg/utils/typeutil/types.go rename to internal/pkg/utils/type/assert.go index 4e5d3711..509a77cc 100644 --- a/internal/pkg/utils/typeutil/types.go +++ b/internal/pkg/utils/type/assert.go @@ -1,4 +1,4 @@ -package typeutil +package typeutil import "reflect" @@ -24,29 +24,3 @@ func IsNil(obj any) bool { return false } - -// 将对象转换为指针。 -// -// 入参: -// - 待转换的对象。 -// -// 出参: -// - 返回对象的指针。 -func ToPtr[T any](v T) (p *T) { - return &v -} - -// 将指针转换为对象。 -// -// 入参: -// - 待转换的指针。 -// -// 出参: -// - 返回指针指向的对象。如果指针为空,则返回对象的零值。 -func ToObj[T any](p *T) (v T) { - if p == nil { - return v - } - - return *p -} diff --git a/internal/pkg/utils/type/cast.go b/internal/pkg/utils/type/cast.go new file mode 100644 index 00000000..684e262e --- /dev/null +++ b/internal/pkg/utils/type/cast.go @@ -0,0 +1,27 @@ +package typeutil + +// 将对象转换为指针。 +// +// 入参: +// - 待转换的对象。 +// +// 出参: +// - 返回对象的指针。 +func ToPtr[T any](v T) (p *T) { + return &v +} + +// 将指针转换为对象。 +// +// 入参: +// - 待转换的指针。 +// +// 出参: +// - 返回指针指向的对象。如果指针为空,则返回对象的零值。 +func ToVal[T any](p *T) (v T) { + if IsNil(p) { + return v + } + + return *p +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_body.go deleted file mode 100644 index 1175cde9..00000000 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_body.go +++ /dev/null @@ -1,54 +0,0 @@ -// @Title Golang SDK Client -// @Description This code is auto generated -// @Author Ecloud SDK - -package model - -import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" -) -type CreateRecordBodyTypeEnum string - -// List of Type -const ( - CreateRecordBodyTypeEnumA CreateRecordBodyTypeEnum = "A" - CreateRecordBodyTypeEnumAaaa CreateRecordBodyTypeEnum = "AAAA" - CreateRecordBodyTypeEnumCaa CreateRecordBodyTypeEnum = "CAA" - CreateRecordBodyTypeEnumCmauth CreateRecordBodyTypeEnum = "CMAUTH" - CreateRecordBodyTypeEnumCname CreateRecordBodyTypeEnum = "CNAME" - CreateRecordBodyTypeEnumMx CreateRecordBodyTypeEnum = "MX" - CreateRecordBodyTypeEnumNs CreateRecordBodyTypeEnum = "NS" - CreateRecordBodyTypeEnumPtr CreateRecordBodyTypeEnum = "PTR" - CreateRecordBodyTypeEnumRp CreateRecordBodyTypeEnum = "RP" - CreateRecordBodyTypeEnumSpf CreateRecordBodyTypeEnum = "SPF" - CreateRecordBodyTypeEnumSrv CreateRecordBodyTypeEnum = "SRV" - CreateRecordBodyTypeEnumTxt CreateRecordBodyTypeEnum = "TXT" - CreateRecordBodyTypeEnumUrl CreateRecordBodyTypeEnum = "URL" -) - -type CreateRecordBody struct { - position.Body - // 主机头 - Rr string `json:"rr"` - - // 域名名称 - DomainName string `json:"domainName"` - - // 备注 - Description string `json:"description,omitempty"` - - // 线路ID - LineId string `json:"lineId"` - - // MX优先级,若“记录类型”选择”MX”,则需要配置该参数,默认是5 - MxPri *int32 `json:"mxPri,omitempty"` - - // 记录类型 - Type CreateRecordBodyTypeEnum `json:"type"` - - // 缓存的生命周期,默认可配置600s - Ttl *int32 `json:"ttl,omitempty"` - - // 记录值 - Value string `json:"value"` -} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_body.go deleted file mode 100644 index ab772c09..00000000 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_body.go +++ /dev/null @@ -1,57 +0,0 @@ -// @Title Golang SDK Client -// @Description This code is auto generated -// @Author Ecloud SDK - -package model - -import ( - "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" -) -type ModifyRecordBodyTypeEnum string - -// List of Type -const ( - ModifyRecordBodyTypeEnumA ModifyRecordBodyTypeEnum = "A" - ModifyRecordBodyTypeEnumAaaa ModifyRecordBodyTypeEnum = "AAAA" - ModifyRecordBodyTypeEnumCaa ModifyRecordBodyTypeEnum = "CAA" - ModifyRecordBodyTypeEnumCmauth ModifyRecordBodyTypeEnum = "CMAUTH" - ModifyRecordBodyTypeEnumCname ModifyRecordBodyTypeEnum = "CNAME" - ModifyRecordBodyTypeEnumMx ModifyRecordBodyTypeEnum = "MX" - ModifyRecordBodyTypeEnumNs ModifyRecordBodyTypeEnum = "NS" - ModifyRecordBodyTypeEnumPtr ModifyRecordBodyTypeEnum = "PTR" - ModifyRecordBodyTypeEnumRp ModifyRecordBodyTypeEnum = "RP" - ModifyRecordBodyTypeEnumSpf ModifyRecordBodyTypeEnum = "SPF" - ModifyRecordBodyTypeEnumSrv ModifyRecordBodyTypeEnum = "SRV" - ModifyRecordBodyTypeEnumTxt ModifyRecordBodyTypeEnum = "TXT" - ModifyRecordBodyTypeEnumUrl ModifyRecordBodyTypeEnum = "URL" -) - -type ModifyRecordBody struct { - position.Body - // 解析记录ID - RecordId string `json:"recordId"` - - // 主机头 - Rr string `json:"rr,omitempty"` - - // 域名名称 - DomainName string `json:"domainName"` - - // 备注 - Description string `json:"description,omitempty"` - - // 线路ID - LineId string `json:"lineId,omitempty"` - - // MX优先级 - MxPri *int32 `json:"mxPri,omitempty"` - - // 记录类型 - Type ModifyRecordBodyTypeEnum `json:"type,omitempty"` - - // 缓存的生命周期 - Ttl *int32 `json:"ttl,omitempty"` - - // 记录值 - Value string `json:"value,omitempty"` -} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/position/http_position.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/position/http_position.go deleted file mode 100644 index 7c2eec1c..00000000 --- a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/position/http_position.go +++ /dev/null @@ -1,13 +0,0 @@ -package position - -type Body struct { -} - -type Query struct { -} - -type Path struct { -} - -type Header struct { -} diff --git a/internal/pkg/vendors/huaweicloud-sdk/cast.go b/internal/pkg/vendors/huaweicloud-sdk/cast.go deleted file mode 100644 index 2abb9c87..00000000 --- a/internal/pkg/vendors/huaweicloud-sdk/cast.go +++ /dev/null @@ -1,9 +0,0 @@ -package huaweicloudsdk - -func Int32Ptr(i int32) *int32 { - return &i -} - -func StringPtr(s string) *string { - return &s -} diff --git a/internal/repository/access.go b/internal/repository/access.go index d25a6366..16cc7378 100644 --- a/internal/repository/access.go +++ b/internal/repository/access.go @@ -53,6 +53,7 @@ func (r *AccessRepository) castRecordToModel(record *core.Record) (*domain.Acces Name: record.GetString("name"), Provider: record.GetString("provider"), Config: config, + Reserve: record.GetString("reserve"), } return access, nil } diff --git a/internal/workflow/dispatcher/dispatcher.go b/internal/workflow/dispatcher/dispatcher.go index d453b72f..7874b945 100644 --- a/internal/workflow/dispatcher/dispatcher.go +++ b/internal/workflow/dispatcher/dispatcher.go @@ -1,25 +1,31 @@ -package dispatcher +package dispatcher import ( "context" "errors" "fmt" "os" + "runtime" "strconv" "sync" "time" "github.com/usual2970/certimate/internal/app" "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/utils/sliceutil" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" ) -var maxWorkers = 16 +var maxWorkers = 1 func init() { envMaxWorkers := os.Getenv("CERTIMATE_WORKFLOW_MAX_WORKERS") if n, err := strconv.Atoi(envMaxWorkers); err != nil && n > 0 { maxWorkers = n + } else { + maxWorkers = runtime.GOMAXPROCS(0) + if maxWorkers == 0 { + maxWorkers = max(1, runtime.NumCPU()) + } } } diff --git a/internal/workflow/dispatcher/invoker.go b/internal/workflow/dispatcher/invoker.go index 5f344458..c644b26b 100644 --- a/internal/workflow/dispatcher/invoker.go +++ b/internal/workflow/dispatcher/invoker.go @@ -47,8 +47,10 @@ func (w *workflowInvoker) GetLogs() domain.WorkflowLogs { func (w *workflowInvoker) processNode(ctx context.Context, node *domain.WorkflowNode) error { current := node for current != nil { - if ctx.Err() != nil { + select { + case <-ctx.Done(): return ctx.Err() + default: } if current.Type == domain.WorkflowNodeTypeBranch || current.Type == domain.WorkflowNodeTypeExecuteResultBranch { diff --git a/internal/workflow/dispatcher/singleton.go b/internal/workflow/dispatcher/singleton.go index 20fdde97..e5a77655 100644 --- a/internal/workflow/dispatcher/singleton.go +++ b/internal/workflow/dispatcher/singleton.go @@ -1,4 +1,4 @@ -package dispatcher +package dispatcher import ( "context" diff --git a/internal/workflow/node-processor/apply_node.go b/internal/workflow/node-processor/apply_node.go index a1ca977d..97b7575d 100644 --- a/internal/workflow/node-processor/apply_node.go +++ b/internal/workflow/node-processor/apply_node.go @@ -9,7 +9,7 @@ import ( "github.com/usual2970/certimate/internal/applicant" "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" "github.com/usual2970/certimate/internal/repository" ) @@ -41,22 +41,25 @@ func (n *applyNode) Process(ctx context.Context) error { } // 检测是否可以跳过本次执行 - if skippable, skipReason := n.checkCanSkip(ctx, lastOutput); skippable { - n.logger.Info(fmt.Sprintf("skip this application, because %s", skipReason)) + if skippable, reason := n.checkCanSkip(ctx, lastOutput); skippable { + n.logger.Info(fmt.Sprintf("skip this application, because %s", reason)) return nil - } else if skipReason != "" { - n.logger.Info(fmt.Sprintf("re-apply, because %s", skipReason)) + } else if reason != "" { + n.logger.Info(fmt.Sprintf("re-apply, because %s", reason)) } // 初始化申请器 - applicant, err := applicant.NewWithApplyNode(n.node) + applicant, err := applicant.NewWithWorkflowNode(applicant.ApplicantWithWorkflowNodeConfig{ + Node: n.node, + Logger: n.logger, + }) if err != nil { n.logger.Warn("failed to create applicant provider") return err } // 申请证书 - applyResult, err := applicant.Apply() + applyResult, err := applicant.Apply(ctx) if err != nil { n.logger.Warn("failed to apply") return err @@ -109,12 +112,24 @@ func (n *applyNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workflo if currentNodeConfig.ContactEmail != lastNodeConfig.ContactEmail { return false, "the configuration item 'ContactEmail' changed" } + if currentNodeConfig.Provider != lastNodeConfig.Provider { + return false, "the configuration item 'Provider' changed" + } if currentNodeConfig.ProviderAccessId != lastNodeConfig.ProviderAccessId { return false, "the configuration item 'ProviderAccessId' changed" } if !maps.Equal(currentNodeConfig.ProviderConfig, lastNodeConfig.ProviderConfig) { return false, "the configuration item 'ProviderConfig' changed" } + if currentNodeConfig.CAProvider != lastNodeConfig.CAProvider { + return false, "the configuration item 'CAProvider' changed" + } + if currentNodeConfig.CAProviderAccessId != lastNodeConfig.CAProviderAccessId { + return false, "the configuration item 'CAProviderAccessId' changed" + } + if !maps.Equal(currentNodeConfig.CAProviderConfig, lastNodeConfig.CAProviderConfig) { + return false, "the configuration item 'CAProviderConfig' changed" + } if currentNodeConfig.KeyAlgorithm != lastNodeConfig.KeyAlgorithm { return false, "the configuration item 'KeyAlgorithm' changed" } diff --git a/internal/workflow/node-processor/deploy_node.go b/internal/workflow/node-processor/deploy_node.go index edb3c53d..d60a5a7a 100644 --- a/internal/workflow/node-processor/deploy_node.go +++ b/internal/workflow/node-processor/deploy_node.go @@ -54,26 +54,27 @@ func (n *deployNode) Process(ctx context.Context) error { // 检测是否可以跳过本次执行 if lastOutput != nil && certificate.CreatedAt.Before(lastOutput.UpdatedAt) { - if skippable, skipReason := n.checkCanSkip(ctx, lastOutput); skippable { - n.logger.Info(fmt.Sprintf("skip this deployment, because %s", skipReason)) + if skippable, reason := n.checkCanSkip(ctx, lastOutput); skippable { + n.logger.Info(fmt.Sprintf("skip this deployment, because %s", reason)) return nil - } else if skipReason != "" { - n.logger.Info(fmt.Sprintf("re-deploy, because %s", skipReason)) + } else if reason != "" { + n.logger.Info(fmt.Sprintf("re-deploy, because %s", reason)) } } // 初始化部署器 - deployer, err := deployer.NewWithDeployNode(n.node, struct { - Certificate string - PrivateKey string - }{Certificate: certificate.Certificate, PrivateKey: certificate.PrivateKey}) + deployer, err := deployer.NewWithWorkflowNode(deployer.DeployerWithWorkflowNodeConfig{ + Node: n.node, + Logger: n.logger, + CertificatePEM: certificate.Certificate, + PrivateKeyPEM: certificate.PrivateKey, + }) if err != nil { n.logger.Warn("failed to create deployer provider") return err } // 部署证书 - deployer.SetLogger(n.logger) if err := deployer.Deploy(ctx); err != nil { n.logger.Warn("failed to deploy") return err diff --git a/internal/workflow/node-processor/notify_node.go b/internal/workflow/node-processor/notify_node.go index 1c2b49d8..1840938b 100644 --- a/internal/workflow/node-processor/notify_node.go +++ b/internal/workflow/node-processor/notify_node.go @@ -30,25 +30,50 @@ func (n *notifyNode) Process(ctx context.Context) error { nodeConfig := n.node.GetConfigForNotify() - // 获取通知配置 - settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels") + if nodeConfig.Provider == "" { + // Deprecated: v0.4.x 将废弃 + // 兼容旧版本的通知渠道 + n.logger.Warn("WARNING! you are using the notification channel from global settings, which will be deprecated in the future") + + // 获取通知配置 + settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels") + if err != nil { + return err + } + + // 获取通知渠道 + channelConfig, err := settings.GetNotifyChannelConfig(nodeConfig.Channel) + if err != nil { + return err + } + + // 发送通知 + if err := notify.SendToChannel(nodeConfig.Subject, nodeConfig.Message, nodeConfig.Channel, channelConfig); err != nil { + n.logger.Warn("failed to notify", slog.String("channel", nodeConfig.Channel)) + return err + } + + n.logger.Info("notify completed") + return nil + } + + // 初始化通知器 + deployer, err := notify.NewWithWorkflowNode(notify.NotifierWithWorkflowNodeConfig{ + Node: n.node, + Logger: n.logger, + Subject: nodeConfig.Subject, + Message: nodeConfig.Message, + }) if err != nil { + n.logger.Warn("failed to create notifier provider") return err } - // 获取通知渠道 - channelConfig, err := settings.GetNotifyChannelConfig(nodeConfig.Channel) - if err != nil { + // 推送通知 + if err := deployer.Notify(ctx); err != nil { + n.logger.Warn("failed to notify") return err } - // 发送通知 - if err := notify.SendToChannel(nodeConfig.Subject, nodeConfig.Message, nodeConfig.Channel, channelConfig); err != nil { - n.logger.Warn("failed to notify", slog.String("channel", nodeConfig.Channel)) - return err - } - - n.logger.Info("notify completed") - return nil } diff --git a/internal/workflow/node-processor/upload_node.go b/internal/workflow/node-processor/upload_node.go index 891f2978..2da19eed 100644 --- a/internal/workflow/node-processor/upload_node.go +++ b/internal/workflow/node-processor/upload_node.go @@ -39,11 +39,11 @@ func (n *uploadNode) Process(ctx context.Context) error { } // 检测是否可以跳过本次执行 - if skippable, skipReason := n.checkCanSkip(ctx, lastOutput); skippable { - n.logger.Info(fmt.Sprintf("skip this upload, because %s", skipReason)) + if skippable, reason := n.checkCanSkip(ctx, lastOutput); skippable { + n.logger.Info(fmt.Sprintf("skip this upload, because %s", reason)) return nil - } else if skipReason != "" { - n.logger.Info(fmt.Sprintf("re-upload, because %s", skipReason)) + } else if reason != "" { + n.logger.Info(fmt.Sprintf("re-upload, because %s", reason)) } // 生成证书实体 diff --git a/migrations/1739462400_collections_snapshot.go b/migrations/1739462400_collections_snapshot.go index d8ed7409..25564526 100644 --- a/migrations/1739462400_collections_snapshot.go +++ b/migrations/1739462400_collections_snapshot.go @@ -7,7 +7,7 @@ import ( "github.com/pocketbase/pocketbase/core" m "github.com/pocketbase/pocketbase/migrations" - "github.com/usual2970/certimate/internal/pkg/utils/certutil" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" ) func init() { diff --git a/migrations/1742209200_upgrade.go b/migrations/1742209200_upgrade.go index 0a980972..5cda0c35 100644 --- a/migrations/1742209200_upgrade.go +++ b/migrations/1742209200_upgrade.go @@ -7,8 +7,6 @@ import ( "github.com/pocketbase/pocketbase/core" m "github.com/pocketbase/pocketbase/migrations" - - "github.com/usual2970/certimate/internal/domain" ) func init() { @@ -179,20 +177,20 @@ func init() { } for _, workflowRun := range workflowRuns { - type oldWorkflowRunLogRecord struct { + type dWorkflowRunLogRecord struct { Time string `json:"time"` Level string `json:"level"` Content string `json:"content"` Error string `json:"error"` } - type oldWorkflowRunLog struct { - NodeId string `json:"nodeId"` - NodeName string `json:"nodeName"` - Records []oldWorkflowRunLogRecord `json:"records"` - Error string `json:"error"` + type dWorkflowRunLog struct { + NodeId string `json:"nodeId"` + NodeName string `json:"nodeName"` + Records []dWorkflowRunLogRecord `json:"records"` + Error string `json:"error"` } - logs := make([]oldWorkflowRunLog, 0) + logs := make([]dWorkflowRunLog, 0) if err := workflowRun.UnmarshalJSONField("logs", &logs); err != nil { continue } @@ -259,8 +257,20 @@ func init() { return err } + type dWorkflowNode struct { + Id string `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + Config map[string]any `json:"config"` + Inputs []map[string]any `json:"inputs"` + Outputs []map[string]any `json:"outputs"` + Next *dWorkflowNode `json:"next,omitempty"` + Branches []dWorkflowNode `json:"branches,omitempty"` + Validated bool `json:"validated"` + } + for _, workflowRun := range workflowRuns { - node := &domain.WorkflowNode{} + node := &dWorkflowNode{} for _, workflowOutput := range workflowOutputs { if workflowOutput.GetString("runId") != workflowRun.Get("id") { continue @@ -270,8 +280,8 @@ func init() { continue } - if node.Type != domain.WorkflowNodeTypeApply { - node = &domain.WorkflowNode{} + if node.Type != "apply" { + node = &dWorkflowNode{} continue } } @@ -286,7 +296,7 @@ func init() { } else { workflow, _ := app.FindRecordById("workflow", workflowRun.GetString("workflowId")) if workflow != nil { - rootNode := &domain.WorkflowNode{} + rootNode := &dWorkflowNode{} if err := workflow.UnmarshalJSONField("content", rootNode); err != nil { return err } @@ -294,9 +304,9 @@ func init() { rootNode.Next = node workflowRun.Set("detail", rootNode) } else { - rootNode := &domain.WorkflowNode{ + rootNode := &dWorkflowNode{ Id: core.GenerateDefaultRandomId(), - Type: domain.WorkflowNodeTypeStart, + Type: "start", Name: "开始", Config: map[string]any{ "trigger": "manual", diff --git a/migrations/1743264000_upgrade.go b/migrations/1743264000_upgrade.go new file mode 100644 index 00000000..75f98b30 --- /dev/null +++ b/migrations/1743264000_upgrade.go @@ -0,0 +1,173 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + // update collection `settings` + { + collection, err := app.FindCollectionByNameOrId("dy6ccjb60spfy6p") + if err != nil { + return err + } + + records, err := app.FindRecordsByFilter(collection, "name='sslProvider'", "-created", 1, 0) + if err != nil { + return err + } + + if len(records) == 1 { + record := records[0] + + content := make(map[string]any) + if err := record.UnmarshalJSONField("content", &content); err != nil { + return err + } + + if provider, ok := content["provider"]; ok { + if providerStr, ok := provider.(string); ok { + if providerStr == "letsencrypt_staging" { + content["provider"] = "letsencryptstaging" + } + } + } + + if config, ok := content["config"]; ok { + if configMap, ok := config.(map[string]any); ok { + if _, ok := configMap["letsencrypt_staging"]; ok { + configMap["letsencryptstaging"] = configMap["letsencrypt_staging"] + delete(configMap, "letsencrypt_staging") + } + if _, ok := configMap["gts"]; ok { + configMap["googletrustservices"] = configMap["gts"] + delete(configMap, "gts") + } + } + } + + record.Set("content", content) + if err := app.Save(record); err != nil { + return err + } + } + } + + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // update field + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "hwy7m03o", + "maxSelect": 1, + "name": "provider", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "1panel", + "acmehttpreq", + "akamai", + "aliyun", + "aws", + "azure", + "baiducloud", + "baishan", + "baotapanel", + "byteplus", + "buypass", + "cachefly", + "cdnfly", + "cloudflare", + "cloudns", + "cmcccloud", + "ctcccloud", + "cucccloud", + "desec", + "dnsla", + "dogecloud", + "dynv6", + "edgio", + "fastly", + "gname", + "gcore", + "godaddy", + "goedge", + "googletrustservices", + "huaweicloud", + "jdcloud", + "k8s", + "letsencrypt", + "letsencryptstaging", + "local", + "namecheap", + "namedotcom", + "namesilo", + "ns1", + "porkbun", + "powerdns", + "qiniu", + "qingcloud", + "rainyun", + "safeline", + "ssh", + "sslcom", + "tencentcloud", + "ucloud", + "upyun", + "vercel", + "volcengine", + "webhook", + "westcn", + "zerossl" + ] + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + } + + // update collection `acme_accounts` + { + collection, err := app.FindCollectionByNameOrId("012d7abbod1hwvr") + if err != nil { + return err + } + + records, err := app.FindRecordsByFilter(collection, "ca='letsencrypt_staging' || ca='gts'", "-created", 0, 0) + if err != nil { + return err + } + + for _, record := range records { + ca := record.GetString("ca") + if ca == "letsencrypt_staging" { + record.Set("ca", "letsencryptstaging") + } else if ca == "gts" { + record.Set("ca", "googletrustservices") + } else { + continue + } + + if err := app.Save(record); err != nil { + return err + } + } + } + + return nil + }, func(app core.App) error { + return nil + }) +} diff --git a/migrations/1744192800_upgrade.go b/migrations/1744192800_upgrade.go new file mode 100644 index 00000000..83e83ee6 --- /dev/null +++ b/migrations/1744192800_upgrade.go @@ -0,0 +1,91 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // update field + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "hwy7m03o", + "maxSelect": 1, + "name": "provider", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "1panel", + "acmehttpreq", + "akamai", + "aliyun", + "aws", + "azure", + "baiducloud", + "baishan", + "baotapanel", + "byteplus", + "buypass", + "cachefly", + "cdnfly", + "cloudflare", + "cloudns", + "cmcccloud", + "ctcccloud", + "cucccloud", + "desec", + "dnsla", + "dogecloud", + "dynv6", + "edgio", + "fastly", + "gname", + "gcore", + "godaddy", + "goedge", + "googletrustservices", + "huaweicloud", + "jdcloud", + "k8s", + "letsencrypt", + "letsencryptstaging", + "local", + "namecheap", + "namedotcom", + "namesilo", + "ns1", + "porkbun", + "powerdns", + "qiniu", + "qingcloud", + "rainyun", + "safeline", + "ssh", + "sslcom", + "tencentcloud", + "ucloud", + "upyun", + "vercel", + "volcengine", + "wangsu", + "webhook", + "westcn", + "zerossl" + ] + }`)); err != nil { + return err + } + + return app.Save(collection) + }, func(app core.App) error { + return nil + }) +} diff --git a/migrations/1744459000_upgrade.go b/migrations/1744459000_upgrade.go new file mode 100644 index 00000000..4b2bbba9 --- /dev/null +++ b/migrations/1744459000_upgrade.go @@ -0,0 +1,92 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // update field + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "hwy7m03o", + "maxSelect": 1, + "name": "provider", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "1panel", + "acmehttpreq", + "akamai", + "aliyun", + "aws", + "azure", + "baiducloud", + "baishan", + "baotapanel", + "bunny", + "byteplus", + "buypass", + "cachefly", + "cdnfly", + "cloudflare", + "cloudns", + "cmcccloud", + "ctcccloud", + "cucccloud", + "desec", + "dnsla", + "dogecloud", + "dynv6", + "edgio", + "fastly", + "gname", + "gcore", + "godaddy", + "goedge", + "googletrustservices", + "huaweicloud", + "jdcloud", + "k8s", + "letsencrypt", + "letsencryptstaging", + "local", + "namecheap", + "namedotcom", + "namesilo", + "ns1", + "porkbun", + "powerdns", + "qiniu", + "qingcloud", + "rainyun", + "safeline", + "ssh", + "sslcom", + "tencentcloud", + "ucloud", + "upyun", + "vercel", + "volcengine", + "wangsu", + "webhook", + "westcn", + "zerossl" + ] + }`)); err != nil { + return err + } + + return app.Save(collection) + }, func(app core.App) error { + return nil + }) +} diff --git a/migrations/1745308800_upgrade.go b/migrations/1745308800_upgrade.go new file mode 100644 index 00000000..989664fb --- /dev/null +++ b/migrations/1745308800_upgrade.go @@ -0,0 +1,94 @@ +package migrations + +import ( + "encoding/json" + + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // add temp field `providerTmp` + if err := collection.Fields.AddMarshaledJSONAt(3, []byte(`{ + "autogeneratePattern": "", + "hidden": false, + "id": "text2024822322", + "max": 0, + "min": 0, + "name": "providerTmp", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + // copy `provider` to `providerTmp` + if _, err := app.DB().NewQuery("UPDATE access SET providerTmp = provider").Execute(); err != nil { + return err + } + + // remove old field `provider` + collection.Fields.RemoveById("hwy7m03o") + if err := json.Unmarshal([]byte(`{ + "indexes": [ + "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)" + ] + }`), &collection); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + // rename field `providerTmp` to `provider` + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "autogeneratePattern": "", + "hidden": false, + "id": "text2024822322", + "max": 0, + "min": 0, + "name": "provider", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + // rebuild indexes + if err := json.Unmarshal([]byte(`{ + "indexes": [ + "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)", + "CREATE INDEX `+"`"+`idx_frh0JT1Aqx`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`provider`+"`"+`)" + ] + }`), &collection); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + return nil + }, func(app core.App) error { + return nil + }) +} diff --git a/migrations/1745726400_upgrade.go b/migrations/1745726400_upgrade.go new file mode 100644 index 00000000..e4449f36 --- /dev/null +++ b/migrations/1745726400_upgrade.go @@ -0,0 +1,88 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(4, []byte(`{ + "autogeneratePattern": "", + "hidden": false, + "id": "text2859962647", + "max": 0, + "min": 0, + "name": "reserve", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + } + + // migrate data + { + accesses, err := app.FindAllRecords("access") + if err != nil { + return err + } + + for _, access := range accesses { + changed := false + + if access.GetString("provider") == "buypass" { + access.Set("reserve", "ca") + changed = true + } else if access.GetString("provider") == "googletrustservices" { + access.Set("reserve", "ca") + changed = true + } else if access.GetString("provider") == "sslcom" { + access.Set("reserve", "ca") + changed = true + } else if access.GetString("provider") == "zerossl" { + access.Set("reserve", "ca") + changed = true + } + + if access.GetString("provider") == "webhook" { + config := make(map[string]any) + if err := access.UnmarshalJSONField("config", &config); err != nil { + return err + } + + config["method"] = "POST" + config["headers"] = "Content-Type: application/json" + access.Set("config", config) + changed = true + } + + if changed { + err = app.Save(access) + if err != nil { + return err + } + } + } + } + + return nil + }, func(app core.App) error { + return nil + }) +} diff --git a/ui/package-lock.json b/ui/package-lock.json index 90a18bbf..bf44f371 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,57 +8,57 @@ "name": "ui", "version": "0.0.0", "dependencies": { - "@ant-design/icons": "^5.6.1", - "@ant-design/pro-components": "^2.8.6", + "@ant-design/icons": "^6.0.0", + "@ant-design/pro-components": "^2.8.7", "ahooks": "^3.8.4", - "antd": "^5.24.4", - "antd-zod": "^6.0.1", + "antd": "^5.24.8", + "antd-zod": "^6.1.0", "clsx": "^2.1.1", - "cron-parser": "^5.0.6", + "cron-parser": "^5.1.1", "file-saver": "^2.0.5", - "i18next": "^24.2.3", - "i18next-browser-languagedetector": "^8.0.4", + "i18next": "^25.0.1", + "i18next-browser-languagedetector": "^8.0.5", "immer": "^10.1.1", - "lucide-react": "^0.483.0", + "lucide-react": "^0.503.0", "nanoid": "^5.1.5", - "pocketbase": "^0.25.2", + "pocketbase": "^0.26.0", "radash": "^12.1.0", "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", "react-i18next": "^15.4.1", - "react-router-dom": "^7.4.0", + "react-router-dom": "^7.5.1", "tailwind-merge": "^2.6.0", - "zod": "^3.24.2", + "zod": "^3.24.3", "zustand": "^5.0.3" }, "devDependencies": { "@types/file-saver": "^2.0.7", "@types/fs-extra": "^11.0.4", - "@types/node": "^22.13.10", + "@types/node": "^22.14.1", "@types/react": "^18.3.12", "@types/react-copy-to-clipboard": "^5.0.7", "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^8.27.0", - "@typescript-eslint/parser": "^8.27.0", - "@vitejs/plugin-legacy": "^6.0.2", - "@vitejs/plugin-react": "^4.3.4", + "@typescript-eslint/eslint-plugin": "^8.31.0", + "@typescript-eslint/parser": "^8.31.0", + "@vitejs/plugin-legacy": "^6.1.0", + "@vitejs/plugin-react": "^4.4.1", "autoprefixer": "^10.4.21", "eslint": "^8.57.0", - "eslint-config-prettier": "^10.1.1", + "eslint-config-prettier": "^10.1.2", "eslint-import-resolver-typescript": "^3.8.3", "eslint-plugin-import": "^2.31.0", - "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.19", + "eslint-plugin-react-refresh": "^0.4.20", "eslint-plugin-tailwindcss": "^3.18.0", "fs-extra": "^11.3.0", "postcss": "^8.5.3", "prettier": "^3.5.3", "tailwindcss": "^3.4.17", "tailwindcss-animate": "^1.0.7", - "typescript": "^5.8.2", - "vite": "^6.2.2" + "typescript": "^5.8.3", + "vite": "^6.3.2" } }, "node_modules/@alloc/quick-lru": { @@ -138,6 +138,64 @@ } }, "node_modules/@ant-design/icons": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-6.0.0.tgz", + "integrity": "sha512-o0aCCAlHc1o4CQcapAwWzHeaW2x9F49g7P3IDtvtNXgHowtRWYb7kiubt8sQPFvfVIVU/jLw2hzeSlNt0FU+Uw==", + "dependencies": { + "@ant-design/colors": "^8.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@rc-component/util": "^1.2.1", + "classnames": "^2.2.6" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmmirror.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==" + }, + "node_modules/@ant-design/icons/node_modules/@ant-design/colors": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-8.0.0.tgz", + "integrity": "sha512-6YzkKCw30EI/E9kHOIXsQDHmMvTllT8STzjMb4K2qzit33RW2pqCJP0sk+hidBntXxE+Vz4n1+RvCTfBw6OErw==", + "dependencies": { + "@ant-design/fast-color": "^3.0.0" + } + }, + "node_modules/@ant-design/icons/node_modules/@ant-design/fast-color": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/@ant-design/fast-color/-/fast-color-3.0.0.tgz", + "integrity": "sha512-eqvpP7xEDm2S7dUzl5srEQCBTXZMmY3ekf97zI+M2DHOYyKdJGH0qua0JACHTqbkRnD/KHFQP9J1uMJ/XWVzzA==", + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/pro-card": { + "version": "2.9.7", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-card/-/pro-card-2.9.7.tgz", + "integrity": "sha512-uDDYowmYH1ldRfG8Mb4QOwcEEz6ptRBQDLO1tkVADCRkdOMwz82xlZneR4uVuFyKcuNmgHzarYNncozBKhFuaA==", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.4.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-card/node_modules/@ant-design/icons": { "version": "5.6.1", "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz", "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", @@ -156,45 +214,21 @@ "react-dom": ">=16.0.0" } }, - "node_modules/@ant-design/icons-svg": { - "version": "4.4.2", - "resolved": "https://registry.npmmirror.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", - "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==" - }, - "node_modules/@ant-design/pro-card": { - "version": "2.9.6", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-card/-/pro-card-2.9.6.tgz", - "integrity": "sha512-boUvowODMhc1l55ZZj/08YwnaggL50fAio2NaA7uXsgpbLduSPL2OE0RyiI24NCqFhPRZMZQHbPOmcHw4Bf3yQ==", - "dependencies": { - "@ant-design/cssinjs": "^1.21.1", - "@ant-design/icons": "^5.0.0", - "@ant-design/pro-provider": "2.15.3", - "@ant-design/pro-utils": "2.16.4", - "@babel/runtime": "^7.18.0", - "classnames": "^2.3.2", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.4.0" - }, - "peerDependencies": { - "antd": "^4.24.15 || ^5.11.2", - "react": ">=17.0.0" - } - }, "node_modules/@ant-design/pro-components": { - "version": "2.8.6", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-components/-/pro-components-2.8.6.tgz", - "integrity": "sha512-iNd9kTLI0vAYGiyVrpDRflmee+h7486OLXIgIb89g3G+5YS9xSnRuCYt6UBRAEGsB1GRUPznRUGUd6Gsg33V+g==", + "version": "2.8.7", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-components/-/pro-components-2.8.7.tgz", + "integrity": "sha512-QhibkPsUJryEjI1QmwUn+XCngGHidu0ekvricL6TIEvPgP+AUAca29XutN5+Mmn8Xfja1ca9HFTHTgFoV74Z7Q==", "dependencies": { - "@ant-design/pro-card": "2.9.6", - "@ant-design/pro-descriptions": "2.6.6", - "@ant-design/pro-field": "3.0.3", - "@ant-design/pro-form": "2.31.6", - "@ant-design/pro-layout": "7.22.3", - "@ant-design/pro-list": "2.6.6", - "@ant-design/pro-provider": "2.15.3", + "@ant-design/pro-card": "2.9.7", + "@ant-design/pro-descriptions": "2.6.7", + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-form": "2.31.7", + "@ant-design/pro-layout": "7.22.4", + "@ant-design/pro-list": "2.6.7", + "@ant-design/pro-provider": "2.15.4", "@ant-design/pro-skeleton": "2.2.1", - "@ant-design/pro-table": "3.18.6", - "@ant-design/pro-utils": "2.16.4", + "@ant-design/pro-table": "3.19.0", + "@ant-design/pro-utils": "2.17.0", "@babel/runtime": "^7.16.3" }, "peerDependencies": { @@ -204,15 +238,15 @@ } }, "node_modules/@ant-design/pro-descriptions": { - "version": "2.6.6", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-descriptions/-/pro-descriptions-2.6.6.tgz", - "integrity": "sha512-Onwn79P5wNcFNHQmXVdTDgewXt4MCW5snEFctZuCY6T5KwpH7Y9UA8GWtFNIL2KfF5+uu83es20N0E2hg73G0w==", + "version": "2.6.7", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-descriptions/-/pro-descriptions-2.6.7.tgz", + "integrity": "sha512-fgn2d0kDWUODGDWKpgziZuuqPlmIoKxQFJY9Yg4nbaRp8GDDKZeSSqgvW+OxjpYM8dxq31fiz1dZlZnOPoYKpg==", "dependencies": { - "@ant-design/pro-field": "3.0.3", - "@ant-design/pro-form": "2.31.6", - "@ant-design/pro-provider": "2.15.3", + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-form": "2.31.7", + "@ant-design/pro-provider": "2.15.4", "@ant-design/pro-skeleton": "2.2.1", - "@ant-design/pro-utils": "2.16.4", + "@ant-design/pro-utils": "2.17.0", "@babel/runtime": "^7.18.0", "rc-resize-observer": "^0.2.3", "rc-util": "^5.0.6" @@ -238,13 +272,13 @@ } }, "node_modules/@ant-design/pro-field": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-field/-/pro-field-3.0.3.tgz", - "integrity": "sha512-MrDZcx1kP4vpmnSJyHuxeool2o5hDRiJ8aRFloM2M7yW+Tw9ilABMlHFkKz1FLIt4esO9Zc4vd8Iv2ndlkB4/Q==", + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-field/-/pro-field-3.0.4.tgz", + "integrity": "sha512-nJSng/6/pPZFdiFeTtZcBQLNrHg9tIeiKFR1+zzbnQbI3qBOFP9aBZS/+LwkQZcI2G71vrRgz2x5OhHb7AX0wQ==", "dependencies": { "@ant-design/icons": "^5.0.0", - "@ant-design/pro-provider": "2.15.3", - "@ant-design/pro-utils": "2.16.4", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", "@babel/runtime": "^7.18.0", "@chenshuai2144/sketch-color": "^1.0.8", "classnames": "^2.3.2", @@ -259,15 +293,34 @@ "react": ">=17.0.0" } }, + "node_modules/@ant-design/pro-field/node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, "node_modules/@ant-design/pro-form": { - "version": "2.31.6", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-form/-/pro-form-2.31.6.tgz", - "integrity": "sha512-pDthX9AjLiryFrtPFY+Ep1z/CfEbhg++K25p7jA6tyl1gVeOIcHVkLTEFMNKmWrc9ZSCA35D/UeSz3bn102GLg==", + "version": "2.31.7", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-form/-/pro-form-2.31.7.tgz", + "integrity": "sha512-0TCtIC/ynbLPoes8sLBFwFbi0tkeNmSU6the2EcyKIKDLfWHDbfkLM1OSFrzv3QD+H8OgFWMkTSOjhMOKSsOBg==", "dependencies": { "@ant-design/icons": "^5.0.0", - "@ant-design/pro-field": "3.0.3", - "@ant-design/pro-provider": "2.15.3", - "@ant-design/pro-utils": "2.16.4", + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", "@babel/runtime": "^7.18.0", "@chenshuai2144/sketch-color": "^1.0.7", "@umijs/use-params": "^1.0.9", @@ -285,15 +338,34 @@ "react-dom": ">=17.0.0" } }, + "node_modules/@ant-design/pro-form/node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, "node_modules/@ant-design/pro-layout": { - "version": "7.22.3", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-layout/-/pro-layout-7.22.3.tgz", - "integrity": "sha512-di/EOMDuoMDRjBweqesYyCxEYr2LCmO82y6A4bSwmmJ6ehxN7HGC73Wx4RuBkzDR7kHLTOXt7WxI6875ENT8mg==", + "version": "7.22.4", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-layout/-/pro-layout-7.22.4.tgz", + "integrity": "sha512-X2WO4L2itXemX4zhS+0NG+8kXQD5SX9sG+zjx/15BmIO3FvsUGqOHgoCg0vhd424EiyPj7WtdMZJ39G1xdgDwA==", "dependencies": { "@ant-design/cssinjs": "^1.21.1", "@ant-design/icons": "^5.0.0", - "@ant-design/pro-provider": "2.15.3", - "@ant-design/pro-utils": "2.16.4", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", "@babel/runtime": "^7.18.0", "@umijs/route-utils": "^4.0.0", "@umijs/use-params": "^1.0.9", @@ -312,17 +384,36 @@ "react-dom": ">=17.0.0" } }, + "node_modules/@ant-design/pro-layout/node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, "node_modules/@ant-design/pro-list": { - "version": "2.6.6", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-list/-/pro-list-2.6.6.tgz", - "integrity": "sha512-Yea/KDd3kjOKklz1AHs66JGvtguvPYYFSaZFXW4m4VBjABHoaF6seo7ySW9UUWgwuoegdGtiglvleYv/rQoEcw==", + "version": "2.6.7", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-list/-/pro-list-2.6.7.tgz", + "integrity": "sha512-6k/En7pioMgepho/1HMf2DAnkSTZiat1lDg2ggCok2lhSgqXzir7x22ewJQRgPvEiVb6/qqaFQNd7a8dnrFj1w==", "dependencies": { "@ant-design/cssinjs": "^1.21.1", "@ant-design/icons": "^5.0.0", - "@ant-design/pro-card": "2.9.6", - "@ant-design/pro-field": "3.0.3", - "@ant-design/pro-table": "3.18.6", - "@ant-design/pro-utils": "2.16.4", + "@ant-design/pro-card": "2.9.7", + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-table": "3.19.0", + "@ant-design/pro-utils": "2.17.0", "@babel/runtime": "^7.18.0", "classnames": "^2.3.2", "dayjs": "^1.11.10", @@ -335,6 +426,38 @@ "react-dom": ">=17.0.0" } }, + "node_modules/@ant-design/pro-list/node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/pro-list/node_modules/@ant-design/icons/node_modules/rc-util": { + "version": "5.44.4", + "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.44.4.tgz", + "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/@ant-design/pro-list/node_modules/rc-util": { "version": "4.21.1", "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-4.21.1.tgz", @@ -347,15 +470,15 @@ "shallowequal": "^1.1.0" } }, - "node_modules/@ant-design/pro-list/node_modules/react-is": { + "node_modules/@ant-design/pro-list/node_modules/rc-util/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/@ant-design/pro-provider": { - "version": "2.15.3", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-provider/-/pro-provider-2.15.3.tgz", - "integrity": "sha512-jUBCuRrhAXNMumSZ++704/zEg/7U1k2N3jMVBgtirvVaCAk5O9iZQKK4W3O3LRFc+D8yO16sXjsxhawvdGL4cA==", + "version": "2.15.4", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-provider/-/pro-provider-2.15.4.tgz", + "integrity": "sha512-DBX0JNUNOYXAucVqd/zTdqtXckCDqr2Lo85KIku2YzWdhptDPDZRTNqL04JShjGejDl8fzwQ8yREHgVUfzn6Gg==", "dependencies": { "@ant-design/cssinjs": "^1.21.1", "@babel/runtime": "^7.18.0", @@ -384,17 +507,17 @@ } }, "node_modules/@ant-design/pro-table": { - "version": "3.18.6", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-table/-/pro-table-3.18.6.tgz", - "integrity": "sha512-ABXavpJWUOGGcer/WLBpRtzWCbfwZX3T8vAwMbLUQAl1xz3TTgkYzDDTcIdwUmtVdkgJUdWc8GdHWangWW30iQ==", + "version": "3.19.0", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-table/-/pro-table-3.19.0.tgz", + "integrity": "sha512-nL25734d5q5oqtmG7Apn2TNJUnJE8m9dkopXMQdoNZnv8qeRQLBH+i5cZT1yh7FIO8z6QLXleg+KnR/cI7VRRw==", "dependencies": { "@ant-design/cssinjs": "^1.21.1", "@ant-design/icons": "^5.0.0", - "@ant-design/pro-card": "2.9.6", - "@ant-design/pro-field": "3.0.3", - "@ant-design/pro-form": "2.31.6", - "@ant-design/pro-provider": "2.15.3", - "@ant-design/pro-utils": "2.16.4", + "@ant-design/pro-card": "2.9.7", + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-form": "2.31.7", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", "@babel/runtime": "^7.18.0", "@dnd-kit/core": "^6.0.8", "@dnd-kit/modifiers": "^6.0.1", @@ -414,13 +537,32 @@ "react-dom": ">=17.0.0" } }, + "node_modules/@ant-design/pro-table/node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, "node_modules/@ant-design/pro-utils": { - "version": "2.16.4", - "resolved": "https://registry.npmmirror.com/@ant-design/pro-utils/-/pro-utils-2.16.4.tgz", - "integrity": "sha512-PFxqF0fsUsLj8ORvJSuMgVv9NDHwAxZaglzPN/u3jZX7rWYcrHD04EMJEXooZaSyT6Q4+j7SqXDx6oBsdb9zNw==", + "version": "2.17.0", + "resolved": "https://registry.npmmirror.com/@ant-design/pro-utils/-/pro-utils-2.17.0.tgz", + "integrity": "sha512-hHKUISjMEoS+E5ltJWyvNTrlEA3IimZNxtDrEhorRIbgVYAlmEN5Mj/ESSofzDM3+UlxiI5+A/Y6IHkByTfDEA==", "dependencies": { "@ant-design/icons": "^5.0.0", - "@ant-design/pro-provider": "2.15.3", + "@ant-design/pro-provider": "2.15.4", "@babel/runtime": "^7.18.0", "classnames": "^2.3.2", "dayjs": "^1.11.10", @@ -436,6 +578,25 @@ "react-dom": ">=17.0.0" } }, + "node_modules/@ant-design/pro-utils/node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, "node_modules/@ant-design/react-slick": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz", @@ -475,21 +636,21 @@ } }, "node_modules/@babel/core": { - "version": "7.26.9", - "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.26.9.tgz", - "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", + "version": "7.26.10", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", + "@babel/generator": "^7.26.10", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.9", - "@babel/parser": "^7.26.9", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -514,13 +675,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.9", - "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", "dev": true, "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -791,25 +952,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.9", - "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", "dev": true, "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "dev": true, "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1890,30 +2051,30 @@ } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.9", - "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1922,9 +2083,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -2730,15 +2891,15 @@ } }, "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmmirror.com/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "version": "0.2.4", + "resolved": "https://registry.npmmirror.com/@pkgr/core/-/core-0.2.4.tgz", + "integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==", "dev": true, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/pkgr" } }, "node_modules/@rc-component/async-validator": { @@ -2881,10 +3042,22 @@ "react-dom": ">=16.9.0" } }, + "node_modules/@rc-component/util": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@rc-component/util/-/util-1.2.1.tgz", + "integrity": "sha512-AUVu6jO+lWjQnUOOECwu8iR0EdElQgWW5NBv5vP/Uf9dWbAX3udhMutRlkVXjuac2E40ghkFy+ve00mc/3Fymg==", + "dependencies": { + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.6.tgz", - "integrity": "sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", "cpu": [ "arm" ], @@ -2895,9 +3068,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.6.tgz", - "integrity": "sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", "cpu": [ "arm64" ], @@ -2908,9 +3081,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.6.tgz", - "integrity": "sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", "cpu": [ "arm64" ], @@ -2921,9 +3094,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.6.tgz", - "integrity": "sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", "cpu": [ "x64" ], @@ -2934,9 +3107,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.6.tgz", - "integrity": "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", "cpu": [ "arm64" ], @@ -2947,9 +3120,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.6.tgz", - "integrity": "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", "cpu": [ "x64" ], @@ -2960,9 +3133,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.6.tgz", - "integrity": "sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", "cpu": [ "arm" ], @@ -2973,9 +3146,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.6.tgz", - "integrity": "sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", "cpu": [ "arm" ], @@ -2986,9 +3159,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.6.tgz", - "integrity": "sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", "cpu": [ "arm64" ], @@ -2999,9 +3172,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.6.tgz", - "integrity": "sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", "cpu": [ "arm64" ], @@ -3012,9 +3185,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.6.tgz", - "integrity": "sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", "cpu": [ "loong64" ], @@ -3025,9 +3198,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.6.tgz", - "integrity": "sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", "cpu": [ "ppc64" ], @@ -3038,9 +3211,22 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.6.tgz", - "integrity": "sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", "cpu": [ "riscv64" ], @@ -3051,9 +3237,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.6.tgz", - "integrity": "sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", "cpu": [ "s390x" ], @@ -3064,9 +3250,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.6.tgz", - "integrity": "sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", "cpu": [ "x64" ], @@ -3077,9 +3263,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.6.tgz", - "integrity": "sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", "cpu": [ "x64" ], @@ -3090,9 +3276,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.6.tgz", - "integrity": "sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", "cpu": [ "arm64" ], @@ -3103,9 +3289,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.6.tgz", - "integrity": "sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", "cpu": [ "ia32" ], @@ -3116,9 +3302,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.6.tgz", - "integrity": "sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", "cpu": [ "x64" ], @@ -3175,15 +3361,10 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmmirror.com/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" - }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true }, "node_modules/@types/file-saver": { @@ -3218,12 +3399,12 @@ } }, "node_modules/@types/node": { - "version": "22.13.10", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.13.10.tgz", - "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "version": "22.14.1", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", "dev": true, "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~6.21.0" } }, "node_modules/@types/prop-types": { @@ -3261,16 +3442,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.27.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz", - "integrity": "sha512-4henw4zkePi5p252c8ncBLzLce52SEUz2Ebj8faDnuUXz2UuHEONYcJ+G0oaCF+bYCWVZtrGzq3FD7YXetmnSA==", + "version": "8.31.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz", + "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.27.0", - "@typescript-eslint/type-utils": "8.27.0", - "@typescript-eslint/utils": "8.27.0", - "@typescript-eslint/visitor-keys": "8.27.0", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/type-utils": "8.31.0", + "@typescript-eslint/utils": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3290,15 +3471,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.27.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.27.0.tgz", - "integrity": "sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==", + "version": "8.31.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.31.0.tgz", + "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.27.0", - "@typescript-eslint/types": "8.27.0", - "@typescript-eslint/typescript-estree": "8.27.0", - "@typescript-eslint/visitor-keys": "8.27.0", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4" }, "engines": { @@ -3314,13 +3495,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.27.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.27.0.tgz", - "integrity": "sha512-8oI9GwPMQmBryaaxG1tOZdxXVeMDte6NyJA4i7/TWa4fBwgnAXYlIQP+uYOeqAaLJ2JRxlG9CAyL+C+YE9Xknw==", + "version": "8.31.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz", + "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.27.0", - "@typescript-eslint/visitor-keys": "8.27.0" + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3331,13 +3512,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.27.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.27.0.tgz", - "integrity": "sha512-wVArTVcz1oJOIEJxui/nRhV0TXzD/zMSOYi/ggCfNq78EIszddXcJb7r4RCp/oBrjt8n9A0BSxRMKxHftpDxDA==", + "version": "8.31.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz", + "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.27.0", - "@typescript-eslint/utils": "8.27.0", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/utils": "8.31.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -3354,9 +3535,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.27.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.27.0.tgz", - "integrity": "sha512-/6cp9yL72yUHAYq9g6DsAU+vVfvQmd1a8KyA81uvfDE21O2DwQ/qxlM4AR8TSdAu+kJLBDrEHKC5/W2/nxsY0A==", + "version": "8.31.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3367,13 +3548,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.27.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.27.0.tgz", - "integrity": "sha512-BnKq8cqPVoMw71O38a1tEb6iebEgGA80icSxW7g+kndx0o6ot6696HjG7NdgfuAVmVEtwXUr3L8R9ZuVjoQL6A==", + "version": "8.31.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz", + "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.27.0", - "@typescript-eslint/visitor-keys": "8.27.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3393,15 +3574,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.27.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.27.0.tgz", - "integrity": "sha512-njkodcwH1yvmo31YWgRHNb/x1Xhhq4/m81PhtvmRngD8iHPehxffz1SNCO+kwaePhATC+kOa/ggmvPoPza5i0Q==", + "version": "8.31.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.31.0.tgz", + "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.27.0", - "@typescript-eslint/types": "8.27.0", - "@typescript-eslint/typescript-estree": "8.27.0" + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3416,12 +3597,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.27.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.27.0.tgz", - "integrity": "sha512-WsXQwMkILJvffP6z4U3FYJPlbf/j07HIxmDjZpbNvBJkMfvwXj5ACRkkHwBDvLBbDbtX5TdU64/rcvKJ/vuInQ==", + "version": "8.31.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz", + "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.27.0", + "@typescript-eslint/types": "8.31.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3464,16 +3645,16 @@ "dev": true }, "node_modules/@vitejs/plugin-legacy": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-legacy/-/plugin-legacy-6.0.2.tgz", - "integrity": "sha512-b/a6ARuJ1yCoIH/lSjpwPMyqo3NSCoqyxYtff7VCC6cnJfvBTzd7PthcrbomhLZnMsp/eW41b6TrbNSQvHW2lA==", + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-legacy/-/plugin-legacy-6.1.0.tgz", + "integrity": "sha512-D5/33NZFBDdlXvCoRbdGe3DcnSY4TZIDkQpolbMdMXBtUNgwJc03X1LHmEov4Igi4f5z2l9/lC0te74R8iHn5A==", "dev": true, "dependencies": { - "@babel/core": "^7.26.9", + "@babel/core": "^7.26.10", "@babel/preset-env": "^7.26.9", "browserslist": "^4.24.4", "browserslist-to-esbuild": "^2.1.1", - "core-js": "^3.40.0", + "core-js": "^3.41.0", "magic-string": "^0.30.17", "regenerator-runtime": "^0.14.1", "systemjs": "^6.15.1" @@ -3490,16 +3671,16 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", + "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", "dev": true, "dependencies": { - "@babel/core": "^7.26.0", + "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -3585,9 +3766,9 @@ } }, "node_modules/antd": { - "version": "5.24.4", - "resolved": "https://registry.npmmirror.com/antd/-/antd-5.24.4.tgz", - "integrity": "sha512-s89666DcoWeekJFaIqbtz2vRlIvgPR28GuDYYGUpW1mVP08bV7HZAPBH5lFJKYNGKrN3dHbZGgRK5aNRD2iPHg==", + "version": "5.24.8", + "resolved": "https://registry.npmmirror.com/antd/-/antd-5.24.8.tgz", + "integrity": "sha512-vJcW81WSRq+ymBKTiA3NE+FddmiqTAKxdWVRZU+HnLLrRrIz896svcUxXFPa7M4mH9HqyeJ5JPOHsne4sQAC1A==", "dependencies": { "@ant-design/colors": "^7.2.0", "@ant-design/cssinjs": "^1.23.0", @@ -3611,13 +3792,13 @@ "rc-drawer": "~7.2.0", "rc-dropdown": "~4.2.1", "rc-field-form": "~2.7.0", - "rc-image": "~7.11.0", - "rc-input": "~1.7.3", - "rc-input-number": "~9.4.0", - "rc-mentions": "~2.19.1", + "rc-image": "~7.11.1", + "rc-input": "~1.8.0", + "rc-input-number": "~9.5.0", + "rc-mentions": "~2.20.0", "rc-menu": "~9.16.1", "rc-motion": "^2.9.5", - "rc-notification": "~5.6.3", + "rc-notification": "~5.6.4", "rc-pagination": "~5.1.0", "rc-picker": "~4.11.3", "rc-progress": "~4.0.0", @@ -3629,8 +3810,8 @@ "rc-steps": "~6.0.1", "rc-switch": "~4.1.0", "rc-table": "~7.50.4", - "rc-tabs": "~15.5.1", - "rc-textarea": "~1.9.0", + "rc-tabs": "~15.6.0", + "rc-textarea": "~1.10.0", "rc-tooltip": "~6.4.0", "rc-tree": "~5.13.1", "rc-tree-select": "~5.27.0", @@ -3649,9 +3830,9 @@ } }, "node_modules/antd-zod": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/antd-zod/-/antd-zod-6.0.1.tgz", - "integrity": "sha512-O4cEUq2p/DOwmpZYtPHbrie1CzcvhJKeN/bDOtfostjcFRz3E/HgINqALZeNaT68X+hqcXxhnxjwIHhxb+oaWw==", + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/antd-zod/-/antd-zod-6.1.0.tgz", + "integrity": "sha512-7gXf5NgZO9MRrn54dQVmSDujGuY9YgV6jmE4sOKw4MuFu+YgYPrk0oAEiRTZ9DveCyyZ1krurOwsXJJGuDJ3IQ==", "dependencies": { "lodash.merge": "^4.6.2" }, @@ -3660,6 +3841,25 @@ "zod": ">=3.0.0" } }, + "node_modules/antd/node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", @@ -4177,9 +4377,9 @@ } }, "node_modules/core-js": { - "version": "3.40.0", - "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.40.0.tgz", - "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", + "version": "3.41.0", + "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.41.0.tgz", + "integrity": "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==", "dev": true, "hasInstallScript": true, "funding": { @@ -4201,11 +4401,11 @@ } }, "node_modules/cron-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-5.0.6.tgz", - "integrity": "sha512-KtZxEaO4XtQwQ6q2Val3gX09TxM/1Okz0BIqkm6Wwc582gAHTnjBP1rtYEgWcVOUPRIg2CeirOTiUSu7A2I+HQ==", + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-5.1.1.tgz", + "integrity": "sha512-xNhwjTUTJcvevF4EvOxB3xYpEKC/qOAmykR+2Qf91ARIfdbjStUwo8qpem6jjzdwFgoo4pnf3sS264xG0G858w==", "dependencies": { - "luxon": "^3.5.0" + "luxon": "^3.6.1" }, "engines": { "node": ">=18" @@ -4670,9 +4870,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.1", - "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz", - "integrity": "sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==", + "version": "10.1.2", + "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz", + "integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -4847,13 +5047,13 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.3", - "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", - "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", + "version": "5.2.6", + "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz", + "integrity": "sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.9.1" + "synckit": "^0.11.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4864,7 +5064,7 @@ "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", - "eslint-config-prettier": "*", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -4889,9 +5089,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.19", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", - "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", + "version": "0.4.20", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", "dev": true, "peerDependencies": { "eslint": ">=8.40" @@ -5608,9 +5808,9 @@ } }, "node_modules/i18next": { - "version": "24.2.3", - "resolved": "https://registry.npmmirror.com/i18next/-/i18next-24.2.3.tgz", - "integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==", + "version": "25.0.1", + "resolved": "https://registry.npmmirror.com/i18next/-/i18next-25.0.1.tgz", + "integrity": "sha512-8S8PyZbrymJZn3DaN70/34JYWNhsqrU6yA4MuzcygJBv+41dgNMocEA8h+kV1P7MCc1ll03lOTOIXE7mpNCicw==", "funding": [ { "type": "individual", @@ -5638,9 +5838,9 @@ } }, "node_modules/i18next-browser-languagedetector": { - "version": "8.0.4", - "resolved": "https://registry.npmmirror.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.4.tgz", - "integrity": "sha512-f3frU3pIxD50/Tz20zx9TD9HobKYg47fmAETb117GKGPrhwcSSPJDoCposXlVycVebQ9GQohC3Efbpq7/nnJ5w==", + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.5.tgz", + "integrity": "sha512-OstebRKqKiQw8xEvQF5aRyUujsCatanj7Q9eo5iiH2gJpoXGZ7483ol3sVBwfqbobTQPNH1J+NAyJ1aCQoEC+w==", "dependencies": { "@babel/runtime": "^7.23.2" } @@ -6324,17 +6524,17 @@ } }, "node_modules/lucide-react": { - "version": "0.483.0", - "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.483.0.tgz", - "integrity": "sha512-WldsY17Qb/T3VZdMnVQ9C3DDIP7h1ViDTHVdVGnLZcvHNg30zH/MTQ04RTORjexoGmpsXroiQXZ4QyR0kBy0FA==", + "version": "0.503.0", + "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.503.0.tgz", + "integrity": "sha512-HGGkdlPWQ0vTF8jJ5TdIqhQXZi6uh3LnNgfZ8MHiuxFfX3RZeA79r2MW2tHAZKlAVfoNE8esm3p+O6VkIvpj6w==", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/luxon": { - "version": "3.5.0", - "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.5.0.tgz", - "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.6.1.tgz", + "integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==", "engines": { "node": ">=12" } @@ -6787,9 +6987,9 @@ } }, "node_modules/pocketbase": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/pocketbase/-/pocketbase-0.25.2.tgz", - "integrity": "sha512-ONZl1+qHJMnhR2uacBlBJ90lm7njtL/zy0606+1ROfK9hSL4LRBRc8r89rMcNRzPzRqCNyoFTh2Qg/lYXdEC1w==" + "version": "0.26.0", + "resolved": "https://registry.npmmirror.com/pocketbase/-/pocketbase-0.26.0.tgz", + "integrity": "sha512-WBBeOgz4Jnrd7a1KEzSBUJqpTortKKCcp16j5KoF+4tNIyQHsmynj+qRSvS56/RVacVMbAqO8Qkfj3N84fpzEw==" }, "node_modules/possible-typed-array-names": { "version": "1.0.0", @@ -7159,9 +7359,9 @@ } }, "node_modules/rc-image": { - "version": "7.11.0", - "resolved": "https://registry.npmmirror.com/rc-image/-/rc-image-7.11.0.tgz", - "integrity": "sha512-aZkTEZXqeqfPZtnSdNUnKQA0N/3MbgR7nUnZ+/4MfSFWPFHZau4p5r5ShaI0KPEMnNjv4kijSCFq/9wtJpwykw==", + "version": "7.11.1", + "resolved": "https://registry.npmmirror.com/rc-image/-/rc-image-7.11.1.tgz", + "integrity": "sha512-XuoWx4KUXg7hNy5mRTy1i8c8p3K8boWg6UajbHpDXS5AlRVucNfTi5YxTtPBTBzegxAZpvuLfh3emXFt6ybUdA==", "dependencies": { "@babel/runtime": "^7.11.2", "@rc-component/portal": "^1.0.2", @@ -7176,9 +7376,9 @@ } }, "node_modules/rc-input": { - "version": "1.7.3", - "resolved": "https://registry.npmmirror.com/rc-input/-/rc-input-1.7.3.tgz", - "integrity": "sha512-A5w4egJq8+4JzlQ55FfQjDnPvOaAbzwC3VLOAdOytyek3TboSOP9qxN+Gifup+shVXfvecBLBbWBpWxmk02SWQ==", + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/rc-input/-/rc-input-1.8.0.tgz", + "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==", "dependencies": { "@babel/runtime": "^7.11.1", "classnames": "^2.2.1", @@ -7190,14 +7390,14 @@ } }, "node_modules/rc-input-number": { - "version": "9.4.0", - "resolved": "https://registry.npmmirror.com/rc-input-number/-/rc-input-number-9.4.0.tgz", - "integrity": "sha512-Tiy4DcXcFXAf9wDhN8aUAyMeCLHJUHA/VA/t7Hj8ZEx5ETvxG7MArDOSE6psbiSCo+vJPm4E3fGN710ITVn6GA==", + "version": "9.5.0", + "resolved": "https://registry.npmmirror.com/rc-input-number/-/rc-input-number-9.5.0.tgz", + "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/mini-decimal": "^1.0.1", "classnames": "^2.2.5", - "rc-input": "~1.7.1", + "rc-input": "~1.8.0", "rc-util": "^5.40.1" }, "peerDependencies": { @@ -7206,16 +7406,16 @@ } }, "node_modules/rc-mentions": { - "version": "2.19.1", - "resolved": "https://registry.npmmirror.com/rc-mentions/-/rc-mentions-2.19.1.tgz", - "integrity": "sha512-KK3bAc/bPFI993J3necmaMXD2reZTzytZdlTvkeBbp50IGH1BDPDvxLdHDUrpQx2b2TGaVJsn+86BvYa03kGqA==", + "version": "2.20.0", + "resolved": "https://registry.npmmirror.com/rc-mentions/-/rc-mentions-2.20.0.tgz", + "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==", "dependencies": { "@babel/runtime": "^7.22.5", "@rc-component/trigger": "^2.0.0", "classnames": "^2.2.6", - "rc-input": "~1.7.1", + "rc-input": "~1.8.0", "rc-menu": "~9.16.0", - "rc-textarea": "~1.9.0", + "rc-textarea": "~1.10.0", "rc-util": "^5.34.1" }, "peerDependencies": { @@ -7255,9 +7455,9 @@ } }, "node_modules/rc-notification": { - "version": "5.6.3", - "resolved": "https://registry.npmmirror.com/rc-notification/-/rc-notification-5.6.3.tgz", - "integrity": "sha512-42szwnn8VYQoT6GnjO00i1iwqV9D1TTMvxObWsuLwgl0TsOokzhkYiufdtQBsJMFjJravS1hfDKVMHLKLcPE4g==", + "version": "5.6.4", + "resolved": "https://registry.npmmirror.com/rc-notification/-/rc-notification-5.6.4.tgz", + "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", @@ -7490,9 +7690,9 @@ } }, "node_modules/rc-tabs": { - "version": "15.5.1", - "resolved": "https://registry.npmmirror.com/rc-tabs/-/rc-tabs-15.5.1.tgz", - "integrity": "sha512-yiWivLAjEo5d1v2xlseB2dQocsOhkoVSfo1krS8v8r+02K+TBUjSjXIf7dgyVSxp6wRIPv5pMi5hanNUlQMgUA==", + "version": "15.6.0", + "resolved": "https://registry.npmmirror.com/rc-tabs/-/rc-tabs-15.6.0.tgz", + "integrity": "sha512-SQ99Yjc9ewrJCUwoWPKq0FeGL2znWsqPhfcZgsHz1R7bkA2rMNe7CPgOiJkwppdJ98wkLhzs9vPrv21QOE1RyQ==", "dependencies": { "@babel/runtime": "^7.11.2", "classnames": "2.x", @@ -7511,13 +7711,13 @@ } }, "node_modules/rc-textarea": { - "version": "1.9.0", - "resolved": "https://registry.npmmirror.com/rc-textarea/-/rc-textarea-1.9.0.tgz", - "integrity": "sha512-dQW/Bc/MriPBTugj2Kx9PMS5eXCCGn2cxoIaichjbNvOiARlaHdI99j4DTxLl/V8+PIfW06uFy7kjfUIDDKyxQ==", + "version": "1.10.0", + "resolved": "https://registry.npmmirror.com/rc-textarea/-/rc-textarea-1.10.0.tgz", + "integrity": "sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.2.1", - "rc-input": "~1.7.1", + "rc-input": "~1.8.0", "rc-resize-observer": "^1.0.0", "rc-util": "^5.27.0" }, @@ -7693,20 +7893,19 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/react-router": { - "version": "7.4.0", - "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.4.0.tgz", - "integrity": "sha512-Y2g5ObjkvX3VFeVt+0CIPuYd9PpgqCslG7ASSIdN73LwA1nNWzcMLaoMRJfP3prZFI92svxFwbn7XkLJ+UPQ6A==", + "version": "7.5.1", + "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.5.1.tgz", + "integrity": "sha512-/jjU3fcYNd2bwz9Q0xt5TwyiyoO8XjSEFXJY4O/lMAlkGTHWuHRAbR9Etik+lSDqMC7A7mz3UlXzgYT6Vl58sA==", "dependencies": { - "@types/cookie": "^0.6.0", "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0", "turbo-stream": "2.4.0" @@ -7725,11 +7924,11 @@ } }, "node_modules/react-router-dom": { - "version": "7.4.0", - "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.4.0.tgz", - "integrity": "sha512-VlksBPf3n2bijPvnA7nkTsXxMAKOj+bWp4R9c3i+bnwlSOFAGOkJkKhzy/OsRkWaBMICqcAl1JDzh9ZSOze9CA==", + "version": "7.5.1", + "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.5.1.tgz", + "integrity": "sha512-5DPSPc7ENrt2tlKPq0FtpG80ZbqA9aIKEyqX6hSNJDlol/tr6iqCK4crqdsusmOSSotq6zDsn0y3urX9TuTNmA==", "dependencies": { - "react-router": "7.4.0" + "react-router": "7.5.1" }, "engines": { "node": ">=20.0.0" @@ -7954,12 +8153,12 @@ } }, "node_modules/rollup": { - "version": "4.34.6", - "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.34.6.tgz", - "integrity": "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==", + "version": "4.40.0", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", "dev": true, "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.7" }, "bin": { "rollup": "dist/bin/rollup" @@ -7969,25 +8168,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.6", - "@rollup/rollup-android-arm64": "4.34.6", - "@rollup/rollup-darwin-arm64": "4.34.6", - "@rollup/rollup-darwin-x64": "4.34.6", - "@rollup/rollup-freebsd-arm64": "4.34.6", - "@rollup/rollup-freebsd-x64": "4.34.6", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.6", - "@rollup/rollup-linux-arm-musleabihf": "4.34.6", - "@rollup/rollup-linux-arm64-gnu": "4.34.6", - "@rollup/rollup-linux-arm64-musl": "4.34.6", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.6", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.6", - "@rollup/rollup-linux-riscv64-gnu": "4.34.6", - "@rollup/rollup-linux-s390x-gnu": "4.34.6", - "@rollup/rollup-linux-x64-gnu": "4.34.6", - "@rollup/rollup-linux-x64-musl": "4.34.6", - "@rollup/rollup-win32-arm64-msvc": "4.34.6", - "@rollup/rollup-win32-ia32-msvc": "4.34.6", - "@rollup/rollup-win32-x64-msvc": "4.34.6", + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" } }, @@ -8540,9 +8740,9 @@ } }, "node_modules/swr": { - "version": "2.3.2", - "resolved": "https://registry.npmmirror.com/swr/-/swr-2.3.2.tgz", - "integrity": "sha512-RosxFpiabojs75IwQ316DGoDRmOqtiAj0tg8wCcbEu4CiLZBs/a9QNtHV7TUfDXmmlgqij/NqzKq/eLelyv9xA==", + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/swr/-/swr-2.3.3.tgz", + "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" @@ -8552,19 +8752,19 @@ } }, "node_modules/synckit": { - "version": "0.9.2", - "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.9.2.tgz", - "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.11.4.tgz", + "integrity": "sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ==", "dev": true, "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" + "@pkgr/core": "^0.2.3", + "tslib": "^2.8.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/synckit" } }, "node_modules/systemjs": { @@ -8805,9 +9005,9 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/turbo-stream": { "version": "2.4.0", @@ -8913,9 +9113,9 @@ } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.8.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -8944,9 +9144,9 @@ } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "version": "6.21.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -9052,14 +9252,17 @@ "dev": true }, "node_modules/vite": { - "version": "6.2.2", - "resolved": "https://registry.npmmirror.com/vite/-/vite-6.2.2.tgz", - "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", + "version": "6.3.2", + "resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.2.tgz", + "integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==", "dev": true, "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" }, "bin": { "vite": "bin/vite.js" @@ -9122,6 +9325,32 @@ } } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz", @@ -9416,9 +9645,9 @@ } }, "node_modules/zod": { - "version": "3.24.2", - "resolved": "https://registry.npmmirror.com/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "version": "3.24.3", + "resolved": "https://registry.npmmirror.com/zod/-/zod-3.24.3.tgz", + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/ui/package.json b/ui/package.json index d11376fe..1862c30d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -10,56 +10,56 @@ "preview": "vite preview" }, "dependencies": { - "@ant-design/icons": "^5.6.1", - "@ant-design/pro-components": "^2.8.6", + "@ant-design/icons": "^6.0.0", + "@ant-design/pro-components": "^2.8.7", "ahooks": "^3.8.4", - "antd": "^5.24.4", - "antd-zod": "^6.0.1", + "antd": "^5.24.8", + "antd-zod": "^6.1.0", "clsx": "^2.1.1", - "cron-parser": "^5.0.6", + "cron-parser": "^5.1.1", "file-saver": "^2.0.5", - "i18next": "^24.2.3", - "i18next-browser-languagedetector": "^8.0.4", + "i18next": "^25.0.1", + "i18next-browser-languagedetector": "^8.0.5", "immer": "^10.1.1", - "lucide-react": "^0.483.0", + "lucide-react": "^0.503.0", "nanoid": "^5.1.5", - "pocketbase": "^0.25.2", + "pocketbase": "^0.26.0", "radash": "^12.1.0", "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", "react-i18next": "^15.4.1", - "react-router-dom": "^7.4.0", + "react-router-dom": "^7.5.1", "tailwind-merge": "^2.6.0", - "zod": "^3.24.2", + "zod": "^3.24.3", "zustand": "^5.0.3" }, "devDependencies": { "@types/file-saver": "^2.0.7", "@types/fs-extra": "^11.0.4", - "@types/node": "^22.13.10", + "@types/node": "^22.14.1", "@types/react": "^18.3.12", "@types/react-copy-to-clipboard": "^5.0.7", "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^8.27.0", - "@typescript-eslint/parser": "^8.27.0", - "@vitejs/plugin-legacy": "^6.0.2", - "@vitejs/plugin-react": "^4.3.4", + "@typescript-eslint/eslint-plugin": "^8.31.0", + "@typescript-eslint/parser": "^8.31.0", + "@vitejs/plugin-legacy": "^6.1.0", + "@vitejs/plugin-react": "^4.4.1", "autoprefixer": "^10.4.21", "eslint": "^8.57.0", - "eslint-config-prettier": "^10.1.1", + "eslint-config-prettier": "^10.1.2", "eslint-import-resolver-typescript": "^3.8.3", "eslint-plugin-import": "^2.31.0", - "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.19", + "eslint-plugin-react-refresh": "^0.4.20", "eslint-plugin-tailwindcss": "^3.18.0", "fs-extra": "^11.3.0", "postcss": "^8.5.3", "prettier": "^3.5.3", "tailwindcss": "^3.4.17", "tailwindcss-animate": "^1.0.7", - "typescript": "^5.8.2", - "vite": "^6.2.2" + "typescript": "^5.8.3", + "vite": "^6.3.2" } } diff --git a/ui/public/imgs/acme/google.svg b/ui/public/imgs/acme/google.svg deleted file mode 100644 index 78f81c93..00000000 --- a/ui/public/imgs/acme/google.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ui/public/imgs/providers/bunny.svg b/ui/public/imgs/providers/bunny.svg new file mode 100644 index 00000000..edbbab32 --- /dev/null +++ b/ui/public/imgs/providers/bunny.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui/public/imgs/providers/buypass.png b/ui/public/imgs/providers/buypass.png new file mode 100644 index 00000000..f4692538 Binary files /dev/null and b/ui/public/imgs/providers/buypass.png differ diff --git a/ui/public/imgs/providers/dingtalk.svg b/ui/public/imgs/providers/dingtalk.svg new file mode 100644 index 00000000..24d3881a --- /dev/null +++ b/ui/public/imgs/providers/dingtalk.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/email.svg b/ui/public/imgs/providers/email.svg new file mode 100644 index 00000000..a74f0c41 --- /dev/null +++ b/ui/public/imgs/providers/email.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/goedge.png b/ui/public/imgs/providers/goedge.png new file mode 100644 index 00000000..760ab333 Binary files /dev/null and b/ui/public/imgs/providers/goedge.png differ diff --git a/ui/public/imgs/providers/google.svg b/ui/public/imgs/providers/google.svg new file mode 100644 index 00000000..350cdfcc --- /dev/null +++ b/ui/public/imgs/providers/google.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/lark.svg b/ui/public/imgs/providers/lark.svg new file mode 100644 index 00000000..ab32e57c --- /dev/null +++ b/ui/public/imgs/providers/lark.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/acme/letsencrypt.svg b/ui/public/imgs/providers/letsencrypt.svg similarity index 100% rename from ui/public/imgs/acme/letsencrypt.svg rename to ui/public/imgs/providers/letsencrypt.svg diff --git a/ui/public/imgs/providers/mattermost.svg b/ui/public/imgs/providers/mattermost.svg new file mode 100644 index 00000000..18cc2819 --- /dev/null +++ b/ui/public/imgs/providers/mattermost.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/proxmoxve.svg b/ui/public/imgs/providers/proxmoxve.svg new file mode 100644 index 00000000..76e497f1 --- /dev/null +++ b/ui/public/imgs/providers/proxmoxve.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/sslcom.svg b/ui/public/imgs/providers/sslcom.svg new file mode 100644 index 00000000..4cd53176 --- /dev/null +++ b/ui/public/imgs/providers/sslcom.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/telegram.svg b/ui/public/imgs/providers/telegram.svg new file mode 100644 index 00000000..f4cd38c1 --- /dev/null +++ b/ui/public/imgs/providers/telegram.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/wangsu.svg b/ui/public/imgs/providers/wangsu.svg new file mode 100644 index 00000000..276ec1cc --- /dev/null +++ b/ui/public/imgs/providers/wangsu.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/wecom.svg b/ui/public/imgs/providers/wecom.svg new file mode 100644 index 00000000..e99e59a7 --- /dev/null +++ b/ui/public/imgs/providers/wecom.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/acme/zerossl.svg b/ui/public/imgs/providers/zerossl.svg similarity index 100% rename from ui/public/imgs/acme/zerossl.svg rename to ui/public/imgs/providers/zerossl.svg diff --git a/ui/src/components/access/AccessEditDrawer.tsx b/ui/src/components/access/AccessEditDrawer.tsx new file mode 100644 index 00000000..7fa2f5d8 --- /dev/null +++ b/ui/src/components/access/AccessEditDrawer.tsx @@ -0,0 +1,118 @@ +import { useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useControllableValue } from "ahooks"; +import { Button, Drawer, Space, notification } from "antd"; + +import { type AccessModel } from "@/domain/access"; +import { useTriggerElement, useZustandShallowSelector } from "@/hooks"; +import { useAccessesStore } from "@/stores/access"; +import { getErrMsg } from "@/utils/error"; + +import AccessForm, { type AccessFormInstance, type AccessFormProps } from "./AccessForm"; + +export type AccessEditDrawerProps = { + data?: AccessFormProps["initialValues"]; + loading?: boolean; + open?: boolean; + scene: AccessFormProps["scene"]; + trigger?: React.ReactNode; + usage?: AccessFormProps["usage"]; + onOpenChange?: (open: boolean) => void; + afterSubmit?: (record: AccessModel) => void; +}; + +const AccessEditDrawer = ({ data, loading, trigger, scene, usage, afterSubmit, ...props }: AccessEditDrawerProps) => { + const { t } = useTranslation(); + + const [notificationApi, NotificationContextHolder] = notification.useNotification(); + + const { createAccess, updateAccess } = useAccessesStore(useZustandShallowSelector(["createAccess", "updateAccess"])); + + const [open, setOpen] = useControllableValue(props, { + valuePropName: "open", + defaultValuePropName: "defaultOpen", + trigger: "onOpenChange", + }); + + const triggerEl = useTriggerElement(trigger, { onClick: () => setOpen(true) }); + + const formRef = useRef(null); + const [formPending, setFormPending] = useState(false); + + const handleOkClick = async () => { + setFormPending(true); + try { + await formRef.current!.validateFields(); + } catch (err) { + setFormPending(false); + throw err; + } + + try { + let values: AccessModel = formRef.current!.getFieldsValue(); + + if (scene === "add") { + if (data?.id) { + throw "Invalid props: `data`"; + } + + values = await createAccess(values); + } else if (scene === "edit") { + if (!data?.id) { + throw "Invalid props: `data`"; + } + + values = await updateAccess({ ...data, ...values }); + } else { + throw "Invalid props: `scene`"; + } + + afterSubmit?.(values); + setOpen(false); + } catch (err) { + notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + + throw err; + } finally { + setFormPending(false); + } + }; + + const handleCancelClick = () => { + if (formPending) return; + + setOpen(false); + }; + + return ( + <> + {NotificationContextHolder} + + {triggerEl} + + + + + + } + loading={loading} + maskClosable={!formPending} + open={open} + title={t(`access.action.${scene}`)} + width={720} + onClose={() => setOpen(false)} + > + + + + ); +}; + +export default AccessEditDrawer; diff --git a/ui/src/components/access/AccessEditModal.tsx b/ui/src/components/access/AccessEditModal.tsx index 66154866..9cfb5b0f 100644 --- a/ui/src/components/access/AccessEditModal.tsx +++ b/ui/src/components/access/AccessEditModal.tsx @@ -14,13 +14,14 @@ export type AccessEditModalProps = { data?: AccessFormProps["initialValues"]; loading?: boolean; open?: boolean; - preset: AccessFormProps["preset"]; + usage?: AccessFormProps["usage"]; + scene: AccessFormProps["scene"]; trigger?: React.ReactNode; onOpenChange?: (open: boolean) => void; afterSubmit?: (record: AccessModel) => void; }; -const AccessEditModal = ({ data, loading, trigger, preset, afterSubmit, ...props }: AccessEditModalProps) => { +const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, ...props }: AccessEditModalProps) => { const { t } = useTranslation(); const [notificationApi, NotificationContextHolder] = notification.useNotification(); @@ -50,13 +51,13 @@ const AccessEditModal = ({ data, loading, trigger, preset, afterSubmit, ...props try { let values: AccessModel = formRef.current!.getFieldsValue(); - if (preset === "add") { + if (scene === "add") { if (data?.id) { throw "Invalid props: `data`"; } values = await createAccess(values); - } else if (preset === "edit") { + } else if (scene === "edit") { if (!data?.id) { throw "Invalid props: `data`"; } @@ -96,15 +97,15 @@ const AccessEditModal = ({ data, loading, trigger, preset, afterSubmit, ...props confirmLoading={formPending} destroyOnClose loading={loading} - okText={preset === "edit" ? t("common.button.save") : t("common.button.submit")} + okText={scene === "edit" ? t("common.button.save") : t("common.button.submit")} open={open} - title={t(`access.action.${preset}`)} + title={t(`access.action.${scene}`)} width={480} onOk={handleOkClick} onCancel={handleCancelClick} >
- +
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index 487d449a..5f82ad67 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -6,7 +6,7 @@ import { z } from "zod"; import AccessProviderSelect from "@/components/provider/AccessProviderSelect"; import { type AccessModel } from "@/domain/access"; -import { ACCESS_PROVIDERS } from "@/domain/provider"; +import { ACCESS_PROVIDERS, ACCESS_USAGES } from "@/domain/provider"; import { useAntdForm, useAntdFormName } from "@/hooks"; import AccessForm1PanelConfig from "./AccessForm1PanelConfig"; @@ -17,6 +17,7 @@ import AccessFormAzureConfig from "./AccessFormAzureConfig"; import AccessFormBaiduCloudConfig from "./AccessFormBaiduCloudConfig"; import AccessFormBaishanConfig from "./AccessFormBaishanConfig"; import AccessFormBaotaPanelConfig from "./AccessFormBaotaPanelConfig"; +import AccessFormBunnyConfig from "./AccessFormBunnyConfig"; import AccessFormBytePlusConfig from "./AccessFormBytePlusConfig"; import AccessFormCacheFlyConfig from "./AccessFormCacheFlyConfig"; import AccessFormCdnflyConfig from "./AccessFormCdnflyConfig"; @@ -24,44 +25,57 @@ import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig"; import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig"; import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig"; import AccessFormDeSECConfig from "./AccessFormDeSECConfig"; +import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig"; import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig"; import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; import AccessFormDynv6Config from "./AccessFormDynv6Config"; import AccessFormEdgioConfig from "./AccessFormEdgioConfig"; +import AccessFormEmailConfig from "./AccessFormEmailConfig"; import AccessFormGcoreConfig from "./AccessFormGcoreConfig"; import AccessFormGnameConfig from "./AccessFormGnameConfig"; import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; +import AccessFormGoEdgeConfig from "./AccessFormGoEdgeConfig"; +import AccessFormGoogleTrustServicesConfig from "./AccessFormGoogleTrustServicesConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; -import AccessFormLocalConfig from "./AccessFormLocalConfig"; +import AccessFormLarkBotConfig from "./AccessFormLarkBotConfig"; +import AccessFormMattermostConfig from "./AccessFormMattermostConfig"; import AccessFormNamecheapConfig from "./AccessFormNamecheapConfig"; import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig"; import AccessFormNameSiloConfig from "./AccessFormNameSiloConfig"; import AccessFormNS1Config from "./AccessFormNS1Config"; import AccessFormPorkbunConfig from "./AccessFormPorkbunConfig"; import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig"; +import AccessFormProxmoxVEConfig from "./AccessFormProxmoxVEConfig"; import AccessFormQiniuConfig from "./AccessFormQiniuConfig"; import AccessFormRainYunConfig from "./AccessFormRainYunConfig"; import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig"; import AccessFormSSHConfig from "./AccessFormSSHConfig"; +import AccessFormSSLComConfig from "./AccessFormSSLComConfig"; +import AccessFormTelegramConfig from "./AccessFormTelegramConfig"; import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig"; import AccessFormUCloudConfig from "./AccessFormUCloudConfig"; import AccessFormUpyunConfig from "./AccessFormUpyunConfig"; import AccessFormVercelConfig from "./AccessFormVercelConfig"; import AccessFormVolcEngineConfig from "./AccessFormVolcEngineConfig"; +import AccessFormWangsuConfig from "./AccessFormWangsuConfig"; import AccessFormWebhookConfig from "./AccessFormWebhookConfig"; +import AccessFormWeComBotConfig from "./AccessFormWeComBotConfig"; import AccessFormWestcnConfig from "./AccessFormWestcnConfig"; +import AccessFormZeroSSLConfig from "./AccessFormZeroSSLConfig"; type AccessFormFieldValues = Partial>; -type AccessFormPresets = "add" | "edit"; +type AccessFormScenes = "add" | "edit"; +type AccessFormUsages = "both-dns-hosting" | "ca-only" | "notification-only"; export type AccessFormProps = { className?: string; style?: React.CSSProperties; disabled?: boolean; initialValues?: AccessFormFieldValues; - preset: AccessFormPresets; + scene: AccessFormScenes; + usage?: AccessFormUsages; onValuesChange?: (values: AccessFormFieldValues) => void; }; @@ -71,7 +85,7 @@ export type AccessFormInstance = { validateFields: FormInstance["validateFields"]; }; -const AccessForm = forwardRef(({ className, style, disabled, initialValues, preset, onValuesChange }, ref) => { +const AccessForm = forwardRef(({ className, style, disabled, initialValues, usage, scene, onValuesChange }, ref) => { const { t } = useTranslation(); const formSchema = z.object({ @@ -80,14 +94,51 @@ const AccessForm = forwardRef(({ className, .min(1, t("access.form.name.placeholder")) .max(64, t("common.errmsg.string_max", { max: 64 })) .trim(), - provider: z.nativeEnum(ACCESS_PROVIDERS, { message: t("access.form.provider.placeholder") }), + provider: z.nativeEnum(ACCESS_PROVIDERS, { + message: + usage === "ca-only" + ? t("access.form.certificate_authority.placeholder") + : usage === "notification-only" + ? t("access.form.notification_channel.placeholder") + : t("access.form.provider.placeholder"), + }), config: z.any(), + reserve: z.string().nullish(), }); const formRule = createSchemaFieldRule(formSchema); const { form: formInst, formProps } = useAntdForm({ initialValues: initialValues, }); + const providerLabel = useMemo(() => { + switch (usage) { + case "ca-only": + return t("access.form.certificate_authority.label"); + case "notification-only": + return t("access.form.notification_channel.label"); + } + + return t("access.form.provider.label"); + }, [usage]); + const providerPlaceholder = useMemo(() => { + switch (usage) { + case "ca-only": + return t("access.form.certificate_authority.placeholder"); + case "notification-only": + return t("access.form.notification_channel.placeholder"); + } + + return t("access.form.provider.placeholder"); + }, [usage]); + const providerTooltip = useMemo(() => { + switch (usage) { + case "both-dns-hosting": + return ; + } + + return undefined; + }, [usage]); + const fieldProvider = Form.useWatch("provider", formInst); const [nestedFormInst] = Form.useForm(); @@ -121,6 +172,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.BAOTAPANEL: return ; + case ACCESS_PROVIDERS.BUNNY: + return ; case ACCESS_PROVIDERS.BYTEPLUS: return ; case ACCESS_PROVIDERS.CACHEFLY: @@ -135,6 +188,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.DESEC: return ; + case ACCESS_PROVIDERS.DINGTALKBOT: + return ; case ACCESS_PROVIDERS.DNSLA: return ; case ACCESS_PROVIDERS.DOGECLOUD: @@ -147,16 +202,24 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.GODADDY: return ; + case ACCESS_PROVIDERS.GOEDGE: + return ; + case ACCESS_PROVIDERS.GOOGLETRUSTSERVICES: + return ; case ACCESS_PROVIDERS.EDGIO: return ; + case ACCESS_PROVIDERS.EMAIL: + return ; case ACCESS_PROVIDERS.HUAWEICLOUD: return ; case ACCESS_PROVIDERS.JDCLOUD: return ; case ACCESS_PROVIDERS.KUBERNETES: return ; - case ACCESS_PROVIDERS.LOCAL: - return ; + case ACCESS_PROVIDERS.LARKBOT: + return ; + case ACCESS_PROVIDERS.MATTERMOST: + return ; case ACCESS_PROVIDERS.NAMECHEAP: return ; case ACCESS_PROVIDERS.NAMEDOTCOM: @@ -169,6 +232,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.POWERDNS: return ; + case ACCESS_PROVIDERS.PROXMOXVE: + return ; case ACCESS_PROVIDERS.QINIU: return ; case ACCESS_PROVIDERS.RAINYUN: @@ -177,6 +242,10 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.SSH: return ; + case ACCESS_PROVIDERS.TELEGRAM: + return ; + case ACCESS_PROVIDERS.SSLCOM: + return ; case ACCESS_PROVIDERS.TENCENTCLOUD: return ; case ACCESS_PROVIDERS.UCLOUD: @@ -187,10 +256,21 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.VOLCENGINE: return ; + case ACCESS_PROVIDERS.WANGSU: + return ; case ACCESS_PROVIDERS.WEBHOOK: - return ; + return ( + + ); + case ACCESS_PROVIDERS.WECOMBOT: + return ; case ACCESS_PROVIDERS.WESTCN: return ; + case ACCESS_PROVIDERS.ZEROSSL: + return ; } }, [disabled, initialValues?.config, fieldProvider, nestedFormInst, nestedFormName]); @@ -210,6 +290,7 @@ const AccessForm = forwardRef(({ className, getFieldsValue: () => { const values = formInst.getFieldsValue(true); values.config = nestedFormInst.getFieldsValue(); + values.reserve = usage === "ca-only" ? "ca" : usage === "notification-only" ? "notification" : undefined; return values; }, resetFields: (fields) => { @@ -235,13 +316,25 @@ const AccessForm = forwardRef(({ className, - } - > - + + { + if (usage == null) return true; + + switch (usage) { + case "both-dns-hosting": + return record.usages.includes(ACCESS_USAGES.DNS) || record.usages.includes(ACCESS_USAGES.HOSTING); + case "ca-only": + return record.usages.includes(ACCESS_USAGES.CA); + case "notification-only": + return record.usages.includes(ACCESS_USAGES.NOTIFICATION); + } + }} + disabled={scene !== "add"} + placeholder={providerPlaceholder} + showOptionTags={usage == null || (usage === "both-dns-hosting" ? { [ACCESS_USAGES.DNS]: true, [ACCESS_USAGES.HOSTING]: true } : false)} + showSearch={!disabled} + /> diff --git a/ui/src/components/access/AccessForm1PanelConfig.tsx b/ui/src/components/access/AccessForm1PanelConfig.tsx index 1dde96b5..c0762bbd 100644 --- a/ui/src/components/access/AccessForm1PanelConfig.tsx +++ b/ui/src/components/access/AccessForm1PanelConfig.tsx @@ -49,12 +49,7 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal name={formName} onValuesChange={handleFormChange} > - } - > + @@ -67,12 +62,7 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal - } - > + - } - > + @@ -67,12 +62,7 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia - } - > + ; + +export type AccessFormBunnyConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormBunnyConfigFieldValues; + onValuesChange?: (values: AccessFormBunnyConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormBunnyConfigFieldValues => { + return { + apiKey: "", + }; +}; + +const AccessFormBunnyConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormBunnyConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiKey: z + .string() + .nonempty(t("access.form.bunny_api_key.placeholder")) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + } + > + + + +
+ ); +}; + +export default AccessFormBunnyConfig; diff --git a/ui/src/components/access/AccessFormCdnflyConfig.tsx b/ui/src/components/access/AccessFormCdnflyConfig.tsx index ec0afaa2..e95e230b 100644 --- a/ui/src/components/access/AccessFormCdnflyConfig.tsx +++ b/ui/src/components/access/AccessFormCdnflyConfig.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { Form, type FormInstance, Input } from "antd"; +import { Form, type FormInstance, Input, Switch } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; @@ -38,6 +38,7 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal .min(1, t("access.form.cdnfly_api_secret.placeholder")) .max(64, t("common.errmsg.string_max", { max: 64 })) .trim(), + allowInsecureConnections: z.boolean().nullish(), }); const formRule = createSchemaFieldRule(formSchema); @@ -54,12 +55,7 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal name={formName} onValuesChange={handleFormChange} > - } - > + @@ -80,6 +76,13 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal > + + + + ); }; diff --git a/ui/src/components/access/AccessFormCloudflareConfig.tsx b/ui/src/components/access/AccessFormCloudflareConfig.tsx index cd760ed6..a06d753d 100644 --- a/ui/src/components/access/AccessFormCloudflareConfig.tsx +++ b/ui/src/components/access/AccessFormCloudflareConfig.tsx @@ -30,6 +30,11 @@ const AccessFormCloudflareConfig = ({ form: formInst, formName, disabled, initia .min(1, t("access.form.cloudflare_dns_api_token.placeholder")) .max(64, t("common.errmsg.string_max", { max: 64 })) .trim(), + zoneApiToken: z + .string() + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim() + .nullish(), }); const formRule = createSchemaFieldRule(formSchema); @@ -54,6 +59,15 @@ const AccessFormCloudflareConfig = ({ form: formInst, formName, disabled, initia >
+ + } + > + + ); }; diff --git a/ui/src/components/access/AccessFormDingTalkBotConfig.tsx b/ui/src/components/access/AccessFormDingTalkBotConfig.tsx new file mode 100644 index 00000000..35aebf6e --- /dev/null +++ b/ui/src/components/access/AccessFormDingTalkBotConfig.tsx @@ -0,0 +1,68 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForDingTalkBot } from "@/domain/access"; + +type AccessFormDingTalkBotConfigFieldValues = Nullish; + +export type AccessFormDingTalkBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormDingTalkBotConfigFieldValues; + onValuesChange?: (values: AccessFormDingTalkBotConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormDingTalkBotConfigFieldValues => { + return { + webhookUrl: "", + secret: "", + }; +}; + +const AccessFormDingTalkBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDingTalkBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + webhookUrl: z.string().url(t("common.errmsg.url_invalid")), + secret: z.string().nonempty(t("access.form.dingtalkbot_secret.placeholder")).trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormDingTalkBotConfig; diff --git a/ui/src/components/access/AccessFormEmailConfig.tsx b/ui/src/components/access/AccessFormEmailConfig.tsx new file mode 100644 index 00000000..ae79794a --- /dev/null +++ b/ui/src/components/access/AccessFormEmailConfig.tsx @@ -0,0 +1,125 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, InputNumber, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForEmail } from "@/domain/access"; +import { validEmailAddress, validPortNumber } from "@/utils/validators"; + +type AccessFormEmailConfigFieldValues = Nullish; + +export type AccessFormEmailConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormEmailConfigFieldValues; + onValuesChange?: (values: AccessFormEmailConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormEmailConfigFieldValues => { + return { + smtpHost: "", + smtpPort: 465, + smtpTls: true, + username: "", + password: "", + }; +}; + +const AccessFormEmailConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormEmailConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + smtpHost: z + .string() + .min(1, t("access.form.email_smtp_host.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })), + smtpPort: z.preprocess( + (v) => Number(v), + z.number().refine((v) => validPortNumber(v), t("common.errmsg.port_invalid")) + ), + smtpTls: z.boolean().nullish(), + username: z + .string() + .min(1, t("access.form.email_username.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })), + password: z + .string() + .min(1, t("access.form.email_password.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })), + defaultSenderAddress: z + .string() + .nullish() + .refine((v) => { + if (!v) return true; + return validEmailAddress(v); + }, t("common.errmsg.email_invalid")), + defaultReceiverAddress: z + .string() + .nullish() + .refine((v) => { + if (!v) return true; + return validEmailAddress(v); + }, t("common.errmsg.email_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleTlsSwitchChange = (checked: boolean) => { + const oldPort = formInst.getFieldValue("smtpPort"); + const newPort = checked && (oldPort == null || oldPort === 25) ? 465 : !checked && (oldPort == null || oldPort === 465) ? 25 : oldPort; + if (newPort !== oldPort) { + formInst.setFieldValue("smtpPort", newPort); + } + }; + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+
+
+ + + +
+ +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +export default AccessFormEmailConfig; diff --git a/ui/src/components/access/AccessFormGoEdgeConfig.tsx b/ui/src/components/access/AccessFormGoEdgeConfig.tsx new file mode 100644 index 00000000..eb4140f4 --- /dev/null +++ b/ui/src/components/access/AccessFormGoEdgeConfig.tsx @@ -0,0 +1,90 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForGoEdge } from "@/domain/access"; + +type AccessFormGoEdgeConfigFieldValues = Nullish; + +export type AccessFormGoEdgeConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormGoEdgeConfigFieldValues; + onValuesChange?: (values: AccessFormGoEdgeConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormGoEdgeConfigFieldValues => { + return { + apiUrl: "http://:7788/", + accessKeyId: "", + accessKey: "", + }; +}; + +const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormGoEdgeConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiUrl: z.string().url(t("common.errmsg.url_invalid")), + accessKeyId: z + .string() + .min(1, t("access.form.goedge_access_key_id.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + accessKey: z + .string() + .min(1, t("access.form.goedge_access_key.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + allowInsecureConnections: z.boolean().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + } + > + + + + } + > + + + + + + +
+ ); +}; + +export default AccessFormGoEdgeConfig; diff --git a/ui/src/components/access/AccessFormGoogleTrustServicesConfig.tsx b/ui/src/components/access/AccessFormGoogleTrustServicesConfig.tsx new file mode 100644 index 00000000..95eb6270 --- /dev/null +++ b/ui/src/components/access/AccessFormGoogleTrustServicesConfig.tsx @@ -0,0 +1,82 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForGoogleTrustServices } from "@/domain/access"; + +type AccessFormGoogleTrustServicesConfigFieldValues = Nullish; + +export type AccessFormGoogleTrustServicesConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormGoogleTrustServicesConfigFieldValues; + onValuesChange?: (values: AccessFormGoogleTrustServicesConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormGoogleTrustServicesConfigFieldValues => { + return { + eabKid: "", + eabHmacKey: "", + }; +}; + +const AccessFormGoogleTrustServicesConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: AccessFormGoogleTrustServicesConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + eabKid: z + .string() + .min(1, t("access.form.googletrustservices_eab_kid.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), + eabHmacKey: z + .string() + .min(1, t("access.form.googletrustservices_eab_hmac_key.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormGoogleTrustServicesConfig; diff --git a/ui/src/components/access/AccessFormLarkBotConfig.tsx b/ui/src/components/access/AccessFormLarkBotConfig.tsx new file mode 100644 index 00000000..2a07505e --- /dev/null +++ b/ui/src/components/access/AccessFormLarkBotConfig.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForLarkBot } from "@/domain/access"; + +type AccessFormLarkBotConfigFieldValues = Nullish; + +export type AccessFormLarkBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormLarkBotConfigFieldValues; + onValuesChange?: (values: AccessFormLarkBotConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormLarkBotConfigFieldValues => { + return { + webhookUrl: "", + }; +}; + +const AccessFormLarkBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormLarkBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + webhookUrl: z.string().url(t("common.errmsg.url_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormLarkBotConfig; diff --git a/ui/src/components/access/AccessFormLocalConfig.tsx b/ui/src/components/access/AccessFormLocalConfig.tsx deleted file mode 100644 index cde72374..00000000 --- a/ui/src/components/access/AccessFormLocalConfig.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Form, type FormInstance } from "antd"; - -import { type AccessConfigForLocal } from "@/domain/access"; - -type AccessFormLocalConfigFieldValues = Nullish; - -export type AccessFormLocalConfigProps = { - form: FormInstance; - formName: string; - disabled?: boolean; - initialValues?: AccessFormLocalConfigFieldValues; - onValuesChange?: (values: AccessFormLocalConfigFieldValues) => void; -}; - -const initFormModel = (): AccessFormLocalConfigFieldValues => { - return {}; -}; - -const AccessFormLocalConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormLocalConfigProps) => { - const handleFormChange = (_: unknown, values: any) => { - onValuesChange?.(values); - }; - - return ( -
- ); -}; - -export default AccessFormLocalConfig; diff --git a/ui/src/components/access/AccessFormMattermostConfig.tsx b/ui/src/components/access/AccessFormMattermostConfig.tsx new file mode 100644 index 00000000..a583cc19 --- /dev/null +++ b/ui/src/components/access/AccessFormMattermostConfig.tsx @@ -0,0 +1,79 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForMattermost } from "@/domain/access"; + +type AccessFormMattermostConfigFieldValues = Nullish; + +export type AccessFormMattermostConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormMattermostConfigFieldValues; + onValuesChange?: (values: AccessFormMattermostConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormMattermostConfigFieldValues => { + return { + serverUrl: "http://:8065/", + username: "", + password: "", + }; +}; + +const AccessFormMattermostConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormMattermostConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + serverUrl: z.string().url(t("common.errmsg.url_invalid")), + username: z.string().nonempty(t("access.form.mattermost_username.placeholder")), + password: z.string().nonempty(t("access.form.mattermost_password.placeholder")), + defaultChannelId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + + + + + + + + + } + > + + +
+ ); +}; + +export default AccessFormMattermostConfig; diff --git a/ui/src/components/access/AccessFormPowerDNSConfig.tsx b/ui/src/components/access/AccessFormPowerDNSConfig.tsx index cada5b61..b2bfb081 100644 --- a/ui/src/components/access/AccessFormPowerDNSConfig.tsx +++ b/ui/src/components/access/AccessFormPowerDNSConfig.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { Form, type FormInstance, Input } from "antd"; +import { Form, type FormInstance, Input, Switch } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; @@ -32,6 +32,7 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV .min(1, t("access.form.powerdns_api_key.placeholder")) .max(64, t("common.errmsg.string_max", { max: 64 })) .trim(), + allowInsecureConnections: z.boolean().nullish(), }); const formRule = createSchemaFieldRule(formSchema); @@ -48,12 +49,7 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - } - > + @@ -65,6 +61,13 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV > + + + + ); }; diff --git a/ui/src/components/access/AccessFormProxmoxVEConfig.tsx b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx new file mode 100644 index 00000000..afdc02de --- /dev/null +++ b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx @@ -0,0 +1,81 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForProxmoxVE } from "@/domain/access"; + +type AccessFormProxmoxVEConfigFieldValues = Nullish; + +export type AccessFormProxmoxVEConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormProxmoxVEConfigFieldValues; + onValuesChange?: (values: AccessFormProxmoxVEConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormProxmoxVEConfigFieldValues => { + return { + apiUrl: "http://:8006/", + apiToken: "", + }; +}; + +const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormProxmoxVEConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiUrl: z.string().url(t("common.errmsg.url_invalid")), + apiToken: z.string().nonempty(t("access.form.proxmoxve_api_token.placeholder")).trim(), + apiTokenSecret: z.string().nullish(), + allowInsecureConnections: z.boolean().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + } + > + + + + } + > + + + + + + +
+ ); +}; + +export default AccessFormProxmoxVEConfig; diff --git a/ui/src/components/access/AccessFormSSHConfig.tsx b/ui/src/components/access/AccessFormSSHConfig.tsx index d455a5fa..ebaeba90 100644 --- a/ui/src/components/access/AccessFormSSHConfig.tsx +++ b/ui/src/components/access/AccessFormSSHConfig.tsx @@ -7,7 +7,7 @@ import { z } from "zod"; import { type AccessConfigForSSH } from "@/domain/access"; import { readFileContent } from "@/utils/file"; -import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/validators"; +import { validDomainName, validIPv4Address, validIPv6Address, validPortNumber } from "@/utils/validators"; type AccessFormSSHConfigFieldValues = Nullish; @@ -31,14 +31,14 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues const { t } = useTranslation(); const formSchema = z.object({ - host: z - .string({ message: t("access.form.ssh_host.placeholder") }) - .refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")), - port: z - .number({ message: t("access.form.ssh_port.placeholder") }) - .int() - .gte(1, t("common.errmsg.port_invalid")) - .lte(65535, t("common.errmsg.port_invalid")), + host: z.string().refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")), + port: z.preprocess( + (v) => Number(v), + z + .number() + .int(t("access.form.ssh_port.placeholder")) + .refine((v) => validPortNumber(v), t("common.errmsg.port_invalid")) + ), username: z .string() .min(1, "access.form.ssh_username.placeholder") diff --git a/ui/src/components/access/AccessFormSSLComConfig.tsx b/ui/src/components/access/AccessFormSSLComConfig.tsx new file mode 100644 index 00000000..85266266 --- /dev/null +++ b/ui/src/components/access/AccessFormSSLComConfig.tsx @@ -0,0 +1,76 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForSSLCom } from "@/domain/access"; + +type AccessFormSSLComConfigFieldValues = Nullish; + +export type AccessFormSSLComConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormSSLComConfigFieldValues; + onValuesChange?: (values: AccessFormSSLComConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormSSLComConfigFieldValues => { + return { + eabKid: "", + eabHmacKey: "", + }; +}; + +const AccessFormSSLComConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormSSLComConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + eabKid: z + .string() + .min(1, t("access.form.sslcom_eab_kid.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), + eabHmacKey: z + .string() + .min(1, t("access.form.sslcom_eab_hmac_key.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormSSLComConfig; diff --git a/ui/src/components/access/AccessFormSafeLineConfig.tsx b/ui/src/components/access/AccessFormSafeLineConfig.tsx index 5b16c508..2fde089d 100644 --- a/ui/src/components/access/AccessFormSafeLineConfig.tsx +++ b/ui/src/components/access/AccessFormSafeLineConfig.tsx @@ -49,12 +49,7 @@ const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - } - > + @@ -67,12 +62,7 @@ const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialV - } - > + ; + +export type AccessFormTelegramConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormTelegramConfigFieldValues; + onValuesChange?: (values: AccessFormTelegramConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormTelegramConfigFieldValues => { + return { + botToken: "", + }; +}; + +const AccessFormTelegramConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormTelegramConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + botToken: z + .string({ message: t("access.form.telegram_bot_token.placeholder") }) + .min(1, t("access.form.telegram_bot_token.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })), + defaultChatId: z + .preprocess( + (v) => (v == null || v === "" ? undefined : Number(v)), + z + .number() + .nullish() + .refine((v) => { + if (v == null || v + "" === "") return true; + return /^\d+$/.test(v + "") && +v! > 0; + }, t("access.form.telegram_default_chat_id.placeholder")) + ) + .nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormTelegramConfig; diff --git a/ui/src/components/access/AccessFormWangsuConfig.tsx b/ui/src/components/access/AccessFormWangsuConfig.tsx new file mode 100644 index 00000000..bb4f699c --- /dev/null +++ b/ui/src/components/access/AccessFormWangsuConfig.tsx @@ -0,0 +1,91 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForWangsu } from "@/domain/access"; + +type AccessFormWangsuConfigFieldValues = Nullish; + +export type AccessFormWangsuConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormWangsuConfigFieldValues; + onValuesChange?: (values: AccessFormWangsuConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormWangsuConfigFieldValues => { + return { + accessKeyId: "", + accessKeySecret: "", + apiKey: "", + }; +}; + +const AccessFormWangsuConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange: onValuesChange }: AccessFormWangsuConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + accessKeyId: z + .string() + .min(1, t("access.form.wangsu_access_key_id.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + accessKeySecret: z + .string() + .min(1, t("access.form.wangsu_access_key_secret.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + apiKey: z + .string() + .min(1, t("access.form.wangsu_api_key.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormWangsuConfig; diff --git a/ui/src/components/access/AccessFormWeComBotConfig.tsx b/ui/src/components/access/AccessFormWeComBotConfig.tsx new file mode 100644 index 00000000..16a205f5 --- /dev/null +++ b/ui/src/components/access/AccessFormWeComBotConfig.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForWeComBot } from "@/domain/access"; + +type AccessFormWeComBotConfigFieldValues = Nullish; + +export type AccessFormWeComBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormWeComBotConfigFieldValues; + onValuesChange?: (values: AccessFormWeComBotConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormWeComBotConfigFieldValues => { + return { + webhookUrl: "", + }; +}; + +const AccessFormWeComBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormWeComBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + webhookUrl: z.string().url(t("common.errmsg.url_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormWeComBotConfig; diff --git a/ui/src/components/access/AccessFormWebhookConfig.tsx b/ui/src/components/access/AccessFormWebhookConfig.tsx index 89280d79..4bbc6754 100644 --- a/ui/src/components/access/AccessFormWebhookConfig.tsx +++ b/ui/src/components/access/AccessFormWebhookConfig.tsx @@ -1,8 +1,10 @@ import { useTranslation } from "react-i18next"; -import { Form, type FormInstance, Input, Switch } from "antd"; +import { DownOutlined as DownOutlinedIcon } from "@ant-design/icons"; +import { Alert, Button, Dropdown, Form, type FormInstance, Input, Select, Switch } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; +import Show from "@/components/Show"; import { type AccessConfigForWebhook } from "@/domain/access"; type AccessFormWebhookConfigFieldValues = Nullish; @@ -12,24 +14,241 @@ export type AccessFormWebhookConfigProps = { formName: string; disabled?: boolean; initialValues?: AccessFormWebhookConfigFieldValues; + usage?: "deployment" | "notification" | "none"; onValuesChange?: (values: AccessFormWebhookConfigFieldValues) => void; }; const initFormModel = (): AccessFormWebhookConfigFieldValues => { return { url: "", + method: "POST", + headers: "Content-Type: application/json", + allowInsecureConnections: false, + defaultDataForDeployment: JSON.stringify( + { + name: "${DOMAINS}", + cert: "${CERTIFICATE}", + privkey: "${PRIVATE_KEY}", + }, + null, + 2 + ), + defaultDataForNotification: JSON.stringify( + { + subject: "${SUBJECT}", + message: "${MESSAGE}", + }, + null, + 2 + ), }; }; -const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormWebhookConfigProps) => { +const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialValues, usage, onValuesChange }: AccessFormWebhookConfigProps) => { const { t } = useTranslation(); const formSchema = z.object({ - url: z.string({ message: t("access.form.webhook_url.placeholder") }).url(t("common.errmsg.url_invalid")), + url: z.string().url(t("common.errmsg.url_invalid")), + method: z.union([z.literal("GET"), z.literal("POST"), z.literal("PUT"), z.literal("PATCH"), z.literal("DELETE")], { + message: t("access.form.webhook_method.placeholder"), + }), + headers: z + .string() + .nullish() + .refine((v) => { + if (!v) return true; + + const lines = v.split(/\r?\n/); + for (const line of lines) { + if (line.split(":").length < 2) { + return false; + } + } + return true; + }, t("access.form.webhook_headers.errmsg.invalid")), allowInsecureConnections: z.boolean().nullish(), + defaultDataForDeployment: z + .string() + .nullish() + .refine((v) => { + if (usage && usage !== "deployment") return true; + if (!v) return true; + + try { + const obj = JSON.parse(v); + return typeof obj === "object" && !Array.isArray(obj); + } catch { + return false; + } + }, t("access.form.webhook_default_data.errmsg.json_invalid")), + defaultDataForNotification: z + .string() + .nullish() + .refine((v) => { + if (usage && usage !== "notification") return true; + if (!v) return true; + + try { + const obj = JSON.parse(v); + return typeof obj === "object" && !Array.isArray(obj); + } catch { + return false; + } + }, t("access.form.webhook_default_data.errmsg.json_invalid")), }); const formRule = createSchemaFieldRule(formSchema); + const handleWebhookHeadersBlur = (e: React.FocusEvent) => { + let value = e.target.value; + value = value.trim(); + value = value.replace(/(?) => { + const value = e.target.value; + try { + const json = JSON.stringify(JSON.parse(value), null, 2); + formInst.setFieldValue("defaultDataForDeployment", json); + } catch { + return; + } + }; + + const handleWebhookDataForNotificationBlur = (e: React.FocusEvent) => { + const value = e.target.value; + try { + const json = JSON.stringify(JSON.parse(value), null, 2); + formInst.setFieldValue("defaultDataForNotification", json); + } catch { + return; + } + }; + + const handlePresetDataForDeploymentClick = () => { + formInst.setFieldValue("defaultDataForDeployment", initFormModel().defaultDataForDeployment); + }; + + const handlePresetDataForNotificationClick = (key: string) => { + switch (key) { + case "bark": + formInst.setFieldValue("url", "https://api.day.app/push"); + formInst.setFieldValue("method", "POST"); + formInst.setFieldValue("headers", "Content-Type: application/json\r\nAuthorization: Bearer "); + formInst.setFieldValue( + "defaultDataForNotification", + JSON.stringify( + { + title: "${SUBJECT}", + body: "${MESSAGE}", + group: "", + device_keys: "", + }, + null, + 2 + ) + ); + break; + + case "gotify": + formInst.setFieldValue("url", "https:///"); + formInst.setFieldValue("method", "POST"); + formInst.setFieldValue("headers", "Content-Type: application/json\r\nAuthorization: Bearer "); + formInst.setFieldValue( + "defaultDataForNotification", + JSON.stringify( + { + title: "${SUBJECT}", + message: "${MESSAGE}", + priority: 1, + }, + null, + 2 + ) + ); + break; + + case "ntfy": + formInst.setFieldValue("url", "https:///"); + formInst.setFieldValue("method", "POST"); + formInst.setFieldValue("headers", "Content-Type: application/json"); + formInst.setFieldValue( + "defaultDataForNotification", + JSON.stringify( + { + topic: "", + title: "${SUBJECT}", + message: "${MESSAGE}", + priority: 1, + }, + null, + 2 + ) + ); + break; + + case "pushover": + formInst.setFieldValue("url", "https://api.pushover.net/1/messages.json"); + formInst.setFieldValue("method", "POST"); + formInst.setFieldValue("headers", "Content-Type: application/json"); + formInst.setFieldValue( + "defaultDataForNotification", + JSON.stringify( + { + token: "", + user: "", + title: "${SUBJECT}", + message: "${MESSAGE}", + }, + null, + 2 + ) + ); + break; + + case "pushplus": + formInst.setFieldValue("url", "https://www.pushplus.plus/send"); + formInst.setFieldValue("method", "POST"); + formInst.setFieldValue("headers", "Content-Type: application/json"); + formInst.setFieldValue( + "defaultDataForNotification", + JSON.stringify( + { + token: "", + title: "${SUBJECT}", + content: "${MESSAGE}", + }, + null, + 2 + ) + ); + break; + + case "serverchan": + formInst.setFieldValue("url", "https://sctapi.ftqq.com/.send"); + formInst.setFieldValue("method", "POST"); + formInst.setFieldValue("headers", "Content-Type: application/json"); + formInst.setFieldValue( + "defaultDataForNotification", + JSON.stringify( + { + text: "${SUBJECT}", + desp: "${MESSAGE}", + }, + null, + 2 + ) + ); + break; + + default: + formInst.setFieldValue("method", "POST"); + formInst.setFieldValue("headers", "Content-Type: application/json"); + formInst.setFieldValue("defaultDataForNotification", initFormModel().defaultDataForNotification); + break; + } + }; + const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); }; @@ -47,12 +266,93 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
+ + + + + } + > + + + + ); +}; + +export default AccessFormZeroSSLConfig; diff --git a/ui/src/components/certificate/CertificateDetailDrawer.tsx b/ui/src/components/certificate/CertificateDetailDrawer.tsx index 2cedeb2b..361beb96 100644 --- a/ui/src/components/certificate/CertificateDetailDrawer.tsx +++ b/ui/src/components/certificate/CertificateDetailDrawer.tsx @@ -29,7 +29,6 @@ const CertificateDetailDrawer = ({ data, loading, trigger, ...props }: Certifica ["validateFields"]; }; +/** + * @deprecated + */ const NotifyChannelEditForm = forwardRef( ({ className, style, channel, disabled, initialValues, onValuesChange }, ref) => { const { form: formInst, formProps } = useAntdForm({ @@ -48,8 +55,16 @@ const NotifyChannelEditForm = forwardRef; case NOTIFY_CHANNELS.EMAIL: return ; + case NOTIFY_CHANNELS.GOTIFY: + return ; case NOTIFY_CHANNELS.LARK: return ; + case NOTIFY_CHANNELS.MATTERMOST: + return ; + case NOTIFY_CHANNELS.PUSHOVER: + return ; + case NOTIFY_CHANNELS.PUSHPLUS: + return ; case NOTIFY_CHANNELS.SERVERCHAN: return ; case NOTIFY_CHANNELS.TELEGRAM: diff --git a/ui/src/components/notification/NotifyChannelEditFormEmailFields.tsx b/ui/src/components/notification/NotifyChannelEditFormEmailFields.tsx index 615950bb..d7c08c4f 100644 --- a/ui/src/components/notification/NotifyChannelEditFormEmailFields.tsx +++ b/ui/src/components/notification/NotifyChannelEditFormEmailFields.tsx @@ -3,6 +3,8 @@ import { Form, Input, InputNumber, Switch } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; +import { validPortNumber } from "@/utils/validators"; + const NotifyChannelEditFormEmailFields = () => { const { t } = useTranslation(); @@ -11,11 +13,13 @@ const NotifyChannelEditFormEmailFields = () => { .string({ message: t("settings.notification.channel.form.email_smtp_host.placeholder") }) .min(1, t("settings.notification.channel.form.email_smtp_host.placeholder")) .max(256, t("common.errmsg.string_max", { max: 256 })), - smtpPort: z - .number({ message: t("settings.notification.channel.form.email_smtp_port.placeholder") }) - .int() - .gte(1, t("common.errmsg.port_invalid")) - .lte(65535, t("common.errmsg.port_invalid")), + smtpPort: z.preprocess( + (v) => Number(v), + z + .number({ message: t("settings.notification.channel.form.email_smtp_port.placeholder") }) + .int(t("settings.notification.channel.form.email_smtp_port.placeholder")) + .refine((v) => validPortNumber(v), t("common.errmsg.port_invalid")) + ), smtpTLS: z.boolean().nullish(), username: z .string({ message: t("settings.notification.channel.form.email_username.placeholder") }) diff --git a/ui/src/components/notification/NotifyChannelEditFormGotifyFields.tsx b/ui/src/components/notification/NotifyChannelEditFormGotifyFields.tsx new file mode 100644 index 00000000..189f4e16 --- /dev/null +++ b/ui/src/components/notification/NotifyChannelEditFormGotifyFields.tsx @@ -0,0 +1,46 @@ +import { useTranslation } from "react-i18next"; +import { Form, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +const NotifyChannelEditFormGotifyFields = () => { + const { t } = useTranslation(); + + const formSchema = z.object({ + url: z.string({ message: t("settings.notification.channel.form.gotify_url.placeholder") }).url({ message: t("common.errmsg.url_invalid") }), + token: z.string({ message: t("settings.notification.channel.form.gotify_token.placeholder") }), + priority: z.preprocess(val => Number(val), z.number({ message: t("settings.notification.channel.form.gotify_priority.placeholder") }).gte(0, { message: t("settings.notification.channel.form.gotify_priority.error.gte0") })), + }); + const formRule = createSchemaFieldRule(formSchema); + + return ( + <> + } + > + + + } + > + + + } + > + + + + ); +}; + +export default NotifyChannelEditFormGotifyFields; diff --git a/ui/src/components/notification/NotifyChannelEditFormMattermostFields.tsx b/ui/src/components/notification/NotifyChannelEditFormMattermostFields.tsx new file mode 100644 index 00000000..829a2a0d --- /dev/null +++ b/ui/src/components/notification/NotifyChannelEditFormMattermostFields.tsx @@ -0,0 +1,54 @@ +import { useTranslation } from "react-i18next"; +import { Form, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +const NotifyChannelEditFormMattermostFields = () => { + const { t } = useTranslation(); + + const formSchema = z.object({ + serverUrl: z.string({ message: t("settings.notification.channel.form.mattermost_server_url.placeholder") }).url(t("common.errmsg.url_invalid")), + channelId: z + .string({ message: t("settings.notification.channel.form.mattermost_channel_id.placeholder") }) + .nonempty(t("settings.notification.channel.form.mattermost_channel_id.placeholder")), + username: z + .string({ message: t("settings.notification.channel.form.mattermost_username.placeholder") }) + .nonempty(t("settings.notification.channel.form.mattermost_username.placeholder")), + password: z + .string({ message: t("settings.notification.channel.form.mattermost_password.placeholder") }) + .nonempty(t("settings.notification.channel.form.mattermost_password.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + return ( + <> + } + > + + + + } + > + + + + + + + + + + + + ); +}; + +export default NotifyChannelEditFormMattermostFields; diff --git a/ui/src/components/notification/NotifyChannelEditFormPushPlusFields.tsx b/ui/src/components/notification/NotifyChannelEditFormPushPlusFields.tsx new file mode 100644 index 00000000..36f6e21a --- /dev/null +++ b/ui/src/components/notification/NotifyChannelEditFormPushPlusFields.tsx @@ -0,0 +1,28 @@ +import { useTranslation } from "react-i18next"; +import { Form, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +const NotifyChannelEditFormPushPlusFields = () => { + const { t } = useTranslation(); + + const formSchema = z.object({ + token: z.string({ message: t("settings.notification.channel.form.pushplus_token.placeholder") }), + }); + const formRule = createSchemaFieldRule(formSchema); + + return ( + <> + } + > + + + + ); +}; + +export default NotifyChannelEditFormPushPlusFields; diff --git a/ui/src/components/notification/NotifyChannelEditFormPushoverFields.tsx b/ui/src/components/notification/NotifyChannelEditFormPushoverFields.tsx new file mode 100644 index 00000000..449c98fa --- /dev/null +++ b/ui/src/components/notification/NotifyChannelEditFormPushoverFields.tsx @@ -0,0 +1,41 @@ +import { useTranslation } from "react-i18next"; +import { Form, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +const NotifyChannelEditFormPushoverFields = () => { + const { t } = useTranslation(); + + const formSchema = z.object({ + token: z + .string({ message: t("settings.notification.channel.form.pushover_token.placeholder") }) + .nonempty(t("settings.notification.channel.form.pushover_token.placeholder")), + user: z + .string({ message: t("settings.notification.channel.form.pushover_user.placeholder") }) + .nonempty(t("settings.notification.channel.form.pushover_user.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + return ( + <> + } + > + + + } + > + + + + ); +}; + +export default NotifyChannelEditFormPushoverFields; diff --git a/ui/src/components/provider/ApplyDNSProviderPicker.tsx b/ui/src/components/provider/ACMEDns01ProviderPicker.tsx similarity index 65% rename from ui/src/components/provider/ApplyDNSProviderPicker.tsx rename to ui/src/components/provider/ACMEDns01ProviderPicker.tsx index 10fa39ff..7f99cf6a 100644 --- a/ui/src/components/provider/ApplyDNSProviderPicker.tsx +++ b/ui/src/components/provider/ACMEDns01ProviderPicker.tsx @@ -1,11 +1,11 @@ -import { memo, useEffect, useRef, useState } from "react"; +import { memo, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Avatar, Card, Col, Empty, Flex, Input, type InputRef, Row, Typography } from "antd"; import Show from "@/components/Show"; -import { applyDNSProvidersMap } from "@/domain/provider"; +import { acmeDns01ProvidersMap } from "@/domain/provider"; -export type ApplyDNSProviderPickerProps = { +export type ACMEDns01ProviderPickerProps = { className?: string; style?: React.CSSProperties; autoFocus?: boolean; @@ -13,7 +13,7 @@ export type ApplyDNSProviderPickerProps = { onSelect?: (value: string) => void; }; -const ApplyDNSProviderPicker = ({ className, style, autoFocus, placeholder, onSelect }: ApplyDNSProviderPickerProps) => { +const ACMEDns01ProviderPicker = ({ className, style, autoFocus, placeholder, onSelect }: ACMEDns01ProviderPickerProps) => { const { t } = useTranslation(); const [keyword, setKeyword] = useState(); @@ -24,15 +24,16 @@ const ApplyDNSProviderPicker = ({ className, style, autoFocus, placeholder, onSe } }, []); - const providers = Array.from(applyDNSProvidersMap.values()); - const filteredProviders = providers.filter((provider) => { - if (keyword) { - const value = keyword.toLowerCase(); - return provider.type.toLowerCase().includes(value) || t(provider.name).toLowerCase().includes(value); - } + const providers = useMemo(() => { + return Array.from(acmeDns01ProvidersMap.values()).filter((provider) => { + if (keyword) { + const value = keyword.toLowerCase(); + return provider.type.toLowerCase().includes(value) || t(provider.name).toLowerCase().includes(value); + } - return true; - }); + return true; + }); + }, [keyword]); const handleProviderTypeSelect = (value: string) => { onSelect?.(value); @@ -40,12 +41,12 @@ const ApplyDNSProviderPicker = ({ className, style, autoFocus, placeholder, onSe return (
- setKeyword(e.target.value.trim())} /> + setKeyword(e.target.value.trim())} />
- 0} fallback={}> + 0} fallback={}> - {filteredProviders.map((provider, index) => { + {providers.map((provider, index) => { return ( & { - filter?: (record: DeployProvider) => boolean; + filter?: (record: ACMEDns01Provider) => boolean; }; -const DeployProviderSelect = ({ filter, ...props }: DeployProviderSelectProps) => { +const ACMEDns01ProviderSelect = ({ filter, ...props }: ACMEDns01ProviderSelectProps) => { const { t } = useTranslation(); - const [options, setOptions] = useState>([]); + const [options, setOptions] = useState>([]); useEffect(() => { - const allItems = Array.from(deployProvidersMap.values()); + const allItems = Array.from(acmeDns01ProvidersMap.values()); const filteredItems = filter != null ? allItems.filter(filter) : allItems; setOptions( filteredItems.map((item) => ({ @@ -29,7 +29,7 @@ const DeployProviderSelect = ({ filter, ...props }: DeployProviderSelectProps) = }, [filter]); const renderOption = (key: string) => { - const provider = deployProvidersMap.get(key); + const provider = acmeDns01ProvidersMap.get(key); return ( @@ -64,4 +64,4 @@ const DeployProviderSelect = ({ filter, ...props }: DeployProviderSelectProps) = ); }; -export default memo(DeployProviderSelect); +export default memo(ACMEDns01ProviderSelect); diff --git a/ui/src/components/provider/AccessProviderSelect.tsx b/ui/src/components/provider/AccessProviderSelect.tsx index 0e0a992c..79f1539e 100644 --- a/ui/src/components/provider/AccessProviderSelect.tsx +++ b/ui/src/components/provider/AccessProviderSelect.tsx @@ -1,17 +1,19 @@ -import { memo, useEffect, useState } from "react"; +import { memo, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Avatar, Select, type SelectProps, Space, Tag, Typography } from "antd"; -import { ACCESS_USAGES, type AccessProvider, accessProvidersMap } from "@/domain/provider"; +import Show from "@/components/Show"; +import { ACCESS_USAGES, type AccessProvider, type AccessUsageType, accessProvidersMap } from "@/domain/provider"; export type AccessProviderSelectProps = Omit< SelectProps, "filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender" > & { filter?: (record: AccessProvider) => boolean; + showOptionTags?: boolean | { [key in AccessUsageType]?: boolean }; }; -const AccessProviderSelect = ({ filter, ...props }: AccessProviderSelectProps) => { +const AccessProviderSelect = ({ filter, showOptionTags, ...props }: AccessProviderSelectProps = { showOptionTags: true }) => { const { t } = useTranslation(); const [options, setOptions] = useState>([]); @@ -23,32 +25,51 @@ const AccessProviderSelect = ({ filter, ...props }: AccessProviderSelectProps) = key: item.type, value: item.type, label: t(item.name), + disabled: item.builtin, data: item, })) ); }, [filter]); + const showOptionTagForDNS = useMemo(() => { + return typeof showOptionTags === "object" ? !!showOptionTags[ACCESS_USAGES.DNS] : !!showOptionTags; + }, [showOptionTags]); + const showOptionTagForHosting = useMemo(() => { + return typeof showOptionTags === "object" ? !!showOptionTags[ACCESS_USAGES.HOSTING] : !!showOptionTags; + }, [showOptionTags]); + const showOptionTagForCA = useMemo(() => { + return typeof showOptionTags === "object" ? !!showOptionTags[ACCESS_USAGES.CA] : !!showOptionTags; + }, [showOptionTags]); + const showOptionTagForNotification = useMemo(() => { + return typeof showOptionTags === "object" ? !!showOptionTags[ACCESS_USAGES.NOTIFICATION] : !!showOptionTags; + }, [showOptionTags]); + const renderOption = (key: string) => { - const provider = accessProvidersMap.get(key); + const provider = accessProvidersMap.get(key) ?? ({ type: "", name: "", icon: "", usages: [] } as unknown as AccessProvider); return (
- - - {t(provider?.name ?? "")} + + + {t(provider.name)}
- {provider?.usages?.includes(ACCESS_USAGES.APPLY) && ( - <> - {t("access.props.provider.usage.dns")} - - )} - {provider?.usages?.includes(ACCESS_USAGES.DEPLOY) && ( - <> - {t("access.props.provider.usage.host")} - - )} + + {t("access.props.provider.builtin")} + + + {t("access.props.provider.usage.dns")} + + + {t("access.props.provider.usage.hosting")} + + + {t("access.props.provider.usage.ca")} + + + {t("access.props.provider.usage.notification")} +
); diff --git a/ui/src/components/provider/CAProviderSelect.tsx b/ui/src/components/provider/CAProviderSelect.tsx new file mode 100644 index 00000000..15d31230 --- /dev/null +++ b/ui/src/components/provider/CAProviderSelect.tsx @@ -0,0 +1,83 @@ +import { memo, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Avatar, Select, type SelectProps, Space, Typography } from "antd"; + +import { type CAProvider, caProvidersMap } from "@/domain/provider"; + +export type CAProviderSelectProps = Omit< + SelectProps, + "filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender" +> & { + filter?: (record: CAProvider) => boolean; +}; + +const CAProviderSelect = ({ filter, ...props }: CAProviderSelectProps) => { + const { t } = useTranslation(); + + const [options, setOptions] = useState>([]); + useEffect(() => { + const allItems = Array.from(caProvidersMap.values()); + const filteredItems = filter != null ? allItems.filter(filter) : allItems; + setOptions([ + { + key: "", + value: "", + label: t("provider.default_ca_provider.label"), + data: {} as CAProvider, + }, + ...filteredItems.map((item) => ({ + key: item.type, + value: item.type, + label: t(item.name), + data: item, + })), + ]); + }, [filter]); + + const renderOption = (key: string) => { + if (key === "") { + return ( + + + {t("provider.default_ca_provider.label")} + + + ); + } + + const provider = caProvidersMap.get(key); + return ( + + + + {t(provider?.name ?? "")} + + + ); + }; + + return ( + { + if (!option) return false; + + const value = inputValue.toLowerCase(); + return option.value.toLowerCase().includes(value) || option.label.toLowerCase().includes(value); + }} + labelRender={({ label, value }) => { + if (!label) { + return {props.placeholder}; + } + + return renderOption(value as string); + }} + options={options} + optionFilterProp={undefined} + optionLabelProp={undefined} + optionRender={(option) => renderOption(option.data.value)} + /> + ); +}; + +export default memo(NotificationProviderSelect); diff --git a/ui/src/components/workflow/WorkflowRunDetail.tsx b/ui/src/components/workflow/WorkflowRunDetail.tsx index e104410a..2d421880 100644 --- a/ui/src/components/workflow/WorkflowRunDetail.tsx +++ b/ui/src/components/workflow/WorkflowRunDetail.tsx @@ -5,6 +5,7 @@ import { CheckOutlined as CheckOutlinedIcon, ClockCircleOutlined as ClockCircleOutlinedIcon, CloseCircleOutlined as CloseCircleOutlinedIcon, + DownloadOutlined as DownloadOutlinedIcon, RightOutlined as RightOutlinedIcon, SelectOutlined as SelectOutlinedIcon, SettingOutlined as SettingOutlinedIcon, @@ -188,6 +189,36 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin ); }; + const handleDownloadClick = () => { + const NEWLINE = "\n"; + const logstr = listData + .map((group) => { + const escape = (str: string) => str.replaceAll("\r", "\\r").replaceAll("\n", "\\n"); + return ( + group.name + + NEWLINE + + group.records + .map((record) => { + const datetime = dayjs(record.timestamp).format("YYYY-MM-DDTHH:mm:ss.SSSZ"); + const level = record.level; + const message = record.message; + const data = record.data && Object.keys(record.data).length > 0 ? JSON.stringify(record.data) : ""; + return `[${datetime}] [${level}] ${escape(message)} ${escape(data)}`.trim(); + }) + .join(NEWLINE) + ); + }) + .join(NEWLINE + NEWLINE); + const blob = new Blob([logstr], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `certimate_workflow_run_#${runId}_logs.txt`; + a.click(); + URL.revokeObjectURL(url); + a.remove(); + }; + return ( <> {t("workflow_run.logs")} @@ -210,6 +241,15 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin icon: , onClick: () => setShowWhitespace(!showWhitespace), }, + { + type: "divider", + }, + { + key: "download-logs", + label: t("workflow_run.logs.menu.download_logs"), + icon: , + onClick: handleDownloadClick, + }, ], }} trigger={["click"]} diff --git a/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx b/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx index 79eb103c..dc8149f3 100644 --- a/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx +++ b/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx @@ -30,7 +30,6 @@ const WorkflowRunDetailDrawer = ({ data, loading, trigger, ...props }: WorkflowR { {NotificationContextHolder}
+ } /> + columns={tableColumns} dataSource={tableData} diff --git a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx index 6d219b49..73f242d0 100644 --- a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx @@ -1,6 +1,12 @@ import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import { FormOutlined as FormOutlinedIcon, PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon } from "@ant-design/icons"; +import { Link } from "react-router"; +import { + FormOutlined as FormOutlinedIcon, + PlusOutlined as PlusOutlinedIcon, + QuestionCircleOutlined as QuestionCircleOutlinedIcon, + RightOutlined as RightOutlinedIcon, +} from "@ant-design/icons"; import { useControllableValue } from "ahooks"; import { AutoComplete, @@ -25,14 +31,17 @@ import AccessEditModal from "@/components/access/AccessEditModal"; import AccessSelect from "@/components/access/AccessSelect"; import ModalForm from "@/components/ModalForm"; import MultipleInput from "@/components/MultipleInput"; -import ApplyDNSProviderSelect from "@/components/provider/ApplyDNSProviderSelect"; -import { ACCESS_USAGES, APPLY_DNS_PROVIDERS, accessProvidersMap, applyDNSProvidersMap } from "@/domain/provider"; +import ACMEDns01ProviderSelect from "@/components/provider/ACMEDns01ProviderSelect"; +import CAProviderSelect from "@/components/provider/CAProviderSelect"; +import Show from "@/components/Show"; +import { ACCESS_USAGES, ACME_DNS01_PROVIDERS, accessProvidersMap, acmeDns01ProvidersMap, caProvidersMap } from "@/domain/provider"; import { type WorkflowNodeConfigForApply } from "@/domain/workflow"; import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks"; import { useAccessesStore } from "@/stores/access"; import { useContactEmailsStore } from "@/stores/contact"; import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/validators"; +import ApplyNodeConfigFormAliyunESAConfig from "./ApplyNodeConfigFormAliyunESAConfig"; import ApplyNodeConfigFormAWSRoute53Config from "./ApplyNodeConfigFormAWSRoute53Config"; import ApplyNodeConfigFormHuaweiCloudDNSConfig from "./ApplyNodeConfigFormHuaweiCloudDNSConfig"; import ApplyNodeConfigFormJDCloudDNSConfig from "./ApplyNodeConfigFormJDCloudDNSConfig"; @@ -60,7 +69,7 @@ const initFormModel = (): ApplyNodeConfigFormFieldValues => { return { challengeType: "dns-01", keyAlgorithm: "RSA2048", - skipBeforeExpiryDays: 20, + skipBeforeExpiryDays: 30, }; }; @@ -83,7 +92,18 @@ const ApplyNodeConfigForm = forwardRef { + if (!fieldCAProvider) return true; + + const provider = caProvidersMap.get(fieldCAProvider); + return !!provider?.builtin || !!v; + }, t("workflow_node.apply.form.ca_provider_access.placeholder")), + caProviderConfig: z.any().nullish(), keyAlgorithm: z .string({ message: t("workflow_node.apply.form.key_algorithm.placeholder") }) .nonempty(t("workflow_node.apply.form.key_algorithm.placeholder")), @@ -96,24 +116,35 @@ const ApplyNodeConfigForm = forwardRef validIPv4Address(e) || validIPv6Address(e) || validDomainName(e)); }, t("common.errmsg.host_invalid")), - dnsPropagationTimeout: z - .union([ - z.number().int().gte(1, t("workflow_node.apply.form.dns_propagation_timeout.placeholder")), - z.string().refine((v) => !v || /^[1-9]\d*$/.test(v), t("workflow_node.apply.form.dns_propagation_timeout.placeholder")), - ]) - .nullish(), - dnsTTL: z - .union([ - z.number().int().gte(1, t("workflow_node.apply.form.dns_ttl.placeholder")), - z.string().refine((v) => !v || /^[1-9]\d*$/.test(v), t("workflow_node.apply.form.dns_ttl.placeholder")), - ]) - .nullish(), + dnsPropagationWait: z.preprocess( + (v) => (v == null || v === "" ? undefined : Number(v)), + z + .number() + .int(t("workflow_node.apply.form.dns_propagation_wait.placeholder")) + .gte(0, t("workflow_node.apply.form.dns_propagation_wait.placeholder")) + .nullish() + ), + dnsPropagationTimeout: z.preprocess( + (v) => (v == null || v === "" ? undefined : Number(v)), + z + .number() + .int(t("workflow_node.apply.form.dns_propagation_timeout.placeholder")) + .gte(1, t("workflow_node.apply.form.dns_propagation_timeout.placeholder")) + .nullish() + ), + dnsTTL: z.preprocess( + (v) => (v == null || v === "" ? undefined : Number(v)), + z.number().int(t("workflow_node.apply.form.dns_ttl.placeholder")).gte(1, t("workflow_node.apply.form.dns_ttl.placeholder")).nullish() + ), disableFollowCNAME: z.boolean().nullish(), disableARI: z.boolean().nullish(), - skipBeforeExpiryDays: z - .number({ message: t("workflow_node.apply.form.skip_before_expiry_days.placeholder") }) - .int(t("workflow_node.apply.form.skip_before_expiry_days.placeholder")) - .gte(1, t("workflow_node.apply.form.skip_before_expiry_days.placeholder")), + skipBeforeExpiryDays: z.preprocess( + (v) => Number(v), + z + .number() + .int(t("workflow_node.apply.form.skip_before_expiry_days.placeholder")) + .gte(1, t("workflow_node.apply.form.skip_before_expiry_days.placeholder")) + ), }); const formRule = createSchemaFieldRule(formSchema); const { form: formInst, formProps } = useAntdForm({ @@ -121,24 +152,36 @@ const ApplyNodeConfigForm = forwardRef("domains", formInst); const fieldProvider = Form.useWatch("provider", { form: formInst, preserve: true }); const fieldProviderAccessId = Form.useWatch("providerAccessId", formInst); - const fieldDomains = Form.useWatch("domains", formInst); + const fieldCAProvider = Form.useWatch("caProvider", formInst); const fieldNameservers = Form.useWatch("nameservers", formInst); const [showProvider, setShowProvider] = useState(false); useEffect(() => { // 通常情况下每个授权信息只对应一个 DNS 提供商,此时无需显示 DNS 提供商字段; - // 如果对应多个(如 AWS 的 Route53、Lightsail,腾讯云的 DNS、EdgeOne 等),则显示。 + // 如果对应多个(如 AWS 的 Route53、Lightsail,阿里云的 DNS、ESA,腾讯云的 DNS、EdgeOne 等),则显示。 if (fieldProviderAccessId) { const access = accesses.find((e) => e.id === fieldProviderAccessId); - const providers = Array.from(applyDNSProvidersMap.values()).filter((e) => e.provider === access?.provider); + const providers = Array.from(acmeDns01ProvidersMap.values()).filter((e) => e.provider === access?.provider); setShowProvider(providers.length > 1); } else { setShowProvider(false); } }, [accesses, fieldProviderAccessId]); + const [showCAProviderAccess, setShowCAProviderAccess] = useState(false); + useEffect(() => { + // 内置的 CA 提供商(如 Let's Encrypt)无需显示授权信息字段 + if (fieldCAProvider) { + const provider = caProvidersMap.get(fieldCAProvider); + setShowCAProviderAccess(!provider?.builtin); + } else { + setShowCAProviderAccess(false); + } + }, [fieldCAProvider]); + const [nestedFormInst] = Form.useForm(); const nestedFormName = useAntdFormName({ form: nestedFormInst, name: "workflowNodeApplyConfigFormProviderConfigForm" }); const nestedFormEl = useMemo(() => { @@ -154,16 +197,18 @@ const ApplyNodeConfigForm = forwardRef; + case ACME_DNS01_PROVIDERS.AWS: + case ACME_DNS01_PROVIDERS.AWS_ROUTE53: return ; - case APPLY_DNS_PROVIDERS.HUAWEICLOUD: - case APPLY_DNS_PROVIDERS.HUAWEICLOUD_DNS: + case ACME_DNS01_PROVIDERS.HUAWEICLOUD: + case ACME_DNS01_PROVIDERS.HUAWEICLOUD_DNS: return ; - case APPLY_DNS_PROVIDERS.JDCLOUD: - case APPLY_DNS_PROVIDERS.JDCLOUD_DNS: + case ACME_DNS01_PROVIDERS.JDCLOUD: + case ACME_DNS01_PROVIDERS.JDCLOUD_DNS: return ; - case APPLY_DNS_PROVIDERS.TENCENTCLOUD_EO: + case ACME_DNS01_PROVIDERS.TENCENTCLOUD_EO: return ; } }, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]); @@ -176,7 +221,7 @@ const ApplyNodeConfigForm = forwardRef { - if (fieldProviderAccessId === value) return; - // 切换授权信息时联动 DNS 提供商 const access = accesses.find((access) => access.id === value); - const provider = Array.from(applyDNSProvidersMap.values()).find((provider) => provider.provider === access?.provider); + const provider = Array.from(acmeDns01ProvidersMap.values()).find((provider) => provider.provider === access?.provider); if (fieldProvider !== provider?.type) { formInst.setFieldValue("provider", provider?.type); onValuesChange?.(formInst.getFieldsValue(true)); } }; + const handleCAProviderSelect = (value?: string | undefined) => { + // 切换 CA 提供商时联动授权信息 + if (value === "") { + setTimeout(() => { + formInst.setFieldValue("caProvider", undefined); + formInst.setFieldValue("caProviderAccessId", undefined); + onValuesChange?.(formInst.getFieldsValue(true)); + }, 1); + } else if (initialValues?.caProvider === value) { + formInst.setFieldValue("caProviderAccessId", initialValues?.caProviderAccessId); + onValuesChange?.(formInst.getFieldsValue(true)); + } else { + if (caProvidersMap.get(fieldCAProvider)?.provider !== caProvidersMap.get(value!)?.provider) { + formInst.setFieldValue("caProviderAccessId", undefined); + onValuesChange?.(formInst.getFieldsValue(true)); + } + } + }; + const handleFormProviderChange = (name: string) => { if (name === nestedFormName) { formInst.setFieldValue("providerConfig", nestedFormInst.getFieldsValue()); @@ -273,7 +335,7 @@ const ApplyNodeConfigForm = forwardRef