diff --git a/.goreleaser.yml b/.goreleaser.yml
index 8b4b64fe..9315c6a7 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -30,6 +30,9 @@ builds:
- goos: darwin
goarch: arm
+upx:
+ enable: true
+
release:
draft: true
diff --git a/Dockerfile b/Dockerfile
index e1d01abd..997cda44 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,7 +15,8 @@ RUN apk add --no-cache tzdata
COPY ../. /app/
RUN rm -rf /app/ui/dist
COPY --from=webui-builder /app/ui/dist /app/ui/dist
-RUN go build -o certimate
+ENV CGO_ENABLED=0
+RUN go build -ldflags="-s -w" -o certimate
diff --git a/Makefile b/Makefile
index 7bf8cabd..eb05c306 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,8 @@ $(OS_ARCH):
@mkdir -p $(BUILD_DIR)
GOOS=$(word 1,$(subst /, ,$@)) \
GOARCH=$(word 2,$(subst /, ,$@)) \
- go build -o $(BUILD_DIR)/$(BINARY_NAME)_$(word 1,$(subst /, ,$@))_$(word 2,$(subst /, ,$@)) -ldflags="-X main.version=$(VERSION)" .
+ CGO_ENABLED=0 \
+ go build -o $(BUILD_DIR)/$(BINARY_NAME)_$(word 1,$(subst /, ,$@))_$(word 2,$(subst /, ,$@)) -ldflags="-X main.version=$(VERSION) -s -w" .
# 清理构建文件
clean:
diff --git a/README.md b/README.md
index 82f81275..988089d9 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
- 支持单域名、多域名、泛域名证书,可选 RSA、ECC 签名算法;
- 支持 PEM、PFX、JKS 等多种格式输出证书;
- 支持 30+ 域名托管商(如阿里云、腾讯云、Cloudflare 等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-dns-providers));
-- 支持 80+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-host-providers));
+- 支持 80+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-hosting-providers));
- 支持邮件、钉钉、飞书、企业微信、Webhook 等多种通知渠道;
- 支持 Let's Encrypt、Buypass、Google Trust Services、SSL.com、ZeroSSL 等多种 ACME 证书颁发机构;
- 更多特性等待探索。
diff --git a/README_EN.md b/README_EN.md
index 472efd66..f94020a2 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -39,7 +39,7 @@ Certimate aims to provide users with a secure and user-friendly SSL certificate
- Supports single-domain, multi-domain, wildcard certificates, with options for RSA or ECC.
- Supports various certificate formats such as PEM, PFX, JKS.
- Supports more than 30+ 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 80+ 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 more than 80+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-hosting-providers));
- Supports multiple notification channels including email, DingTalk, Feishu, WeCom, Webhook, 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.
diff --git a/go.mod b/go.mod
index 7adb56bf..37686d8c 100644
--- a/go.mod
+++ b/go.mod
@@ -20,6 +20,7 @@ require (
github.com/alibabacloud-go/esa-20240910/v2 v2.32.0
github.com/alibabacloud-go/fc-20230330/v4 v4.3.5
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12
+ github.com/alibabacloud-go/ga-20191120/v3 v3.1.8
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
@@ -30,8 +31,10 @@ require (
github.com/aws/aws-sdk-go-v2/service/acm v1.32.0
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1
github.com/baidubce/bce-sdk-go v0.9.226
+ github.com/blinkbean/dingtalk v1.1.3
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.46
github.com/go-acme/lego/v4 v4.23.1
+ github.com/go-lark/lark v1.16.0
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.148
@@ -39,7 +42,6 @@ require (
github.com/libdns/dynv6 v1.0.0
github.com/libdns/libdns v0.2.3
github.com/luthermonson/go-proxmox v0.2.2
- github.com/nikoksr/notify v1.3.0
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0
github.com/pkg/sftp v1.13.9
github.com/pocketbase/dbx v1.11.0
@@ -84,13 +86,11 @@ require (
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.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
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
@@ -99,7 +99,6 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
- 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.2 // indirect
@@ -126,7 +125,6 @@ require (
github.com/qiniu/x v1.10.5 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // 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
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
@@ -195,12 +193,9 @@ require (
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 // indirect
- github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/cast v1.8.0 // 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.1128 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
golang.org/x/image v0.27.0 // indirect
diff --git a/go.sum b/go.sum
index ae7a9d22..5a64f77a 100644
--- a/go.sum
+++ b/go.sum
@@ -112,6 +112,7 @@ github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+M
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
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.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
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=
@@ -137,6 +138,8 @@ github.com/alibabacloud-go/fc-20230330/v4 v4.3.5 h1:nDNjVzGwkQPbQnAuxAmxvS9x8QGL
github.com/alibabacloud-go/fc-20230330/v4 v4.3.5/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/ga-20191120/v3 v3.1.8 h1:5GF0PXijDhxRQ3gTg9Ee/CVPtglkxuVdz4yIQgYLPgw=
+github.com/alibabacloud-go/ga-20191120/v3 v3.1.8/go.mod h1:RVpR9VL4YECKoZCQijTYfPk8k52O61v6hSRekjxF0kw=
github.com/alibabacloud-go/live-20161101 v1.1.1 h1:rUGfA8RHmCMtQ5M3yMSyRde+yRXWqVecmiXBU3XrGJ8=
github.com/alibabacloud-go/live-20161101 v1.1.1/go.mod h1:g84w6qeAodT0/IHdc0tEed2a8PyhQhYl7TAj3jGl4A4=
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3 h1:LtyUVlgBEKyzWgQJurzXM6MXCt84sQr9cE5OKqYymko=
@@ -360,8 +363,8 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
-github.com/go-lark/lark v1.15.1 h1:fo6PQKBJht/71N9Zn3/xjknOYx0TmdVuP+VP8NrUCsI=
-github.com/go-lark/lark v1.15.1/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
+github.com/go-lark/lark v1.16.0 h1:U6BwkLM9wrZedSM7cIiMofganr8PCvJN+M75w2lf2Gg=
+github.com/go-lark/lark v1.16.0/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@@ -400,8 +403,6 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8Wd
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
-github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
-github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
@@ -569,8 +570,6 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
-github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
-github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
@@ -679,8 +678,6 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
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=
@@ -830,8 +827,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
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.1155 h1:ildxJtjnqiKZxWDVKHT/ncIknGDijtg60MuFELON8bY=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155/go.mod h1:iLASpooTdyXtx642E5Ws7cfWENsp4/uZ/78TFoln7OI=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161 h1:yGFg9/6j3NP10r9PfSWHfekuq4SwPyqblWnfISfKANo=
diff --git a/internal/applicant/acme_ca.go b/internal/applicant/acme_ca.go
index 67b7693e..36c0a0a4 100644
--- a/internal/applicant/acme_ca.go
+++ b/internal/applicant/acme_ca.go
@@ -3,25 +3,26 @@ package applicant
import "github.com/usual2970/certimate/internal/domain"
const (
- sslProviderLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt)
- sslProviderLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging)
- sslProviderBuypass = string(domain.CAProviderTypeBuypass)
- sslProviderGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices)
- sslProviderSSLCom = string(domain.CAProviderTypeSSLCom)
- sslProviderZeroSSL = string(domain.CAProviderTypeZeroSSL)
+ caLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt)
+ caLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging)
+ caBuypass = string(domain.CAProviderTypeBuypass)
+ caGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices)
+ caSSLCom = string(domain.CAProviderTypeSSLCom)
+ caZeroSSL = string(domain.CAProviderTypeZeroSSL)
+ caCustom = string(domain.CAProviderTypeACMECA)
- sslProviderDefault = sslProviderLetsEncrypt
+ caDefault = caLetsEncrypt
)
-var sslProviderUrls = map[string]string{
- 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",
+var caDirUrls = map[string]string{
+ caLetsEncrypt: "https://acme-v02.api.letsencrypt.org/directory",
+ caLetsEncryptStaging: "https://acme-staging-v02.api.letsencrypt.org/directory",
+ caBuypass: "https://api.buypass.com/acme/directory",
+ caGoogleTrustServices: "https://dv.acme-v02.api.pki.goog/directory",
+ caSSLCom: "https://acme.ssl.com/sslcom-dv-rsa",
+ caSSLCom + "RSA": "https://acme.ssl.com/sslcom-dv-rsa",
+ caSSLCom + "ECC": "https://acme.ssl.com/sslcom-dv-ecc",
+ caZeroSSL: "https://acme.zerossl.com/v2/DV90",
}
type acmeSSLProviderConfig struct {
diff --git a/internal/applicant/acme_user.go b/internal/applicant/acme_user.go
index 29ac80cd..e6e13cb7 100644
--- a/internal/applicant/acme_user.go
+++ b/internal/applicant/acme_user.go
@@ -7,6 +7,7 @@ import (
"crypto/elliptic"
"crypto/rand"
"fmt"
+ "strings"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
@@ -19,22 +20,31 @@ import (
)
type acmeUser struct {
- CA string
- Email string
+ // 证书颁发机构标识。
+ // 通常等同于 [CAProviderType] 的值。
+ // 对于自定义 ACME CA,值为 "custom#{access_id}"。
+ CA string
+ // 邮箱。
+ Email string
+ // 注册信息。
Registration *registration.Resource
+ // CSR 私钥。
privkey string
}
-func newAcmeUser(ca, email string) (*acmeUser, error) {
+func newAcmeUser(ca, caAccessId, email string) (*acmeUser, error) {
repo := repository.NewAcmeAccountRepository()
applyUser := &acmeUser{
CA: ca,
Email: email,
}
+ if ca == caCustom {
+ applyUser.CA = fmt.Sprintf("%s#%s", ca, caAccessId)
+ }
- acmeAccount, err := repo.GetByCAAndEmail(ca, email)
+ acmeAccount, err := repo.GetByCAAndEmail(applyUser.CA, applyUser.Email)
if err != nil {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
@@ -73,6 +83,10 @@ func (u *acmeUser) hasRegistration() bool {
return u.Registration != nil
}
+func (u *acmeUser) getCAProvider() string {
+ return strings.Split(u.CA, "#")[0]
+}
+
func (u *acmeUser) getPrivateKeyPEM() string {
return u.privkey
}
@@ -94,16 +108,16 @@ func registerAcmeUserWithSingleFlight(client *lego.Client, user *acmeUser, userR
func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) {
var reg *registration.Resource
var err error
- switch user.CA {
- case sslProviderLetsEncrypt, sslProviderLetsEncryptStaging:
+ switch user.getCAProvider() {
+ case caLetsEncrypt, caLetsEncryptStaging:
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
- case sslProviderBuypass:
+ case caBuypass:
{
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
}
- case sslProviderGoogleTrustServices:
+ case caGoogleTrustServices:
{
access := domain.AccessConfigForGoogleTrustServices{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
@@ -117,7 +131,7 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m
})
}
- case sslProviderSSLCom:
+ case caSSLCom:
{
access := domain.AccessConfigForSSLCom{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
@@ -131,7 +145,7 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m
})
}
- case sslProviderZeroSSL:
+ case caZeroSSL:
{
access := domain.AccessConfigForZeroSSL{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
@@ -145,6 +159,26 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m
})
}
+ case caCustom:
+ {
+ access := domain.AccessConfigForACMECA{}
+ if err := maputil.Populate(userRegisterOptions, &access); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ if access.EabKid == "" && access.EabHmacKey == "" {
+ reg, err = client.Registration.Register(registration.RegisterOptions{
+ TermsOfServiceAgreed: true,
+ })
+ } else {
+ reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
+ TermsOfServiceAgreed: true,
+ Kid: access.EabKid,
+ HmacEncoded: access.EabHmacKey,
+ })
+ }
+ }
+
default:
err = fmt.Errorf("unsupported ca provider '%s'", user.CA)
}
diff --git a/internal/applicant/applicant.go b/internal/applicant/applicant.go
index e9ed4cb1..e6a04bcd 100644
--- a/internal/applicant/applicant.go
+++ b/internal/applicant/applicant.go
@@ -20,12 +20,13 @@ import (
"golang.org/x/time/rate"
"github.com/usual2970/certimate/internal/domain"
+ maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
"github.com/usual2970/certimate/internal/repository"
)
type ApplyResult struct {
- CertificateFullChain string
+ FullChainCertificate string
IssuerCertificate string
PrivateKey string
ACMEAccountUrl string
@@ -53,20 +54,20 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err
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,
+ 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),
+ ProviderServiceConfig: nodeConfig.ProviderConfig,
+ CAProvider: domain.CAProviderType(nodeConfig.CAProvider),
+ CAProviderAccessConfig: make(map[string]any),
+ CAProviderServiceConfig: 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()
@@ -81,6 +82,7 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err
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.CAProviderAccessId = access.Id
options.CAProviderAccessConfig = access.Config
}
}
@@ -91,13 +93,13 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err
sslProviderConfig := &acmeSSLProviderConfig{
Config: make(map[domain.CAProviderType]map[string]any),
- Provider: sslProviderDefault,
+ Provider: caDefault,
}
if settings != nil {
if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil {
return nil, err
} else if sslProviderConfig.Provider == "" {
- sslProviderConfig.Provider = sslProviderDefault
+ sslProviderConfig.Provider = caDefault
}
}
@@ -163,7 +165,7 @@ func getLimiter(key string) *rate.Limiter {
}
func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOptions) (*ApplyResult, error) {
- user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail)
+ user, err := newAcmeUser(string(options.CAProvider), options.CAProviderAccessId, options.ContactEmail)
if err != nil {
return nil, err
}
@@ -175,13 +177,26 @@ func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOpt
// Create an ACME client config
config := lego.NewConfig(user)
config.Certificate.KeyType = parseLegoKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
- config.CADirURL = sslProviderUrls[user.CA]
- if user.CA == sslProviderSSLCom {
+ switch user.getCAProvider() {
+ case caSSLCom:
if strings.HasPrefix(options.KeyAlgorithm, "RSA") {
- config.CADirURL = sslProviderUrls[sslProviderSSLCom+"RSA"]
+ config.CADirURL = caDirUrls[caSSLCom+"RSA"]
} else if strings.HasPrefix(options.KeyAlgorithm, "EC") {
- config.CADirURL = sslProviderUrls[sslProviderSSLCom+"ECC"]
+ config.CADirURL = caDirUrls[caSSLCom+"ECC"]
+ } else {
+ config.CADirURL = caDirUrls[caSSLCom]
}
+
+ case caCustom:
+ caDirURL := maputil.GetString(options.CAProviderAccessConfig, "endpoint")
+ if caDirURL != "" {
+ config.CADirURL = caDirURL
+ } else {
+ return nil, fmt.Errorf("invalid ca provider endpoint")
+ }
+
+ default:
+ config.CADirURL = caDirUrls[user.CA]
}
// Create an ACME client
@@ -229,7 +244,7 @@ func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOpt
}
return &ApplyResult{
- CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)),
+ FullChainCertificate: strings.TrimSpace(string(certResource.Certificate)),
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
ACMEAccountUrl: user.Registration.URI,
diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go
index 90a3cf72..de47ae18 100644
--- a/internal/applicant/providers.go
+++ b/internal/applicant/providers.go
@@ -42,22 +42,23 @@ import (
)
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
+ Domains []string
+ ContactEmail string
+ Provider domain.ACMEDns01ProviderType
+ ProviderAccessConfig map[string]any
+ ProviderServiceConfig map[string]any
+ CAProvider domain.CAProviderType
+ CAProviderAccessId string
+ CAProviderAccessConfig map[string]any
+ CAProviderServiceConfig 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) {
@@ -104,7 +105,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
applicant, err := pAliyunESA.NewChallengeProvider(&pAliyunESA.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -125,8 +126,8 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- HostedZoneId: maputil.GetString(options.ProviderExtendedConfig, "hostedZoneId"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ HostedZoneId: maputil.GetString(options.ProviderServiceConfig, "hostedZoneId"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -333,7 +334,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -350,7 +351,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- RegionId: maputil.GetString(options.ProviderExtendedConfig, "regionId"),
+ RegionId: maputil.GetString(options.ProviderServiceConfig, "regionId"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -520,7 +521,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"),
+ ZoneId: maputil.GetString(options.ProviderServiceConfig, "zoneId"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go
index bdacf08e..e4a28746 100644
--- a/internal/deployer/deployer.go
+++ b/internal/deployer/deployer.go
@@ -31,9 +31,9 @@ func NewWithWorkflowNode(config DeployerWithWorkflowNodeConfig) (Deployer, error
nodeConfig := config.Node.GetConfigForDeploy()
options := &deployerProviderOptions{
- Provider: domain.DeploymentProviderType(nodeConfig.Provider),
- ProviderAccessConfig: make(map[string]any),
- ProviderExtendedConfig: nodeConfig.ProviderConfig,
+ Provider: domain.DeploymentProviderType(nodeConfig.Provider),
+ ProviderAccessConfig: make(map[string]any),
+ ProviderServiceConfig: nodeConfig.ProviderConfig,
}
accessRepo := repository.NewAccessRepository()
diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go
index d69b05b7..d1d72408 100644
--- a/internal/deployer/providers.go
+++ b/internal/deployer/providers.go
@@ -19,6 +19,7 @@ import (
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"
+ pAliyunGA "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ga"
pAliyunLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-live"
pAliyunNLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-nlb"
pAliyunOSS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-oss"
@@ -34,12 +35,15 @@ 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"
+ pBaotaWAFConsole "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-console"
+ pBaotaWAFSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-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"
+ pFlexCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/flexcdn"
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"
@@ -51,12 +55,15 @@ import (
pJDCloudLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-live"
pJDCloudVOD "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-vod"
pK8sSecret "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/k8s-secret"
+ pLeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/lecdn"
pLocal "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/local"
pNetlifySite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/netlify-site"
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"
+ pRatPanelConsole "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ratpanel-console"
+ pRatPanelSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ratpanel-site"
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"
@@ -81,7 +88,9 @@ 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"
+ pWangsuCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-cdn"
pWangsuCDNPro "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-cdnpro"
+ pWangsuCertificate "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-certificate"
pWebhook "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/webhook"
httputil "github.com/usual2970/certimate/internal/pkg/utils/http"
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
@@ -89,9 +98,9 @@ import (
)
type deployerProviderOptions struct {
- Provider domain.DeploymentProviderType
- ProviderAccessConfig map[string]any
- ProviderExtendedConfig map[string]any
+ Provider domain.DeploymentProviderType
+ ProviderAccessConfig map[string]any
+ ProviderServiceConfig map[string]any
}
func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer, error) {
@@ -111,20 +120,22 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
case domain.DeploymentProviderType1PanelConsole:
deployer, err := p1PanelConsole.NewDeployer(&p1PanelConsole.DeployerConfig{
ApiUrl: access.ApiUrl,
+ ApiVersion: access.ApiVersion,
ApiKey: access.ApiKey,
AllowInsecureConnections: access.AllowInsecureConnections,
- AutoRestart: maputil.GetBool(options.ProviderExtendedConfig, "autoRestart"),
+ AutoRestart: maputil.GetBool(options.ProviderServiceConfig, "autoRestart"),
})
return deployer, err
case domain.DeploymentProviderType1PanelSite:
deployer, err := p1PanelSite.NewDeployer(&p1PanelSite.DeployerConfig{
ApiUrl: access.ApiUrl,
+ ApiVersion: access.ApiVersion,
ApiKey: access.ApiKey,
AllowInsecureConnections: access.AllowInsecureConnections,
- 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"),
+ ResourceType: p1PanelSite.ResourceType(maputil.GetOrDefaultString(options.ProviderServiceConfig, "resourceType", string(p1PanelSite.RESOURCE_TYPE_WEBSITE))),
+ WebsiteId: maputil.GetInt64(options.ProviderServiceConfig, "websiteId"),
+ CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"),
})
return deployer, err
@@ -133,7 +144,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
}
}
- 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:
+ case domain.DeploymentProviderTypeAliyunALB, domain.DeploymentProviderTypeAliyunAPIGW, domain.DeploymentProviderTypeAliyunCAS, domain.DeploymentProviderTypeAliyunCASDeploy, domain.DeploymentProviderTypeAliyunCDN, domain.DeploymentProviderTypeAliyunCLB, domain.DeploymentProviderTypeAliyunDCDN, domain.DeploymentProviderTypeAliyunDDoS, domain.DeploymentProviderTypeAliyunESA, domain.DeploymentProviderTypeAliyunFC, domain.DeploymentProviderTypeAliyunGA, domain.DeploymentProviderTypeAliyunLive, domain.DeploymentProviderTypeAliyunNLB, domain.DeploymentProviderTypeAliyunOSS, domain.DeploymentProviderTypeAliyunVOD, domain.DeploymentProviderTypeAliyunWAF:
{
access := domain.AccessConfigForAliyun{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -145,11 +156,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunALB.NewDeployer(&pAliyunALB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pAliyunALB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -157,11 +168,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ServiceType: pAliyunAPIGW.ServiceType(maputil.GetString(options.ProviderServiceConfig, "serviceType")),
+ GatewayId: maputil.GetString(options.ProviderServiceConfig, "gatewayId"),
+ GroupId: maputil.GetString(options.ProviderServiceConfig, "groupId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -169,7 +180,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunCAS.NewDeployer(&pAliyunCAS.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
})
return deployer, err
@@ -177,9 +188,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunCASDeploy.NewDeployer(&pAliyunCASDeploy.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- 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 != "" }),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }),
+ ContactIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "contactIds"), ";"), func(s string) bool { return s != "" }),
})
return deployer, err
@@ -187,7 +198,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunCDN.NewDeployer(&pAliyunCDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -195,11 +206,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunCLB.NewDeployer(&pAliyunCLB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pAliyunCLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerPort: maputil.GetOrDefaultInt32(options.ProviderServiceConfig, "listenerPort", 443),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -207,7 +218,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunDCDN.NewDeployer(&pAliyunDCDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -215,8 +226,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunDDoS.NewDeployer(&pAliyunDDoS.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -224,8 +235,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunESA.NewDeployer(&pAliyunESA.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- SiteId: maputil.GetInt64(options.ProviderExtendedConfig, "siteId"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ SiteId: maputil.GetInt64(options.ProviderServiceConfig, "siteId"),
})
return deployer, err
@@ -233,9 +244,20 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunFC.NewDeployer(&pAliyunFC.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- ServiceVersion: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "serviceVersion", "3.0"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ServiceVersion: maputil.GetOrDefaultString(options.ProviderServiceConfig, "serviceVersion", "3.0"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
+ })
+ return deployer, err
+
+ case domain.DeploymentProviderTypeAliyunGA:
+ deployer, err := pAliyunGA.NewDeployer(&pAliyunGA.DeployerConfig{
+ AccessKeyId: access.AccessKeyId,
+ AccessKeySecret: access.AccessKeySecret,
+ ResourceType: pAliyunGA.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ AcceleratorId: maputil.GetString(options.ProviderServiceConfig, "acceleratorId"),
+ ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -243,8 +265,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunLive.NewDeployer(&pAliyunLive.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -252,10 +274,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunNLB.NewDeployer(&pAliyunNLB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pAliyunNLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
})
return deployer, err
@@ -263,9 +285,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunOSS.NewDeployer(&pAliyunOSS.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ Bucket: maputil.GetString(options.ProviderServiceConfig, "bucket"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -273,8 +295,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunVOD.NewDeployer(&pAliyunVOD.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -282,10 +304,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAliyunWAF.NewDeployer(&pAliyunWAF.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ServiceVersion: maputil.GetOrDefaultString(options.ProviderServiceConfig, "serviceVersion", "3.0"),
+ InstanceId: maputil.GetString(options.ProviderServiceConfig, "instanceId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -306,8 +328,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAWSACM.NewDeployer(&pAWSACM.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- CertificateArn: maputil.GetString(options.ProviderExtendedConfig, "certificateArn"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ CertificateArn: maputil.GetString(options.ProviderServiceConfig, "certificateArn"),
})
return deployer, err
@@ -315,8 +337,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pAWSCloudFront.NewDeployer(&pAWSCloudFront.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- DistributionId: maputil.GetString(options.ProviderExtendedConfig, "distributionId"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ DistributionId: maputil.GetString(options.ProviderServiceConfig, "distributionId"),
})
return deployer, err
@@ -339,8 +361,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
ClientId: access.ClientId,
ClientSecret: access.ClientSecret,
CloudName: access.CloudName,
- KeyVaultName: maputil.GetString(options.ProviderExtendedConfig, "keyvaultName"),
- CertificateName: maputil.GetString(options.ProviderExtendedConfig, "certificateName"),
+ KeyVaultName: maputil.GetString(options.ProviderServiceConfig, "keyvaultName"),
+ CertificateName: maputil.GetString(options.ProviderServiceConfig, "certificateName"),
})
return deployer, err
@@ -361,11 +383,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pBaiduCloudAppBLB.NewDeployer(&pBaiduCloudAppBLB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pBaiduCloudAppBLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerPort: maputil.GetInt32(options.ProviderServiceConfig, "listenerPort"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -373,11 +395,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pBaiduCloudBLB.NewDeployer(&pBaiduCloudBLB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pBaiduCloudBLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerPort: maputil.GetInt32(options.ProviderServiceConfig, "listenerPort"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -385,7 +407,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pBaiduCloudCDN.NewDeployer(&pBaiduCloudCDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -412,8 +434,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
case domain.DeploymentProviderTypeBaishanCDN:
deployer, err := pBaishanCDN.NewDeployer(&pBaishanCDN.DeployerConfig{
ApiToken: access.ApiToken,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
- CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
+ CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
})
return deployer, err
@@ -435,7 +457,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
ApiUrl: access.ApiUrl,
ApiKey: access.ApiKey,
AllowInsecureConnections: access.AllowInsecureConnections,
- AutoRestart: maputil.GetBool(options.ProviderExtendedConfig, "autoRestart"),
+ AutoRestart: maputil.GetBool(options.ProviderServiceConfig, "autoRestart"),
})
return deployer, err
@@ -444,9 +466,40 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
ApiUrl: access.ApiUrl,
ApiKey: access.ApiKey,
AllowInsecureConnections: access.AllowInsecureConnections,
- 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 != "" }),
+ SiteType: maputil.GetOrDefaultString(options.ProviderServiceConfig, "siteType", "other"),
+ SiteName: maputil.GetString(options.ProviderServiceConfig, "siteName"),
+ SiteNames: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "siteNames"), ";"), func(s string) bool { return s != "" }),
+ })
+ return deployer, err
+
+ default:
+ break
+ }
+ }
+
+ case domain.DeploymentProviderTypeBaotaWAFConsole, domain.DeploymentProviderTypeBaotaWAFSite:
+ {
+ access := domain.AccessConfigForBaotaWAF{}
+ 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.DeploymentProviderTypeBaotaWAFConsole:
+ deployer, err := pBaotaWAFConsole.NewDeployer(&pBaotaWAFConsole.DeployerConfig{
+ ApiUrl: access.ApiUrl,
+ ApiKey: access.ApiKey,
+ AllowInsecureConnections: access.AllowInsecureConnections,
+ })
+ return deployer, err
+
+ case domain.DeploymentProviderTypeBaotaWAFSite:
+ deployer, err := pBaotaWAFSite.NewDeployer(&pBaotaWAFSite.DeployerConfig{
+ ApiUrl: access.ApiUrl,
+ ApiKey: access.ApiKey,
+ AllowInsecureConnections: access.AllowInsecureConnections,
+ SiteName: maputil.GetString(options.ProviderServiceConfig, "siteName"),
+ SitePort: maputil.GetOrDefaultInt32(options.ProviderServiceConfig, "sitePort", 443),
})
return deployer, err
@@ -464,8 +517,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pBunnyCDN.NewDeployer(&pBunnyCDN.DeployerConfig{
ApiKey: access.ApiKey,
- PullZoneId: maputil.GetString(options.ProviderExtendedConfig, "pullZoneId"),
- Hostname: maputil.GetString(options.ProviderExtendedConfig, "hostname"),
+ PullZoneId: maputil.GetString(options.ProviderServiceConfig, "pullZoneId"),
+ Hostname: maputil.GetString(options.ProviderServiceConfig, "hostname"),
})
return deployer, err
}
@@ -482,7 +535,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pBytePlusCDN.NewDeployer(&pBytePlusCDN.DeployerConfig{
AccessKey: access.AccessKey,
SecretKey: access.SecretKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -516,9 +569,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
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"),
+ ResourceType: pCdnfly.ResourceType(maputil.GetOrDefaultString(options.ProviderServiceConfig, "resourceType", string(pCdnfly.RESOURCE_TYPE_SITE))),
+ SiteId: maputil.GetString(options.ProviderServiceConfig, "siteId"),
+ CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
})
return deployer, err
}
@@ -533,7 +586,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pDogeCDN.NewDeployer(&pDogeCDN.DeployerConfig{
AccessKey: access.AccessKey,
SecretKey: access.SecretKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
}
@@ -548,7 +601,26 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pEdgioApplications.NewDeployer(&pEdgioApplications.DeployerConfig{
ClientId: access.ClientId,
ClientSecret: access.ClientSecret,
- EnvironmentId: maputil.GetString(options.ProviderExtendedConfig, "environmentId"),
+ EnvironmentId: maputil.GetString(options.ProviderServiceConfig, "environmentId"),
+ })
+ return deployer, err
+ }
+
+ case domain.DeploymentProviderTypeFlexCDN:
+ {
+ access := domain.AccessConfigForFlexCDN{}
+ if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ deployer, err := pFlexCDN.NewDeployer(&pFlexCDN.DeployerConfig{
+ ApiUrl: access.ApiUrl,
+ ApiRole: access.ApiRole,
+ AccessKeyId: access.AccessKeyId,
+ AccessKey: access.AccessKey,
+ AllowInsecureConnections: access.AllowInsecureConnections,
+ ResourceType: pFlexCDN.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"),
})
return deployer, err
}
@@ -564,8 +636,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
case domain.DeploymentProviderTypeGcoreCDN:
deployer, err := pGcoreCDN.NewDeployer(&pGcoreCDN.DeployerConfig{
ApiToken: access.ApiToken,
- ResourceId: maputil.GetInt64(options.ProviderExtendedConfig, "resourceId"),
- CertificateId: maputil.GetInt64(options.ProviderExtendedConfig, "certificateId"),
+ ResourceId: maputil.GetInt64(options.ProviderServiceConfig, "resourceId"),
+ CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"),
})
return deployer, err
@@ -587,8 +659,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
AccessKeyId: access.AccessKeyId,
AccessKey: access.AccessKey,
AllowInsecureConnections: access.AllowInsecureConnections,
- ResourceType: pGoEdge.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")),
- CertificateId: maputil.GetInt64(options.ProviderExtendedConfig, "certificateId"),
+ ResourceType: pGoEdge.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"),
})
return deployer, err
}
@@ -605,8 +677,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pHuaweiCloudCDN.NewDeployer(&pHuaweiCloudCDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -614,11 +686,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pHuaweiCloudELB.NewDeployer(&pHuaweiCloudELB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pHuaweiCloudELB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
})
return deployer, err
@@ -633,10 +705,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pHuaweiCloudWAF.NewDeployer(&pHuaweiCloudWAF.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pHuaweiCloudWAF.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -657,10 +729,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pJDCloudALB.NewDeployer(&pJDCloudALB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- 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"),
+ RegionId: maputil.GetString(options.ProviderServiceConfig, "regionId"),
+ ResourceType: pJDCloudALB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
})
return deployer, err
@@ -668,7 +740,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pJDCloudCDN.NewDeployer(&pJDCloudCDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -676,7 +748,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pJDCloudLive.NewDeployer(&pJDCloudLive.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -684,7 +756,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pJDCloudVOD.NewDeployer(&pJDCloudVOD.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -693,21 +765,42 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
}
}
+ case domain.DeploymentProviderTypeLeCDN:
+ {
+ access := domain.AccessConfigForLeCDN{}
+ if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ deployer, err := pLeCDN.NewDeployer(&pLeCDN.DeployerConfig{
+ ApiUrl: access.ApiUrl,
+ ApiVersion: access.ApiVersion,
+ ApiRole: access.ApiRole,
+ Username: access.Username,
+ Password: access.Password,
+ AllowInsecureConnections: access.AllowInsecureConnections,
+ ResourceType: pLeCDN.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"),
+ ClientId: maputil.GetInt64(options.ProviderServiceConfig, "clientId"),
+ })
+ return deployer, err
+ }
+
case domain.DeploymentProviderTypeLocal:
{
deployer, err := pLocal.NewDeployer(&pLocal.DeployerConfig{
- 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"),
- OutputServerCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForServerOnly"),
- OutputIntermediaCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForIntermediaOnly"),
- 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"),
+ ShellEnv: pLocal.ShellEnvType(maputil.GetString(options.ProviderServiceConfig, "shellEnv")),
+ PreCommand: maputil.GetString(options.ProviderServiceConfig, "preCommand"),
+ PostCommand: maputil.GetString(options.ProviderServiceConfig, "postCommand"),
+ OutputFormat: pLocal.OutputFormatType(maputil.GetOrDefaultString(options.ProviderServiceConfig, "format", string(pLocal.OUTPUT_FORMAT_PEM))),
+ OutputCertPath: maputil.GetString(options.ProviderServiceConfig, "certPath"),
+ OutputServerCertPath: maputil.GetString(options.ProviderServiceConfig, "certPathForServerOnly"),
+ OutputIntermediaCertPath: maputil.GetString(options.ProviderServiceConfig, "certPathForIntermediaOnly"),
+ OutputKeyPath: maputil.GetString(options.ProviderServiceConfig, "keyPath"),
+ PfxPassword: maputil.GetString(options.ProviderServiceConfig, "pfxPassword"),
+ JksAlias: maputil.GetString(options.ProviderServiceConfig, "jksAlias"),
+ JksKeypass: maputil.GetString(options.ProviderServiceConfig, "jksKeypass"),
+ JksStorepass: maputil.GetString(options.ProviderServiceConfig, "jksStorepass"),
})
return deployer, err
}
@@ -721,11 +814,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pK8sSecret.NewDeployer(&pK8sSecret.DeployerConfig{
KubeConfig: access.KubeConfig,
- 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"),
+ Namespace: maputil.GetOrDefaultString(options.ProviderServiceConfig, "namespace", "default"),
+ SecretName: maputil.GetString(options.ProviderServiceConfig, "secretName"),
+ SecretType: maputil.GetOrDefaultString(options.ProviderServiceConfig, "secretType", "kubernetes.io/tls"),
+ SecretDataKeyForCrt: maputil.GetOrDefaultString(options.ProviderServiceConfig, "secretDataKeyForCrt", "tls.crt"),
+ SecretDataKeyForKey: maputil.GetOrDefaultString(options.ProviderServiceConfig, "secretDataKeyForKey", "tls.key"),
})
return deployer, err
}
@@ -739,7 +832,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pNetlifySite.NewDeployer(&pNetlifySite.DeployerConfig{
ApiToken: access.ApiToken,
- SiteId: maputil.GetString(options.ProviderExtendedConfig, "siteId"),
+ SiteId: maputil.GetString(options.ProviderServiceConfig, "siteId"),
})
return deployer, err
}
@@ -756,8 +849,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
ApiToken: access.ApiToken,
ApiTokenSecret: access.ApiTokenSecret,
AllowInsecureConnections: access.AllowInsecureConnections,
- NodeName: maputil.GetString(options.ProviderExtendedConfig, "nodeName"),
- AutoRestart: maputil.GetBool(options.ProviderExtendedConfig, "autoRestart"),
+ NodeName: maputil.GetString(options.ProviderServiceConfig, "nodeName"),
+ AutoRestart: maputil.GetBool(options.ProviderServiceConfig, "autoRestart"),
})
return deployer, err
}
@@ -774,7 +867,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pQiniuCDN.NewDeployer(&pQiniuCDN.DeployerConfig{
AccessKey: access.AccessKey,
SecretKey: access.SecretKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -782,8 +875,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pQiniuPili.NewDeployer(&pQiniuPili.DeployerConfig{
AccessKey: access.AccessKey,
SecretKey: access.SecretKey,
- Hub: maputil.GetString(options.ProviderExtendedConfig, "hub"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Hub: maputil.GetString(options.ProviderServiceConfig, "hub"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -803,8 +896,40 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
case domain.DeploymentProviderTypeTencentCloudCDN:
deployer, err := pRainYunRCDN.NewDeployer(&pRainYunRCDN.DeployerConfig{
ApiKey: access.ApiKey,
- InstanceId: maputil.GetInt32(options.ProviderExtendedConfig, "instanceId"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ InstanceId: maputil.GetInt32(options.ProviderServiceConfig, "instanceId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
+ })
+ return deployer, err
+
+ default:
+ break
+ }
+ }
+
+ case domain.DeploymentProviderTypeRatPanelConsole, domain.DeploymentProviderTypeRatPanelSite:
+ {
+ access := domain.AccessConfigForRatPanel{}
+ 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.DeploymentProviderTypeRatPanelConsole:
+ deployer, err := pRatPanelConsole.NewDeployer(&pRatPanelConsole.DeployerConfig{
+ ApiUrl: access.ApiUrl,
+ AccessTokenId: access.AccessTokenId,
+ AccessToken: access.AccessToken,
+ AllowInsecureConnections: access.AllowInsecureConnections,
+ })
+ return deployer, err
+
+ case domain.DeploymentProviderTypeRatPanelSite:
+ deployer, err := pRatPanelSite.NewDeployer(&pRatPanelSite.DeployerConfig{
+ ApiUrl: access.ApiUrl,
+ AccessTokenId: access.AccessTokenId,
+ AccessToken: access.AccessToken,
+ AllowInsecureConnections: access.AllowInsecureConnections,
+ SiteName: maputil.GetString(options.ProviderServiceConfig, "siteName"),
})
return deployer, err
@@ -824,8 +949,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
ApiUrl: access.ApiUrl,
ApiToken: access.ApiToken,
AllowInsecureConnections: access.AllowInsecureConnections,
- ResourceType: pSafeLine.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")),
- CertificateId: maputil.GetInt32(options.ProviderExtendedConfig, "certificateId"),
+ ResourceType: pSafeLine.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ CertificateId: maputil.GetInt32(options.ProviderServiceConfig, "certificateId"),
})
return deployer, err
}
@@ -837,6 +962,18 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
+ jumpServers := make([]pSSH.JumpServerConfig, len(access.JumpServers))
+ for i, jumpServer := range access.JumpServers {
+ jumpServers[i] = pSSH.JumpServerConfig{
+ SshHost: jumpServer.Host,
+ SshPort: jumpServer.Port,
+ SshUsername: jumpServer.Username,
+ SshPassword: jumpServer.Password,
+ SshKey: jumpServer.Key,
+ SshKeyPassphrase: jumpServer.KeyPassphrase,
+ }
+ }
+
deployer, err := pSSH.NewDeployer(&pSSH.DeployerConfig{
SshHost: access.Host,
SshPort: access.Port,
@@ -844,18 +981,19 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
SshPassword: access.Password,
SshKey: access.Key,
SshKeyPassphrase: access.KeyPassphrase,
- 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"),
- OutputServerCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForServerOnly"),
- OutputIntermediaCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForIntermediaOnly"),
- 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"),
+ JumpServers: jumpServers,
+ UseSCP: maputil.GetBool(options.ProviderServiceConfig, "useSCP"),
+ PreCommand: maputil.GetString(options.ProviderServiceConfig, "preCommand"),
+ PostCommand: maputil.GetString(options.ProviderServiceConfig, "postCommand"),
+ OutputFormat: pSSH.OutputFormatType(maputil.GetOrDefaultString(options.ProviderServiceConfig, "format", string(pSSH.OUTPUT_FORMAT_PEM))),
+ OutputCertPath: maputil.GetString(options.ProviderServiceConfig, "certPath"),
+ OutputServerCertPath: maputil.GetString(options.ProviderServiceConfig, "certPathForServerOnly"),
+ OutputIntermediaCertPath: maputil.GetString(options.ProviderServiceConfig, "certPathForIntermediaOnly"),
+ OutputKeyPath: maputil.GetString(options.ProviderServiceConfig, "keyPath"),
+ PfxPassword: maputil.GetString(options.ProviderServiceConfig, "pfxPassword"),
+ JksAlias: maputil.GetString(options.ProviderServiceConfig, "jksAlias"),
+ JksKeypass: maputil.GetString(options.ProviderServiceConfig, "jksKeypass"),
+ JksStorepass: maputil.GetString(options.ProviderServiceConfig, "jksStorepass"),
})
return deployer, err
}
@@ -872,7 +1010,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudCDN.NewDeployer(&pTencentCloudCDN.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -880,11 +1018,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudCLB.NewDeployer(&pTencentCloudCLB.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pTencentCloudCLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -892,9 +1030,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudCOS.NewDeployer(&pTencentCloudCOS.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ Bucket: maputil.GetString(options.ProviderServiceConfig, "bucket"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -902,7 +1040,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudCSS.NewDeployer(&pTencentCloudCSS.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -910,7 +1048,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudECDN.NewDeployer(&pTencentCloudECDN.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -918,8 +1056,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudEO.NewDeployer(&pTencentCloudEO.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ ZoneId: maputil.GetString(options.ProviderServiceConfig, "zoneId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -927,8 +1065,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudSCF.NewDeployer(&pTencentCloudSCF.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -943,9 +1081,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudSSLDeploy.NewDeployer(&pTencentCloudSSLDeploy.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- 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 != "" }),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: maputil.GetString(options.ProviderServiceConfig, "resourceType"),
+ ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }),
})
return deployer, err
@@ -953,8 +1091,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudVOD.NewDeployer(&pTencentCloudVOD.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- SubAppId: maputil.GetInt64(options.ProviderExtendedConfig, "subAppId"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ SubAppId: maputil.GetInt64(options.ProviderServiceConfig, "subAppId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -962,9 +1100,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pTencentCloudWAF.NewDeployer(&pTencentCloudWAF.DeployerConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
- DomainId: maputil.GetString(options.ProviderExtendedConfig, "domainId"),
- InstanceId: maputil.GetString(options.ProviderExtendedConfig, "instanceId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
+ DomainId: maputil.GetString(options.ProviderServiceConfig, "domainId"),
+ InstanceId: maputil.GetString(options.ProviderServiceConfig, "instanceId"),
})
return deployer, err
@@ -986,7 +1124,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
PrivateKey: access.PrivateKey,
PublicKey: access.PublicKey,
ProjectId: access.ProjectId,
- DomainId: maputil.GetString(options.ProviderExtendedConfig, "domainId"),
+ DomainId: maputil.GetString(options.ProviderServiceConfig, "domainId"),
})
return deployer, err
@@ -995,9 +1133,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
PrivateKey: access.PrivateKey,
PublicKey: access.PublicKey,
ProjectId: access.ProjectId,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ Bucket: maputil.GetString(options.ProviderServiceConfig, "bucket"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -1018,7 +1156,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pUpyunCDN.NewDeployer(&pUpyunCDN.DeployerConfig{
Username: access.Username,
Password: access.Password,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -1039,11 +1177,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pVolcEngineALB.NewDeployer(&pVolcEngineALB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.SecretAccessKey,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pVolcEngineALB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -1051,7 +1189,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pVolcEngineCDN.NewDeployer(&pVolcEngineCDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.SecretAccessKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -1059,7 +1197,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pVolcEngineCertCenter.NewDeployer(&pVolcEngineCertCenter.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.SecretAccessKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
})
return deployer, err
@@ -1067,10 +1205,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pVolcEngineCLB.NewDeployer(&pVolcEngineCLB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.SecretAccessKey,
- 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"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ResourceType: pVolcEngineCLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
+ ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
})
return deployer, err
@@ -1078,7 +1216,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pVolcEngineDCDN.NewDeployer(&pVolcEngineDCDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.SecretAccessKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -1086,9 +1224,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pVolcEngineImageX.NewDeployer(&pVolcEngineImageX.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.SecretAccessKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- ServiceId: maputil.GetString(options.ProviderExtendedConfig, "serviceId"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ ServiceId: maputil.GetString(options.ProviderServiceConfig, "serviceId"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -1096,7 +1234,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pVolcEngineLive.NewDeployer(&pVolcEngineLive.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.SecretAccessKey,
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -1104,9 +1242,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pVolcEngineTOS.NewDeployer(&pVolcEngineTOS.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.SecretAccessKey,
- Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
- Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"),
- Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"),
+ Region: maputil.GetString(options.ProviderServiceConfig, "region"),
+ Bucket: maputil.GetString(options.ProviderServiceConfig, "bucket"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
@@ -1115,7 +1253,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
}
}
- case domain.DeploymentProviderTypeWangsuCDNPro:
+ case domain.DeploymentProviderTypeWangsuCDN, domain.DeploymentProviderTypeWangsuCDNPro, domain.DeploymentProviderTypeWangsuCertificate:
{
access := domain.AccessConfigForWangsu{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -1123,15 +1261,31 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
}
switch options.Provider {
+ case domain.DeploymentProviderTypeWangsuCDN:
+ deployer, err := pWangsuCDN.NewDeployer(&pWangsuCDN.DeployerConfig{
+ AccessKeyId: access.AccessKeyId,
+ AccessKeySecret: access.AccessKeySecret,
+ Domains: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "domains"), ";"), func(s string) bool { return s != "" }),
+ })
+ return deployer, err
+
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"),
+ Environment: maputil.GetOrDefaultString(options.ProviderServiceConfig, "environment", "production"),
+ Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
+ CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
+ WebhookId: maputil.GetString(options.ProviderServiceConfig, "webhookId"),
+ })
+ return deployer, err
+
+ case domain.DeploymentProviderTypeWangsuCertificate:
+ deployer, err := pWangsuCertificate.NewDeployer(&pWangsuCertificate.DeployerConfig{
+ AccessKeyId: access.AccessKeyId,
+ AccessKeySecret: access.AccessKeySecret,
+ CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
})
return deployer, err
@@ -1157,7 +1311,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
}
}
- if extendedHeadersString := maputil.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" {
+ if extendedHeadersString := maputil.GetString(options.ProviderServiceConfig, "headers"); extendedHeadersString != "" {
h, err := httputil.ParseHeaders(extendedHeadersString)
if err != nil {
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
@@ -1169,7 +1323,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
deployer, err := pWebhook.NewDeployer(&pWebhook.DeployerConfig{
WebhookUrl: access.Url,
- WebhookData: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForDeployment),
+ WebhookData: maputil.GetOrDefaultString(options.ProviderServiceConfig, "webhookData", access.DefaultDataForDeployment),
Method: access.Method,
Headers: mergedHeaders,
AllowInsecureConnections: access.AllowInsecureConnections,
diff --git a/internal/domain/access.go b/internal/domain/access.go
index 35dd9b0a..13975392 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -17,10 +17,17 @@ type Access struct {
type AccessConfigFor1Panel struct {
ApiUrl string `json:"apiUrl"`
+ ApiVersion string `json:"apiVersion"`
ApiKey string `json:"apiKey"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
+type AccessConfigForACMECA struct {
+ Endpoint string `json:"endpoint"`
+ EabKid string `json:"eabKid,omitempty"`
+ EabHmacKey string `json:"eabHmacKey,omitempty"`
+}
+
type AccessConfigForACMEHttpReq struct {
Endpoint string `json:"endpoint"`
Mode string `json:"mode,omitempty"`
@@ -60,6 +67,12 @@ type AccessConfigForBaotaPanel struct {
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
+type AccessConfigForBaotaWAF struct {
+ ApiUrl string `json:"apiUrl"`
+ ApiKey string `json:"apiKey"`
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+}
+
type AccessConfigForBytePlus struct {
AccessKey string `json:"accessKey"`
SecretKey string `json:"secretKey"`
@@ -133,6 +146,14 @@ type AccessConfigForEmail struct {
DefaultReceiverAddress string `json:"defaultReceiverAddress,omitempty"`
}
+type AccessConfigForFlexCDN struct {
+ ApiUrl string `json:"apiUrl"`
+ ApiRole string `json:"apiRole"`
+ AccessKeyId string `json:"accessKeyId"`
+ AccessKey string `json:"accessKey"`
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+}
+
type AccessConfigForGcore struct {
ApiToken string `json:"apiToken"`
}
@@ -178,6 +199,15 @@ type AccessConfigForLarkBot struct {
WebhookUrl string `json:"webhookUrl"`
}
+type AccessConfigForLeCDN struct {
+ ApiUrl string `json:"apiUrl"`
+ ApiVersion string `json:"apiVersion"`
+ ApiRole string `json:"apiRole"`
+ Username string `json:"username"`
+ Password string `json:"password"`
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+}
+
type AccessConfigForMattermost struct {
ServerUrl string `json:"serverUrl"`
Username string `json:"username"`
@@ -240,6 +270,13 @@ type AccessConfigForRainYun struct {
ApiKey string `json:"apiKey"`
}
+type AccessConfigForRatPanel struct {
+ ApiUrl string `json:"apiUrl"`
+ AccessTokenId int32 `json:"accessTokenId"`
+ AccessToken string `json:"accessToken"`
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+}
+
type AccessConfigForSafeLine struct {
ApiUrl string `json:"apiUrl"`
ApiToken string `json:"apiToken"`
@@ -253,6 +290,14 @@ type AccessConfigForSSH struct {
Password string `json:"password,omitempty"`
Key string `json:"key,omitempty"`
KeyPassphrase string `json:"keyPassphrase,omitempty"`
+ JumpServers []struct {
+ Host string `json:"host"`
+ Port int32 `json:"port"`
+ Username string `json:"username"`
+ Password string `json:"password,omitempty"`
+ Key string `json:"key,omitempty"`
+ KeyPassphrase string `json:"keyPassphrase,omitempty"`
+ } `json:"jumpServers,omitempty"`
}
type AccessConfigForSSLCom struct {
@@ -260,7 +305,7 @@ type AccessConfigForSSLCom struct {
EabHmacKey string `json:"eabHmacKey"`
}
-type AccessConfigForTelegram struct {
+type AccessConfigForTelegramBot struct {
BotToken string `json:"botToken"`
DefaultChatId int64 `json:"defaultChatId,omitempty"`
}
diff --git a/internal/domain/certificate.go b/internal/domain/certificate.go
index d2a998da..710ce268 100644
--- a/internal/domain/certificate.go
+++ b/internal/domain/certificate.go
@@ -20,7 +20,7 @@ type Certificate struct {
SerialNumber string `json:"serialNumber" db:"serialNumber"`
Certificate string `json:"certificate" db:"certificate"`
PrivateKey string `json:"privateKey" db:"privateKey"`
- Issuer string `json:"issuer" db:"issuer"`
+ IssuerOrg string `json:"issuerOrg" db:"issuerOrg"`
IssuerCertificate string `json:"issuerCertificate" db:"issuerCertificate"`
KeyAlgorithm CertificateKeyAlgorithmType `json:"keyAlgorithm" db:"keyAlgorithm"`
EffectAt time.Time `json:"effectAt" db:"effectAt"`
@@ -38,7 +38,7 @@ type Certificate struct {
func (c *Certificate) PopulateFromX509(certX509 *x509.Certificate) *Certificate {
c.SubjectAltNames = strings.Join(certX509.DNSNames, ";")
c.SerialNumber = strings.ToUpper(certX509.SerialNumber.Text(16))
- c.Issuer = strings.Join(certX509.Issuer.Organization, ";")
+ c.IssuerOrg = strings.Join(certX509.Issuer.Organization, ";")
c.EffectAt = certX509.NotBefore
c.ExpireAt = certX509.NotAfter
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index c635aad1..db0de59d 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -10,7 +10,7 @@ type AccessProviderType string
*/
const (
AccessProviderType1Panel = AccessProviderType("1panel")
- AccessProviderTypeACMECA = AccessProviderType("acmeca") // ACME CA(预留)
+ AccessProviderTypeACMECA = AccessProviderType("acmeca")
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留)
AccessProviderTypeAliyun = AccessProviderType("aliyun")
@@ -19,6 +19,7 @@ const (
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
AccessProviderTypeBaishan = AccessProviderType("baishan")
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
+ AccessProviderTypeBaotaWAF = AccessProviderType("baotawaf")
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
AccessProviderTypeBunny = AccessProviderType("bunny")
AccessProviderTypeBuypass = AccessProviderType("buypass")
@@ -36,8 +37,8 @@ const (
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
AccessProviderTypeEdgio = AccessProviderType("edgio")
AccessProviderTypeEmail = AccessProviderType("email")
- AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
- AccessProviderTypeFlexCDN = AccessProviderType("flexcdn") // FlexCDN(预留)
+ AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
+ AccessProviderTypeFlexCDN = AccessProviderType("flexcdn")
AccessProviderTypeGname = AccessProviderType("gname")
AccessProviderTypeGcore = AccessProviderType("gcore")
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
@@ -49,7 +50,7 @@ const (
AccessProviderTypeLarkBot = AccessProviderType("larkbot")
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging")
- AccessProviderTypeLeCDN = AccessProviderType("lecdn") // LeCDN(预留)
+ AccessProviderTypeLeCDN = AccessProviderType("lecdn")
AccessProviderTypeLocal = AccessProviderType("local")
AccessProviderTypeMattermost = AccessProviderType("mattermost")
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
@@ -64,10 +65,11 @@ const (
AccessProviderTypeQiniu = AccessProviderType("qiniu")
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
AccessProviderTypeRainYun = AccessProviderType("rainyun")
+ AccessProviderTypeRatPanel = AccessProviderType("ratpanel")
AccessProviderTypeSafeLine = AccessProviderType("safeline")
AccessProviderTypeSSH = AccessProviderType("ssh")
AccessProviderTypeSSLCOM = AccessProviderType("sslcom")
- AccessProviderTypeTelegram = AccessProviderType("telegram")
+ AccessProviderTypeTelegramBot = AccessProviderType("telegrambot")
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
AccessProviderTypeUCloud = AccessProviderType("ucloud")
AccessProviderTypeUpyun = AccessProviderType("upyun")
@@ -90,6 +92,7 @@ type CAProviderType string
NOTICE: If you add new constant, please keep ASCII order.
*/
const (
+ CAProviderTypeACMECA = CAProviderType(AccessProviderTypeACMECA)
CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass)
CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices)
CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt)
@@ -172,7 +175,7 @@ const (
DeploymentProviderTypeAliyunDDoS = DeploymentProviderType(AccessProviderTypeAliyun + "-ddos")
DeploymentProviderTypeAliyunESA = DeploymentProviderType(AccessProviderTypeAliyun + "-esa")
DeploymentProviderTypeAliyunFC = DeploymentProviderType(AccessProviderTypeAliyun + "-fc")
- DeploymentProviderTypeAliyunGA = DeploymentProviderType(AccessProviderTypeAliyun + "-ga") // 阿里云全球加速(预留)
+ DeploymentProviderTypeAliyunGA = DeploymentProviderType(AccessProviderTypeAliyun + "-ga")
DeploymentProviderTypeAliyunLive = DeploymentProviderType(AccessProviderTypeAliyun + "-live")
DeploymentProviderTypeAliyunNLB = DeploymentProviderType(AccessProviderTypeAliyun + "-nlb")
DeploymentProviderTypeAliyunOSS = DeploymentProviderType(AccessProviderTypeAliyun + "-oss")
@@ -188,13 +191,15 @@ const (
DeploymentProviderTypeBaishanCDN = DeploymentProviderType(AccessProviderTypeBaishan + "-cdn")
DeploymentProviderTypeBaotaPanelConsole = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-console")
DeploymentProviderTypeBaotaPanelSite = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-site")
+ DeploymentProviderTypeBaotaWAFConsole = DeploymentProviderType(AccessProviderTypeBaotaWAF + "-console")
+ DeploymentProviderTypeBaotaWAFSite = DeploymentProviderType(AccessProviderTypeBaotaWAF + "-site")
DeploymentProviderTypeBunnyCDN = DeploymentProviderType(AccessProviderTypeBunny + "-cdn")
DeploymentProviderTypeBytePlusCDN = DeploymentProviderType(AccessProviderTypeBytePlus + "-cdn")
DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly)
DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly)
DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn")
DeploymentProviderTypeEdgioApplications = DeploymentProviderType(AccessProviderTypeEdgio + "-applications")
- DeploymentProviderTypeFlexCDN = DeploymentProviderType(AccessProviderTypeFlexCDN) // FlexCDN(预留)
+ DeploymentProviderTypeFlexCDN = DeploymentProviderType(AccessProviderTypeFlexCDN)
DeploymentProviderTypeGcoreCDN = DeploymentProviderType(AccessProviderTypeGcore + "-cdn")
DeploymentProviderTypeGoEdge = DeploymentProviderType(AccessProviderTypeGoEdge)
DeploymentProviderTypeHuaweiCloudCDN = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-cdn")
@@ -206,7 +211,7 @@ const (
DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live")
DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod")
DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret")
- DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN) // LeCDN(预留)
+ DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN)
DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal)
DeploymentProviderTypeNetlifySite = DeploymentProviderType(AccessProviderTypeNetlify + "-site")
DeploymentProviderTypeProxmoxVE = DeploymentProviderType(AccessProviderTypeProxmoxVE)
@@ -214,6 +219,8 @@ const (
DeploymentProviderTypeQiniuKodo = DeploymentProviderType(AccessProviderTypeQiniu + "-kodo")
DeploymentProviderTypeQiniuPili = DeploymentProviderType(AccessProviderTypeQiniu + "-pili")
DeploymentProviderTypeRainYunRCDN = DeploymentProviderType(AccessProviderTypeRainYun + "-rcdn")
+ DeploymentProviderTypeRatPanelConsole = DeploymentProviderType(AccessProviderTypeRatPanel + "-console")
+ DeploymentProviderTypeRatPanelSite = DeploymentProviderType(AccessProviderTypeRatPanel + "-site")
DeploymentProviderTypeSafeLine = DeploymentProviderType(AccessProviderTypeSafeLine)
DeploymentProviderTypeSSH = DeploymentProviderType(AccessProviderTypeSSH)
DeploymentProviderTypeTencentCloudCDN = DeploymentProviderType(AccessProviderTypeTencentCloud + "-cdn")
@@ -239,9 +246,9 @@ const (
DeploymentProviderTypeVolcEngineImageX = DeploymentProviderType(AccessProviderTypeVolcEngine + "-imagex")
DeploymentProviderTypeVolcEngineLive = DeploymentProviderType(AccessProviderTypeVolcEngine + "-live")
DeploymentProviderTypeVolcEngineTOS = DeploymentProviderType(AccessProviderTypeVolcEngine + "-tos")
- DeploymentProviderTypeWangsuCDN = DeploymentProviderType(AccessProviderTypeWangsu + "-cdn") // 网宿 CDN(预留)
+ DeploymentProviderTypeWangsuCDN = DeploymentProviderType(AccessProviderTypeWangsu + "-cdn")
DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro")
- DeploymentProviderTypeWangsuCert = DeploymentProviderType(AccessProviderTypeWangsu + "-cert") // 网宿证书管理(预留)
+ DeploymentProviderTypeWangsuCertificate = DeploymentProviderType(AccessProviderTypeWangsu + "-certificate")
DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook)
)
@@ -259,7 +266,7 @@ const (
NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail)
NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot)
NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost)
- NotificationProviderTypeTelegram = NotificationProviderType(AccessProviderTypeTelegram)
+ NotificationProviderTypeTelegramBot = NotificationProviderType(AccessProviderTypeTelegramBot)
NotificationProviderTypeWebhook = NotificationProviderType(AccessProviderTypeWebhook)
NotificationProviderTypeWeComBot = NotificationProviderType(AccessProviderTypeWeComBot)
)
diff --git a/internal/domain/workflow.go b/internal/domain/workflow.go
index 6f3cccea..6a96dd81 100644
--- a/internal/domain/workflow.go
+++ b/internal/domain/workflow.go
@@ -105,11 +105,6 @@ type WorkflowNodeConfigForNotify struct {
}
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
- skipBeforeExpiryDays := maputil.GetInt32(n.Config, "skipBeforeExpiryDays")
- if skipBeforeExpiryDays == 0 {
- skipBeforeExpiryDays = 30
- }
-
return WorkflowNodeConfigForApply{
Domains: maputil.GetString(n.Config, "domains"),
ContactEmail: maputil.GetString(n.Config, "contactEmail"),
@@ -126,7 +121,7 @@ func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
DnsTTL: maputil.GetInt32(n.Config, "dnsTTL"),
DisableFollowCNAME: maputil.GetBool(n.Config, "disableFollowCNAME"),
DisableARI: maputil.GetBool(n.Config, "disableARI"),
- SkipBeforeExpiryDays: skipBeforeExpiryDays,
+ SkipBeforeExpiryDays: maputil.GetOrDefaultInt32(n.Config, "skipBeforeExpiryDays", 30),
}
}
diff --git a/internal/notify/notifier.go b/internal/notify/notifier.go
index 955e88c3..ee3fbd2f 100644
--- a/internal/notify/notifier.go
+++ b/internal/notify/notifier.go
@@ -31,9 +31,9 @@ func NewWithWorkflowNode(config NotifierWithWorkflowNodeConfig) (Notifier, error
nodeConfig := config.Node.GetConfigForNotify()
options := ¬ifierProviderOptions{
- Provider: domain.NotificationProviderType(nodeConfig.Provider),
- ProviderAccessConfig: make(map[string]any),
- ProviderExtendedConfig: nodeConfig.ProviderConfig,
+ Provider: domain.NotificationProviderType(nodeConfig.Provider),
+ ProviderAccessConfig: make(map[string]any),
+ ProviderServiceConfig: nodeConfig.ProviderConfig,
}
accessRepo := repository.NewAccessRepository()
diff --git a/internal/notify/providers.go b/internal/notify/providers.go
index c57b9c82..3a8c575d 100644
--- a/internal/notify/providers.go
+++ b/internal/notify/providers.go
@@ -6,21 +6,21 @@ import (
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
- pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
+ pDingTalkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
- pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
+ pLarkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
- pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
+ pTelegramBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
- pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
+ pWeComBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
httputil "github.com/usual2970/certimate/internal/pkg/utils/http"
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
)
type notifierProviderOptions struct {
- Provider domain.NotificationProviderType
- ProviderAccessConfig map[string]any
- ProviderExtendedConfig map[string]any
+ Provider domain.NotificationProviderType
+ ProviderAccessConfig map[string]any
+ ProviderServiceConfig map[string]any
}
func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier, error) {
@@ -36,7 +36,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
- return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
+ return pDingTalkBot.NewNotifier(&pDingTalkBot.NotifierConfig{
WebhookUrl: access.WebhookUrl,
Secret: access.Secret,
})
@@ -55,8 +55,8 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
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),
+ SenderAddress: maputil.GetOrDefaultString(options.ProviderServiceConfig, "senderAddress", access.DefaultSenderAddress),
+ ReceiverAddress: maputil.GetOrDefaultString(options.ProviderServiceConfig, "receiverAddress", access.DefaultReceiverAddress),
})
}
@@ -67,7 +67,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
- return pLark.NewNotifier(&pLark.NotifierConfig{
+ return pLarkBot.NewNotifier(&pLarkBot.NotifierConfig{
WebhookUrl: access.WebhookUrl,
})
}
@@ -83,20 +83,20 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
ServerUrl: access.ServerUrl,
Username: access.Username,
Password: access.Password,
- ChannelId: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "channelId", access.DefaultChannelId),
+ ChannelId: maputil.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.DefaultChannelId),
})
}
- case domain.NotificationProviderTypeTelegram:
+ case domain.NotificationProviderTypeTelegramBot:
{
- access := domain.AccessConfigForTelegram{}
+ access := domain.AccessConfigForTelegramBot{}
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{
+ return pTelegramBot.NewNotifier(&pTelegramBot.NotifierConfig{
BotToken: access.BotToken,
- ChatId: maputil.GetOrDefaultInt64(options.ProviderExtendedConfig, "chatId", access.DefaultChatId),
+ ChatId: maputil.GetOrDefaultInt64(options.ProviderServiceConfig, "chatId", access.DefaultChatId),
})
}
@@ -117,7 +117,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
}
}
- if extendedHeadersString := maputil.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" {
+ if extendedHeadersString := maputil.GetString(options.ProviderServiceConfig, "headers"); extendedHeadersString != "" {
h, err := httputil.ParseHeaders(extendedHeadersString)
if err != nil {
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
@@ -129,7 +129,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
WebhookUrl: access.Url,
- WebhookData: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForNotification),
+ WebhookData: maputil.GetOrDefaultString(options.ProviderServiceConfig, "webhookData", access.DefaultDataForNotification),
Method: access.Method,
Headers: mergedHeaders,
AllowInsecureConnections: access.AllowInsecureConnections,
@@ -143,7 +143,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
- return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
+ return pWeComBot.NewNotifier(&pWeComBot.NotifierConfig{
WebhookUrl: access.WebhookUrl,
})
}
diff --git a/internal/notify/providers_deprecated.go b/internal/notify/providers_deprecated.go
index 1e862866..d2d926a6 100644
--- a/internal/notify/providers_deprecated.go
+++ b/internal/notify/providers_deprecated.go
@@ -6,17 +6,17 @@ import (
"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"
+ pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
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"
+ pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
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"
+ pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
- pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
+ pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
)
@@ -52,9 +52,9 @@ func createNotifierProviderUseGlobalSettings(channel domain.NotifyChannelType, c
case domain.NotifyChannelTypeGotify:
return pGotify.NewNotifier(&pGotify.NotifierConfig{
- Url: maputil.GetString(channelConfig, "url"),
- Token: maputil.GetString(channelConfig, "token"),
- Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1),
+ ServerUrl: maputil.GetString(channelConfig, "url"),
+ Token: maputil.GetString(channelConfig, "token"),
+ Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1),
})
case domain.NotifyChannelTypeLark:
@@ -83,7 +83,7 @@ func createNotifierProviderUseGlobalSettings(channel domain.NotifyChannelType, c
case domain.NotifyChannelTypeServerChan:
return pServerChan.NewNotifier(&pServerChan.NotifierConfig{
- Url: maputil.GetString(channelConfig, "url"),
+ ServerUrl: maputil.GetString(channelConfig, "url"),
})
case domain.NotifyChannelTypeTelegram:
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
index bf7026da..56deaa2d 100644
--- 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
@@ -24,6 +24,7 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider,
providerConfig := internal.NewDefaultConfig()
providerConfig.SecretID = config.AccessKeyId
providerConfig.SecretKey = config.AccessKeySecret
+ providerConfig.RegionID = config.Region
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
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
index 79df4083..51d4e7c4 100644
--- 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
@@ -89,6 +89,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
return &DNSProvider{
client: client,
config: config,
+
+ siteIDs: make(map[string]int64),
}, nil
}
@@ -100,7 +102,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
}
- siteId, err := d.getSiteId(authZone)
+ siteId, err := d.getSiteId(strings.TrimRight(authZone, "."))
if err != nil {
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err)
}
@@ -120,7 +122,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
}
- siteId, err := d.getSiteId(authZone)
+ siteId, err := d.getSiteId(strings.TrimRight(authZone, "."))
if err != nil {
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err)
}
@@ -148,10 +150,11 @@ func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
pageSize := 500
for {
request := &aliesa.ListSitesRequest{
- SiteName: tea.String(siteName),
- PageNumber: tea.Int32(int32(pageNumber)),
- PageSize: tea.Int32(int32(pageNumber)),
- AccessType: tea.String("NS"),
+ SiteName: tea.String(siteName),
+ SiteSearchType: tea.String("exact"),
+ PageNumber: tea.Int32(int32(pageNumber)),
+ PageSize: tea.Int32(int32(pageSize)),
+ AccessType: tea.String("NS"),
}
response, err := d.client.ListSites(request)
if err != nil {
@@ -178,7 +181,7 @@ func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
}
}
- return 0, errors.New("failed to get site id")
+ return 0, errors.New("site not found")
}
func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN string) (*aliesa.ListRecordsResponseBodyRecords, error) {
@@ -186,11 +189,12 @@ func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN string) (*aliesa
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)),
+ SiteId: tea.Int64(siteId),
+ Type: tea.String("TXT"),
+ RecordName: tea.String(effectiveFQDN),
+ RecordMatchType: tea.String("exact"),
+ PageNumber: tea.Int32(int32(pageNumber)),
+ PageSize: tea.Int32(int32(pageSize)),
}
response, err := d.client.ListRecords(request)
if err != nil {
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 069e01f9..e81b264f 100644
--- a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go
+++ b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go
@@ -9,12 +9,15 @@ import (
"net/url"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
- opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
+ onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
)
type DeployerConfig struct {
// 1Panel 地址。
ApiUrl string `json:"apiUrl"`
+ // 1Panel 版本。
+ // 可取值 "v1"、"v2"。
+ ApiVersion string `json:"apiVersion"`
// 1Panel 接口密钥。
ApiKey string `json:"apiKey"`
// 是否允许不安全的连接。
@@ -26,7 +29,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
- sdkClient *opsdk.Client
+ sdkClient *onepanelsdk.Client
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -36,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
panic("config is nil")
}
- client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
+ client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
if err != nil {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
@@ -59,7 +62,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
// 设置面板 SSL 证书
- updateSystemSSLReq := &opsdk.UpdateSystemSSLRequest{
+ updateSystemSSLReq := &onepanelsdk.UpdateSystemSSLRequest{
Cert: certPEM,
Key: privkeyPEM,
SSL: "enable",
@@ -79,16 +82,20 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
return &deployer.DeployResult{}, nil
}
-func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) {
+func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid 1panel api url")
}
+ if apiVersion == "" {
+ return nil, errors.New("invalid 1panel api version")
+ }
+
if apiKey == "" {
return nil, errors.New("invalid 1panel api key")
}
- client := opsdk.NewClient(apiUrl, apiKey)
+ client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
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 abec586c..88bf961a 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
@@ -15,6 +15,7 @@ var (
fInputCertPath string
fInputKeyPath string
fApiUrl string
+ fApiVersion string
fApiKey string
)
@@ -24,6 +25,7 @@ func init() {
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
+ flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
}
@@ -34,6 +36,7 @@ Shell command to run this test:
--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_APIVERSION="v1" \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key"
*/
func TestDeploy(t *testing.T) {
@@ -45,11 +48,13 @@ func TestDeploy(t *testing.T) {
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("APIURL: %v", fApiUrl),
+ fmt.Sprintf("APIVERSION: %v", fApiVersion),
fmt.Sprintf("APIKEY: %v", fApiKey),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
ApiUrl: fApiUrl,
+ ApiVersion: fApiVersion,
ApiKey: fApiKey,
AllowInsecureConnections: true,
AutoRestart: true,
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 7d360c77..690e5242 100644
--- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go
+++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go
@@ -12,12 +12,15 @@ 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/1panel-ssl"
- opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
+ onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
)
type DeployerConfig struct {
// 1Panel 地址。
ApiUrl string `json:"apiUrl"`
+ // 1Panel 版本。
+ // 可取值 "v1"、"v2"。
+ ApiVersion string `json:"apiVersion"`
// 1Panel 接口密钥。
ApiKey string `json:"apiKey"`
// 是否允许不安全的连接。
@@ -35,7 +38,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
- sdkClient *opsdk.Client
+ sdkClient *onepanelsdk.Client
sslUploader uploader.Uploader
}
@@ -46,14 +49,15 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
panic("config is nil")
}
- client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
+ client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
if err != nil {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
- ApiUrl: config.ApiUrl,
- ApiKey: config.ApiKey,
+ ApiUrl: config.ApiUrl,
+ ApiVersion: config.ApiVersion,
+ ApiKey: config.ApiKey,
})
if err != nil {
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
@@ -103,7 +107,7 @@ func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string,
}
// 获取网站 HTTPS 配置
- getHttpsConfReq := &opsdk.GetHttpsConfRequest{
+ getHttpsConfReq := &onepanelsdk.GetHttpsConfRequest{
WebsiteID: d.config.WebsiteId,
}
getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq)
@@ -122,7 +126,7 @@ func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string,
// 修改网站 HTTPS 配置
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
- updateHttpsConfReq := &opsdk.UpdateHttpsConfRequest{
+ updateHttpsConfReq := &onepanelsdk.UpdateHttpsConfRequest{
WebsiteID: d.config.WebsiteId,
Type: "existed",
WebsiteSSLID: certId,
@@ -147,7 +151,7 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
}
// 获取证书详情
- getWebsiteSSLReq := &opsdk.GetWebsiteSSLRequest{
+ getWebsiteSSLReq := &onepanelsdk.GetWebsiteSSLRequest{
SSLID: d.config.CertificateId,
}
getWebsiteSSLResp, err := d.sdkClient.GetWebsiteSSL(getWebsiteSSLReq)
@@ -157,7 +161,7 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
}
// 更新证书
- uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{
+ uploadWebsiteSSLReq := &onepanelsdk.UploadWebsiteSSLRequest{
Type: "paste",
SSLID: d.config.CertificateId,
Description: getWebsiteSSLResp.Data.Description,
@@ -173,16 +177,20 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
return nil
}
-func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) {
+func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid 1panel api url")
}
+ if apiVersion == "" {
+ return nil, errors.New("invalid 1panel api version")
+ }
+
if apiKey == "" {
return nil, errors.New("invalid 1panel api key")
}
- client := opsdk.NewClient(apiUrl, apiKey)
+ client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
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 702584e3..1d5bafef 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
@@ -15,6 +15,7 @@ var (
fInputCertPath string
fInputKeyPath string
fApiUrl string
+ fApiVersion string
fApiKey string
fWebsiteId int64
)
@@ -25,6 +26,7 @@ func init() {
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
+ flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "")
}
@@ -36,6 +38,7 @@ Shell command to run this test:
--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_APIVERSION="v1" \
--CERTIMATE_DEPLOYER_1PANELSITE_APIKEY="your-api-key" \
--CERTIMATE_DEPLOYER_1PANELSITE_WEBSITEID="your-website-id"
*/
@@ -48,12 +51,14 @@ func TestDeploy(t *testing.T) {
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("APIURL: %v", fApiUrl),
+ fmt.Sprintf("APIVERSION: %v", fApiVersion),
fmt.Sprintf("APIKEY: %v", fApiKey),
fmt.Sprintf("WEBSITEID: %v", fWebsiteId),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
ApiUrl: fApiUrl,
+ ApiVersion: fApiVersion,
ApiKey: fApiKey,
AllowInsecureConnections: true,
ResourceType: provider.RESOURCE_TYPE_WEBSITE,
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 3dca4a9d..35b4997c 100644
--- a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go
+++ b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go
@@ -157,7 +157,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
if listListenersResp.Body.Listeners != nil {
for _, listener := range listListenersResp.Body.Listeners {
- listenerIds = append(listenerIds, *listener.ListenerId)
+ listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
}
}
@@ -192,7 +192,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
if listListenersResp.Body.Listeners != nil {
for _, listener := range listListenersResp.Body.Listeners {
- listenerIds = append(listenerIds, *listener.ListenerId)
+ listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
}
}
@@ -211,8 +211,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
d.logger.Info("found https/quic listeners to deploy", slog.Any("listenerIds", listenerIds))
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)
+ }
}
}
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 c75119e9..11d5b565 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
@@ -96,6 +96,7 @@ func TestDeploy(t *testing.T) {
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
fmt.Sprintf("REGION: %v", fRegion),
fmt.Sprintf("LISTENERID: %v", fListenerId),
+ fmt.Sprintf("DOMAIN: %v", fDomain),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
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 d443514e..34c3a49e 100644
--- a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go
+++ b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go
@@ -171,7 +171,6 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
select {
case <-ctx.Done():
return ctx.Err()
-
default:
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
errs = append(errs, 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 3b8ce12d..dfa46173 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
@@ -18,7 +18,7 @@ var (
fAccessKeySecret string
fRegion string
fLoadbalancerId string
- fListenerPort int
+ fListenerPort int64
fDomain string
)
@@ -31,7 +31,7 @@ func init() {
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
- flag.IntVar(&fListenerPort, argsPrefix+"LISTENERPORT", 443, "")
+ flag.Int64Var(&fListenerPort, argsPrefix+"LISTENERPORT", 443, "")
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
}
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 8557068c..426aa3a6 100644
--- a/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go
+++ b/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go
@@ -22,6 +22,7 @@ type DeployerConfig struct {
// 阿里云地域。
Region string `json:"region"`
// 服务版本。
+ // 可取值 "2.0"、"3.0"。
ServiceVersion string `json:"serviceVersion"`
// 自定义域名(支持泛域名)。
Domain string `json:"domain"`
diff --git a/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go b/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go
new file mode 100644
index 00000000..f69660a8
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go
@@ -0,0 +1,322 @@
+package aliyunga
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "log/slog"
+ "strings"
+
+ aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
+ aliga "github.com/alibabacloud-go/ga-20191120/v3/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"
+ sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
+)
+
+type DeployerConfig struct {
+ // 阿里云 AccessKeyId。
+ AccessKeyId string `json:"accessKeyId"`
+ // 阿里云 AccessKeySecret。
+ AccessKeySecret string `json:"accessKeySecret"`
+ // 部署资源类型。
+ ResourceType ResourceType `json:"resourceType"`
+ // 全球加速实例 ID。
+ AcceleratorId string `json:"acceleratorId"`
+ // 全球加速监听 ID。
+ // 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。
+ ListenerId string `json:"listenerId,omitempty"`
+ // SNI 域名(不支持泛域名)。
+ // 部署资源类型为 [RESOURCE_TYPE_ACCELERATOR]、[RESOURCE_TYPE_LISTENER] 时选填。
+ Domain string `json:"domain,omitempty"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient *aliga.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)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create sdk client: %w", err)
+ }
+
+ uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret)
+ 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) {
+ // 上传证书到 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))
+ }
+
+ // 根据部署资源类型决定部署方式
+ switch d.config.ResourceType {
+ case RESOURCE_TYPE_ACCELERATOR:
+ if err := d.deployToAccelerator(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
+ return nil, err
+ }
+
+ case RESOURCE_TYPE_LISTENER:
+ if err := d.deployToListener(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
+ return nil, err
+ }
+
+ default:
+ return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
+ }
+
+ return &deployer.DeployResult{}, nil
+}
+
+func (d *DeployerProvider) deployToAccelerator(ctx context.Context, cloudCertId string) error {
+ if d.config.AcceleratorId == "" {
+ return errors.New("config `acceleratorId` is required")
+ }
+
+ // 查询 HTTPS 监听列表
+ // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlisteners
+ listenerIds := make([]string, 0)
+ listListenersPageNumber := int32(1)
+ listListenersPageSize := int32(50)
+ for {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
+
+ listListenersReq := &aliga.ListListenersRequest{
+ RegionId: tea.String("cn-hangzhou"),
+ AcceleratorId: tea.String(d.config.AcceleratorId),
+ PageNumber: tea.Int32(listListenersPageNumber),
+ PageSize: tea.Int32(listListenersPageSize),
+ }
+ listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
+ d.logger.Debug("sdk request 'ga.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'ga.ListListeners': %w", err)
+ }
+
+ if listListenersResp.Body.Listeners != nil {
+ for _, listener := range listListenersResp.Body.Listeners {
+ if strings.EqualFold(tea.StringValue(listener.Protocol), "https") {
+ listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
+ }
+ }
+ }
+
+ if len(listListenersResp.Body.Listeners) < int(listListenersPageSize) {
+ break
+ } else {
+ listListenersPageNumber++
+ }
+ }
+
+ // 遍历更新监听证书
+ if len(listenerIds) == 0 {
+ d.logger.Info("no ga listeners to deploy")
+ } else {
+ var errs []error
+ d.logger.Info("found https listeners to deploy", slog.Any("listenerIds", listenerIds))
+
+ for _, listenerId := range listenerIds {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ if err := d.updateListenerCertificate(ctx, d.config.AcceleratorId, listenerId, cloudCertId); err != nil {
+ errs = append(errs, err)
+ }
+ }
+ }
+
+ if len(errs) > 0 {
+ return errors.Join(errs...)
+ }
+ }
+
+ return nil
+}
+
+func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error {
+ if d.config.AcceleratorId == "" {
+ return errors.New("config `acceleratorId` is required")
+ }
+ if d.config.ListenerId == "" {
+ return errors.New("config `listenerId` is required")
+ }
+
+ // 更新监听
+ if err := d.updateListenerCertificate(ctx, d.config.AcceleratorId, d.config.ListenerId, cloudCertId); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudAcceleratorId string, cloudListenerId string, cloudCertId string) error {
+ // 查询监听绑定的证书列表
+ // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlistenercertificates
+ var listenerDefaultCertificate *aliga.ListListenerCertificatesResponseBodyCertificates
+ var listenerAdditionalCertificates []*aliga.ListListenerCertificatesResponseBodyCertificates = make([]*aliga.ListListenerCertificatesResponseBodyCertificates, 0)
+ var listListenerCertificatesNextToken *string
+ for {
+ listListenerCertificatesReq := &aliga.ListListenerCertificatesRequest{
+ RegionId: tea.String("cn-hangzhou"),
+ AcceleratorId: tea.String(d.config.AcceleratorId),
+ NextToken: listListenerCertificatesNextToken,
+ MaxResults: tea.Int32(20),
+ }
+ listListenerCertificatesResp, err := d.sdkClient.ListListenerCertificates(listListenerCertificatesReq)
+ d.logger.Debug("sdk request 'ga.ListListenerCertificates'", slog.Any("request", listListenerCertificatesReq), slog.Any("response", listListenerCertificatesResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'ga.ListListenerCertificates': %w", err)
+ }
+
+ if listListenerCertificatesResp.Body.Certificates != nil {
+ for _, certificate := range listListenerCertificatesResp.Body.Certificates {
+ if tea.BoolValue(certificate.IsDefault) {
+ listenerDefaultCertificate = certificate
+ } else {
+ listenerAdditionalCertificates = append(listenerAdditionalCertificates, certificate)
+ }
+ }
+ }
+
+ if listListenerCertificatesResp.Body.NextToken == nil {
+ break
+ } else {
+ listListenerCertificatesNextToken = listListenerCertificatesResp.Body.NextToken
+ }
+ }
+
+ if d.config.Domain == "" {
+ // 未指定 SNI,只需部署到监听器
+ if listenerDefaultCertificate != nil && tea.StringValue(listenerDefaultCertificate.CertificateId) == cloudCertId {
+ d.logger.Info("no need to update ga listener default certificate")
+ return nil
+ }
+
+ // 修改监听的属性
+ // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-updatelistener
+ updateListenerReq := &aliga.UpdateListenerRequest{
+ RegionId: tea.String("cn-hangzhou"),
+ ListenerId: tea.String(cloudListenerId),
+ Certificates: []*aliga.UpdateListenerRequestCertificates{{
+ Id: tea.String(cloudCertId),
+ }},
+ }
+ updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq)
+ d.logger.Debug("sdk request 'ga.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'ga.UpdateListener': %w", err)
+ }
+ } else {
+ // 指定 SNI,需部署到扩展域名
+ if sliceutil.Some(listenerAdditionalCertificates, func(item *aliga.ListListenerCertificatesResponseBodyCertificates) bool {
+ return tea.StringValue(item.CertificateId) == cloudCertId
+ }) {
+ d.logger.Info("no need to update ga listener additional certificate")
+ return nil
+ }
+
+ if sliceutil.Some(listenerAdditionalCertificates, func(item *aliga.ListListenerCertificatesResponseBodyCertificates) bool {
+ return tea.StringValue(item.Domain) == d.config.Domain
+ }) {
+ // 为监听替换扩展证书
+ // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-updateadditionalcertificatewithlistener
+ updateAdditionalCertificateWithListenerReq := &aliga.UpdateAdditionalCertificateWithListenerRequest{
+ RegionId: tea.String("cn-hangzhou"),
+ AcceleratorId: tea.String(cloudAcceleratorId),
+ ListenerId: tea.String(cloudListenerId),
+ CertificateId: tea.String(cloudCertId),
+ Domain: tea.String(d.config.Domain),
+ }
+ updateAdditionalCertificateWithListenerResp, err := d.sdkClient.UpdateAdditionalCertificateWithListener(updateAdditionalCertificateWithListenerReq)
+ d.logger.Debug("sdk request 'ga.UpdateAdditionalCertificateWithListener'", slog.Any("request", updateAdditionalCertificateWithListenerReq), slog.Any("response", updateAdditionalCertificateWithListenerResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'ga.UpdateAdditionalCertificateWithListener': %w", err)
+ }
+ } else {
+ // 为监听绑定扩展证书
+ // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-associateadditionalcertificateswithlistener
+ associateAdditionalCertificatesWithListenerReq := &aliga.AssociateAdditionalCertificatesWithListenerRequest{
+ RegionId: tea.String("cn-hangzhou"),
+ AcceleratorId: tea.String(cloudAcceleratorId),
+ ListenerId: tea.String(cloudListenerId),
+ Certificates: []*aliga.AssociateAdditionalCertificatesWithListenerRequestCertificates{{
+ Id: tea.String(cloudCertId),
+ Domain: tea.String(d.config.Domain),
+ }},
+ }
+ associateAdditionalCertificatesWithListenerResp, err := d.sdkClient.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesWithListenerReq)
+ d.logger.Debug("sdk request 'ga.AssociateAdditionalCertificatesWithListener'", slog.Any("request", associateAdditionalCertificatesWithListenerReq), slog.Any("response", associateAdditionalCertificatesWithListenerResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'ga.AssociateAdditionalCertificatesWithListener': %w", err)
+ }
+ }
+ }
+
+ return nil
+}
+
+func createSdkClient(accessKeyId, accessKeySecret string) (*aliga.Client, error) {
+ // 接入点一览 https://api.aliyun.com/product/Ga
+ config := &aliopen.Config{
+ AccessKeyId: tea.String(accessKeyId),
+ AccessKeySecret: tea.String(accessKeySecret),
+ Endpoint: tea.String("ga.cn-hangzhou.aliyuncs.com"),
+ }
+
+ client, err := aliga.NewClient(config)
+ if err != nil {
+ return nil, err
+ }
+
+ return client, nil
+}
+
+func createSslUploader(accessKeyId, accessKeySecret string) (uploader.Uploader, error) {
+ uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
+ AccessKeyId: accessKeyId,
+ AccessKeySecret: accessKeySecret,
+ Region: "cn-hangzhou",
+ })
+ return uploader, err
+}
diff --git a/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go b/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go
new file mode 100644
index 00000000..611ddc41
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go
@@ -0,0 +1,118 @@
+package aliyunga_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ga"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fAccessKeyId string
+ fAccessKeySecret string
+ fAcceleratorId string
+ fListenerId string
+ fDomain string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNGA_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
+ flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
+ flag.StringVar(&fAcceleratorId, argsPrefix+"ACCELERATORID", "", "")
+ flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
+ flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./aliyun_ga_test.go -args \
+ --CERTIMATE_DEPLOYER_ALIYUNGA_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_ALIYUNGA_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_ALIYUNGA_ACCESSKEYID="your-access-key-id" \
+ --CERTIMATE_DEPLOYER_ALIYUNGA_ACCESSKEYSECRET="your-access-key-secret" \
+ --CERTIMATE_DEPLOYER_ALIYUNGA_ACCELERATORID="your-ga-accelerator-id" \
+ --CERTIMATE_DEPLOYER_ALIYUNGA_LISTENERID="your-ga-listener-id" \
+ --CERTIMATE_DEPLOYER_ALIYUNGA_DOMAIN="your-ga-sni-domain"
+*/
+func TestDeploy(t *testing.T) {
+ flag.Parse()
+
+ t.Run("Deploy_ToAccelerator", 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("ACCELERATORID: %v", fAcceleratorId),
+ fmt.Sprintf("DOMAIN: %v", fDomain),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ AccessKeyId: fAccessKeyId,
+ AccessKeySecret: fAccessKeySecret,
+ ResourceType: provider.RESOURCE_TYPE_ACCELERATOR,
+ AcceleratorId: fAcceleratorId,
+ 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)
+ })
+
+ t.Run("Deploy_ToListener", 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("LISTENERID: %v", fListenerId),
+ fmt.Sprintf("DOMAIN: %v", fDomain),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ AccessKeyId: fAccessKeyId,
+ AccessKeySecret: fAccessKeySecret,
+ ResourceType: provider.RESOURCE_TYPE_LISTENER,
+ ListenerId: fListenerId,
+ 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-ga/consts.go b/internal/pkg/core/deployer/providers/aliyun-ga/consts.go
new file mode 100644
index 00000000..f96d98d5
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/aliyun-ga/consts.go
@@ -0,0 +1,10 @@
+package aliyunga
+
+type ResourceType string
+
+const (
+ // 资源类型:部署到指定全球加速器。
+ RESOURCE_TYPE_ACCELERATOR = ResourceType("accelerator")
+ // 资源类型:部署到指定监听器。
+ RESOURCE_TYPE_LISTENER = ResourceType("listener")
+)
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 b8391144..58015f3d 100644
--- a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go
+++ b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go
@@ -145,7 +145,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
if listListenersResp.Body.Listeners != nil {
for _, listener := range listListenersResp.Body.Listeners {
- listenerIds = append(listenerIds, *listener.ListenerId)
+ listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
}
}
@@ -167,7 +167,6 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
select {
case <-ctx.Done():
return ctx.Err()
-
default:
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
errs = append(errs, err)
diff --git a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go
new file mode 100644
index 00000000..811b350b
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go
@@ -0,0 +1,88 @@
+package baotapanelconsole
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "log/slog"
+ "net/url"
+
+ "github.com/usual2970/certimate/internal/pkg/core/deployer"
+ btsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/btwaf"
+)
+
+type DeployerConfig struct {
+ // 堡塔云 WAF 地址。
+ ApiUrl string `json:"apiUrl"`
+ // 堡塔云 WAF 接口密钥。
+ ApiKey string `json:"apiKey"`
+ // 是否允许不安全的连接。
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient *btsdk.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.ApiKey, 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) {
+ // 设置面板 SSL
+ configSetSSLReq := &btsdk.ConfigSetSSLRequest{
+ CertContent: certPEM,
+ KeyContent: privkeyPEM,
+ }
+ configSetSSLResp, err := d.sdkClient.ConfigSetSSL(configSetSSLReq)
+ d.logger.Debug("sdk request 'bt.ConfigSetSSL'", slog.Any("request", configSetSSLReq), slog.Any("response", configSetSSLResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'bt.ConfigSetSSL': %w", err)
+ }
+
+ return &deployer.DeployResult{}, nil
+}
+
+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")
+ }
+
+ if apiKey == "" {
+ return nil, errors.New("invalid baota api key")
+ }
+
+ client := btsdk.NewClient(apiUrl, apiKey)
+ if skipTlsVerify {
+ client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
+ }
+
+ return client, nil
+}
diff --git a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go
new file mode 100644
index 00000000..ba6ddd26
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go
@@ -0,0 +1,73 @@
+package baotapanelconsole_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-console"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fApiUrl string
+ fApiKey string
+ fSiteName string
+ fSitePort int64
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
+ flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./baotawaf_console_test.go -args \
+ --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_APIURL="http://127.0.0.1:8888" \
+ --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_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("APIURL: %v", fApiUrl),
+ fmt.Sprintf("APIKEY: %v", fApiKey),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ ApiUrl: fApiUrl,
+ ApiKey: fApiKey,
+ AllowInsecureConnections: 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/baotawaf-site/baotawaf_site.go b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go
new file mode 100644
index 00000000..ed05937a
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go
@@ -0,0 +1,151 @@
+package baotapanelwaf
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "log/slog"
+ "net/url"
+
+ "github.com/usual2970/certimate/internal/pkg/core/deployer"
+ btsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/btwaf"
+ typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
+)
+
+type DeployerConfig struct {
+ // 堡塔云 WAF 地址。
+ ApiUrl string `json:"apiUrl"`
+ // 堡塔云 WAF 接口密钥。
+ ApiKey string `json:"apiKey"`
+ // 是否允许不安全的连接。
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+ // 网站名称。
+ SiteName string `json:"siteName"`
+ // 网站 SSL 端口。
+ // 零值时默认为 443。
+ SitePort int32 `json:"sitePort,omitempty"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient *btsdk.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.ApiKey, 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.SiteName == "" {
+ return nil, errors.New("config `siteName` is required")
+ }
+ if d.config.SitePort == 0 {
+ d.config.SitePort = 443
+ }
+
+ // 遍历获取网站列表,获取网站 ID
+ // REF: https://support.huaweicloud.com/api-waf/ListHost.html
+ siteId := ""
+ getSitListPage := int32(1)
+ getSitListPageSize := int32(100)
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ default:
+ }
+
+ getSiteListReq := &btsdk.GetSiteListRequest{
+ SiteName: typeutil.ToPtr(d.config.SiteName),
+ Page: typeutil.ToPtr(getSitListPage),
+ PageSize: typeutil.ToPtr(getSitListPageSize),
+ }
+ getSiteListResp, err := d.sdkClient.GetSiteList(getSiteListReq)
+ d.logger.Debug("sdk request 'bt.GetSiteList'", slog.Any("request", getSiteListReq), slog.Any("response", getSiteListResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'bt.GetSiteList': %w", err)
+ }
+
+ if getSiteListResp.Result != nil && getSiteListResp.Result.List != nil {
+ for _, siteItem := range getSiteListResp.Result.List {
+ if siteItem.SiteName == d.config.SiteName {
+ siteId = siteItem.SiteId
+ break
+ }
+ }
+ }
+
+ if getSiteListResp.Result == nil || len(getSiteListResp.Result.List) < int(getSitListPageSize) {
+ break
+ } else {
+ getSitListPage++
+ }
+ }
+ if siteId == "" {
+ return nil, errors.New("site not found")
+ }
+
+ // 修改站点配置
+ modifySiteReq := &btsdk.ModifySiteRequest{
+ SiteId: siteId,
+ Type: typeutil.ToPtr("openCert"),
+ Server: &btsdk.SiteServerInfo{
+ ListenSSLPort: typeutil.ToPtr(d.config.SitePort),
+ SSL: &btsdk.SiteServerSSLInfo{
+ IsSSL: typeutil.ToPtr(int32(1)),
+ FullChain: typeutil.ToPtr(certPEM),
+ PrivateKey: typeutil.ToPtr(privkeyPEM),
+ },
+ },
+ }
+ modifySiteResp, err := d.sdkClient.ModifySite(modifySiteReq)
+ d.logger.Debug("sdk request 'bt.ModifySite'", slog.Any("request", modifySiteReq), slog.Any("response", modifySiteResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'bt.ModifySite': %w", err)
+ }
+
+ return &deployer.DeployResult{}, nil
+}
+
+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")
+ }
+
+ if apiKey == "" {
+ return nil, errors.New("invalid baota api key")
+ }
+
+ client := btsdk.NewClient(apiUrl, apiKey)
+ if skipTlsVerify {
+ client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
+ }
+
+ return client, nil
+}
diff --git a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go
new file mode 100644
index 00000000..4e1ffe34
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go
@@ -0,0 +1,81 @@
+package baotapanelwaf_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-site"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fApiUrl string
+ fApiKey string
+ fSiteName string
+ fSitePort int64
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_BAOTAWAFSITE_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
+ flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
+ flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "")
+ flag.Int64Var(&fSitePort, argsPrefix+"SITEPORT", 0, "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./baotawaf_site_test.go -args \
+ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_APIURL="http://127.0.0.1:8888" \
+ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_APIKEY="your-api-key" \
+ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_SITENAME="your-site-name"\
+ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_SITEPORT=443
+*/
+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("APIKEY: %v", fApiKey),
+ fmt.Sprintf("SITENAME: %v", fSiteName),
+ fmt.Sprintf("SITEPORT: %v", fSitePort),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ ApiUrl: fApiUrl,
+ ApiKey: fApiKey,
+ AllowInsecureConnections: true,
+ SiteName: fSiteName,
+ SitePort: int32(fSitePort),
+ })
+ 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/flexcdn/consts.go b/internal/pkg/core/deployer/providers/flexcdn/consts.go
new file mode 100644
index 00000000..be55a475
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/flexcdn/consts.go
@@ -0,0 +1,8 @@
+package flexcdn
+
+type ResourceType string
+
+const (
+ // 资源类型:替换指定证书。
+ RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
+)
diff --git a/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go
new file mode 100644
index 00000000..a12ed164
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go
@@ -0,0 +1,145 @@
+package flexcdn
+
+import (
+ "context"
+ "crypto/tls"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "log/slog"
+ "net/url"
+ "time"
+
+ "github.com/usual2970/certimate/internal/pkg/core/deployer"
+ flexcdnsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/flexcdn"
+ certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
+)
+
+type DeployerConfig struct {
+ // FlexCDN URL。
+ ApiUrl string `json:"apiUrl"`
+ // FlexCDN 用户角色。
+ // 可取值 "user"、"admin"。
+ ApiRole string `json:"apiRole"`
+ // FlexCDN AccessKeyId。
+ AccessKeyId string `json:"accessKeyId"`
+ // FlexCDN 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 *flexcdnsdk.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.ApiRole, 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://flexcdn.cloud/dev/api/service/SSLCertService?role=user#updateSSLCert
+ updateSSLCertReq := &flexcdnsdk.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 'flexcdn.UpdateSSLCert'", slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'flexcdn.UpdateSSLCert': %w", err)
+ }
+
+ return nil
+}
+
+func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*flexcdnsdk.Client, error) {
+ if _, err := url.Parse(apiUrl); err != nil {
+ return nil, errors.New("invalid flexcdn api url")
+ }
+
+ if apiRole != "user" && apiRole != "admin" {
+ return nil, errors.New("invalid flexcdn api role")
+ }
+
+ if accessKeyId == "" {
+ return nil, errors.New("invalid flexcdn access key id")
+ }
+
+ if accessKey == "" {
+ return nil, errors.New("invalid flexcdn access key")
+ }
+
+ client := flexcdnsdk.NewClient(apiUrl, apiRole, accessKeyId, accessKey)
+ if skipTlsVerify {
+ client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
+ }
+
+ return client, nil
+}
diff --git a/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go b/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go
new file mode 100644
index 00000000..b9b8de07
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go
@@ -0,0 +1,83 @@
+package flexcdn_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/flexcdn"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fApiUrl string
+ fAccessKeyId string
+ fAccessKey string
+ fCertificateId int64
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_FLEXCDN_"
+
+ 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.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./flexcdn_test.go -args \
+ --CERTIMATE_DEPLOYER_FLEXCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_FLEXCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_FLEXCDN_APIURL="http://127.0.0.1:7788" \
+ --CERTIMATE_DEPLOYER_FLEXCDN_ACCESSKEYID="your-access-key-id" \
+ --CERTIMATE_DEPLOYER_FLEXCDN_ACCESSKEY="your-access-key" \
+ --CERTIMATE_DEPLOYER_FLEXCDN_CERTIFICATEID="your-cerficiate-id"
+*/
+func TestDeploy(t *testing.T) {
+ flag.Parse()
+
+ t.Run("Deploy_ToCertificate", 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,
+ ApiRole: "user",
+ AccessKeyId: fAccessKeyId,
+ AccessKey: fAccessKey,
+ AllowInsecureConnections: true,
+ ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
+ CertificateId: 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/goedge/goedge.go b/internal/pkg/core/deployer/providers/goedge/goedge.go
index 73eade64..ecae774e 100644
--- a/internal/pkg/core/deployer/providers/goedge/goedge.go
+++ b/internal/pkg/core/deployer/providers/goedge/goedge.go
@@ -19,6 +19,7 @@ type DeployerConfig struct {
// GoEdge URL。
ApiUrl string `json:"apiUrl"`
// GoEdge 用户角色。
+ // 可取值 "user"、"admin"。
ApiRole string `json:"apiRole"`
// GoEdge AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
diff --git a/internal/pkg/core/deployer/providers/goedge/goedge_test.go b/internal/pkg/core/deployer/providers/goedge/goedge_test.go
index c8c32b37..d10f931c 100644
--- a/internal/pkg/core/deployer/providers/goedge/goedge_test.go
+++ b/internal/pkg/core/deployer/providers/goedge/goedge_test.go
@@ -17,7 +17,7 @@ var (
fApiUrl string
fAccessKeyId string
fAccessKey string
- fCertificateId int
+ fCertificateId int64
)
func init() {
@@ -28,7 +28,7 @@ func init() {
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "")
- flag.IntVar(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
+ flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
}
/*
@@ -45,7 +45,7 @@ Shell command to run this test:
func TestDeploy(t *testing.T) {
flag.Parse()
- t.Run("Deploy", func(t *testing.T) {
+ t.Run("Deploy_ToCertificate", func(t *testing.T) {
t.Log(strings.Join([]string{
"args:",
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
@@ -58,11 +58,12 @@ func TestDeploy(t *testing.T) {
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
ApiUrl: fApiUrl,
+ ApiRole: "user",
AccessKeyId: fAccessKeyId,
AccessKey: fAccessKey,
AllowInsecureConnections: true,
ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
- CertificateId: int64(fCertificateId),
+ CertificateId: fCertificateId,
})
if err != nil {
t.Errorf("err: %+v", err)
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 748111dd..23ec4a92 100644
--- a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go
+++ b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go
@@ -210,7 +210,6 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPEM str
select {
case <-ctx.Done():
return ctx.Err()
-
default:
if err := d.modifyListenerCertificate(ctx, listenerId, upres.CertId); err != nil {
errs = append(errs, err)
diff --git a/internal/pkg/core/deployer/providers/lecdn/consts.go b/internal/pkg/core/deployer/providers/lecdn/consts.go
new file mode 100644
index 00000000..f5b7c0c9
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/lecdn/consts.go
@@ -0,0 +1,8 @@
+package lecdn
+
+type ResourceType string
+
+const (
+ // 资源类型:替换指定证书。
+ RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
+)
diff --git a/internal/pkg/core/deployer/providers/lecdn/lecdn.go b/internal/pkg/core/deployer/providers/lecdn/lecdn.go
new file mode 100644
index 00000000..1ad88dcf
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/lecdn/lecdn.go
@@ -0,0 +1,176 @@
+package lecdn
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "log/slog"
+ "net/url"
+ "time"
+
+ "github.com/usual2970/certimate/internal/pkg/core/deployer"
+ leclientsdkv3 "github.com/usual2970/certimate/internal/pkg/sdk3rd/lecdn/v3/client"
+ lemastersdkv3 "github.com/usual2970/certimate/internal/pkg/sdk3rd/lecdn/v3/master"
+)
+
+type DeployerConfig struct {
+ // LeCDN URL。
+ ApiUrl string `json:"apiUrl"`
+ // LeCDN 版本。
+ // 可取值 "v3"。
+ ApiVersion string `json:"apiVersion"`
+ // LeCDN 用户角色。
+ // 可取值 "client"、"master"。
+ ApiRole string `json:"apiRole"`
+ // LeCDN 用户名。
+ Username string `json:"accessKeyId"`
+ // LeCDN 用户密码。
+ Password string `json:"accessKey"`
+ // 是否允许不安全的连接。
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+ // 部署资源类型。
+ ResourceType ResourceType `json:"resourceType"`
+ // 证书 ID。
+ // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。
+ CertificateId int64 `json:"certificateId,omitempty"`
+ // 客户 ID。
+ // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时选填。
+ ClientId int64 `json:"clientId,omitempty"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient interface{}
+}
+
+var _ deployer.Deployer = (*DeployerProvider)(nil)
+
+const (
+ apiVersionV3 = "v3"
+
+ apiRoleClient = "client"
+ apiRoleMaster = "master"
+)
+
+func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
+ if config == nil {
+ panic("config is nil")
+ }
+
+ client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiRole, config.Username, config.Password, 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")
+ }
+
+ // 修改证书
+ // REF: https://wdk0pwf8ul.feishu.cn/wiki/YE1XwCRIHiLYeKkPupgcXrlgnDd
+ switch sdkClient := d.sdkClient.(type) {
+ case *leclientsdkv3.Client:
+ updateSSLCertReq := &leclientsdkv3.UpdateCertificateRequest{
+ Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()),
+ Description: "upload from certimate",
+ Type: "upload",
+ SSLPEM: certPEM,
+ SSLKey: privkeyPEM,
+ AutoRenewal: false,
+ }
+ updateSSLCertResp, err := sdkClient.UpdateCertificate(d.config.CertificateId, updateSSLCertReq)
+ d.logger.Debug("sdk request 'lecdn.UpdateCertificate'", slog.Int64("certId", d.config.CertificateId), slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'lecdn.UpdateCertificate': %w", err)
+ }
+
+ case *lemastersdkv3.Client:
+ updateSSLCertReq := &lemastersdkv3.UpdateCertificateRequest{
+ ClientId: d.config.ClientId,
+ Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()),
+ Description: "upload from certimate",
+ Type: "upload",
+ SSLPEM: certPEM,
+ SSLKey: privkeyPEM,
+ AutoRenewal: false,
+ }
+ updateSSLCertResp, err := sdkClient.UpdateCertificate(d.config.CertificateId, updateSSLCertReq)
+ d.logger.Debug("sdk request 'lecdn.UpdateCertificate'", slog.Int64("certId", d.config.CertificateId), slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'lecdn.UpdateCertificate': %w", err)
+ }
+
+ default:
+ panic("sdk client is not implemented")
+ }
+
+ return nil
+}
+
+func createSdkClient(apiUrl, apiVersion, apiRole, username, password string, skipTlsVerify bool) (interface{}, error) {
+ if _, err := url.Parse(apiUrl); err != nil {
+ return nil, errors.New("invalid lecdn api url")
+ }
+
+ if username == "" {
+ return nil, errors.New("invalid lecdn username")
+ }
+
+ if password == "" {
+ return nil, errors.New("invalid lecdn password")
+ }
+
+ if apiVersion == apiVersionV3 && apiRole == apiRoleClient {
+ // v3 版客户端
+ client := leclientsdkv3.NewClient(apiUrl, username, password)
+ if skipTlsVerify {
+ client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
+ }
+
+ return client, nil
+ } else if apiVersion == apiVersionV3 && apiRole == apiRoleMaster {
+ // v3 版主控端
+ client := lemastersdkv3.NewClient(apiUrl, username, password)
+ if skipTlsVerify {
+ client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
+ }
+
+ return client, nil
+ }
+
+ return nil, fmt.Errorf("invalid lecdn api version or user role")
+}
diff --git a/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go b/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go
new file mode 100644
index 00000000..cbaa4523
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go
@@ -0,0 +1,87 @@
+package lecdn_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/lecdn"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fApiUrl string
+ fApiVersion string
+ fUsername string
+ fPassword string
+ fCertificateId int64
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_LECDN_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
+ flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v3", "")
+ flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "")
+ flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "")
+ flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./lecdn_test.go -args \
+ --CERTIMATE_DEPLOYER_LECDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_LECDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_LECDN_APIURL="http://127.0.0.1:5090" \
+ --CERTIMATE_DEPLOYER_LECDN_USERNAME="your-username" \
+ --CERTIMATE_DEPLOYER_LECDN_PASSWORD="your-password" \
+ --CERTIMATE_DEPLOYER_LECDN_CERTIFICATEID="your-cerficiate-id"
+*/
+func TestDeploy(t *testing.T) {
+ flag.Parse()
+
+ t.Run("Deploy_ToCertificate", 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("APIVERSION: %v", fApiVersion),
+ fmt.Sprintf("USERNAME: %v", fUsername),
+ fmt.Sprintf("PASSWORD: %v", fPassword),
+ fmt.Sprintf("CERTIFICATEID: %v", fCertificateId),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ ApiUrl: fApiUrl,
+ ApiVersion: fApiVersion,
+ ApiRole: "user",
+ Username: fUsername,
+ Password: fPassword,
+ AllowInsecureConnections: true,
+ ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
+ CertificateId: 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/ratpanel-console/ratpanel_console.go b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go
new file mode 100644
index 00000000..51faf4f2
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go
@@ -0,0 +1,94 @@
+package ratpanelconsole
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "log/slog"
+ "net/url"
+
+ "github.com/usual2970/certimate/internal/pkg/core/deployer"
+ rpsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/ratpanel"
+)
+
+type DeployerConfig struct {
+ // 耗子面板地址。
+ ApiUrl string `json:"apiUrl"`
+ // 耗子面板访问令牌 ID。
+ AccessTokenId int32 `json:"accessTokenId"`
+ // 耗子面板访问令牌。
+ AccessToken string `json:"accessToken"`
+ // 是否允许不安全的连接。
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient *rpsdk.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.AccessTokenId, config.AccessToken, 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) {
+ // 设置面板 SSL 证书
+ settingCertReq := &rpsdk.SettingCertRequest{
+ Certificate: certPEM,
+ PrivateKey: privkeyPEM,
+ }
+ settingCertResp, err := d.sdkClient.SettingCert(settingCertReq)
+ d.logger.Debug("sdk request 'ratpanel.SettingCert'", slog.Any("request", settingCertReq), slog.Any("response", settingCertResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.SettingCert': %w", err)
+ }
+
+ return &deployer.DeployResult{}, nil
+}
+
+func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) {
+ if _, err := url.Parse(apiUrl); err != nil {
+ return nil, errors.New("invalid ratpanel api url")
+ }
+
+ if accessTokenId == 0 {
+ return nil, errors.New("invalid ratpanel access token id")
+ }
+
+ if accessToken == "" {
+ return nil, errors.New("invalid ratpanel access token")
+ }
+
+ client := rpsdk.NewClient(apiUrl, accessTokenId, accessToken)
+ if skipTlsVerify {
+ client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
+ }
+
+ return client, nil
+}
diff --git a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go
new file mode 100644
index 00000000..3f3193b3
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go
@@ -0,0 +1,76 @@
+package ratpanelconsole_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ratpanel-console"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fApiUrl string
+ fAccessTokenId int64
+ fAccessToken string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_RATPANELCONSOLE_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
+ flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "")
+ flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./ratpanel_console_test.go -args \
+ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_APIURL="http://127.0.0.1:8888" \
+ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKENID="your-access-token-id" \
+ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKEN="your-access-token"
+*/
+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("ACCESSTOKENID: %v", fAccessTokenId),
+ fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ ApiUrl: fApiUrl,
+ AccessTokenId: int32(fAccessTokenId),
+ AccessToken: fAccessToken,
+ AllowInsecureConnections: 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/ratpanel-site/ratpanel_site.go b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go
new file mode 100644
index 00000000..b4e283be
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go
@@ -0,0 +1,101 @@
+package ratpanelsite
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "log/slog"
+ "net/url"
+
+ "github.com/usual2970/certimate/internal/pkg/core/deployer"
+ rpsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/ratpanel"
+)
+
+type DeployerConfig struct {
+ // 耗子面板地址。
+ ApiUrl string `json:"apiUrl"`
+ // 耗子面板访问令牌 ID。
+ AccessTokenId int32 `json:"accessTokenId"`
+ // 耗子面板访问令牌。
+ AccessToken string `json:"accessToken"`
+ // 是否允许不安全的连接。
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+ // 网站名称。
+ SiteName string `json:"siteName"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient *rpsdk.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.AccessTokenId, config.AccessToken, 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.SiteName == "" {
+ return nil, errors.New("config `siteName` is required")
+ }
+
+ // 设置站点 SSL 证书
+ websiteCertReq := &rpsdk.WebsiteCertRequest{
+ SiteName: d.config.SiteName,
+ Certificate: certPEM,
+ PrivateKey: privkeyPEM,
+ }
+ websiteCertResp, err := d.sdkClient.WebsiteCert(websiteCertReq)
+ d.logger.Debug("sdk request 'ratpanel.WebsiteCert'", slog.Any("request", websiteCertReq), slog.Any("response", websiteCertResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.WebsiteCert': %w", err)
+ }
+
+ return &deployer.DeployResult{}, nil
+}
+
+func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) {
+ if _, err := url.Parse(apiUrl); err != nil {
+ return nil, errors.New("invalid ratpanel api url")
+ }
+
+ if accessTokenId == 0 {
+ return nil, errors.New("invalid ratpanel access token id")
+ }
+
+ if accessToken == "" {
+ return nil, errors.New("invalid ratpanel access token")
+ }
+
+ client := rpsdk.NewClient(apiUrl, accessTokenId, accessToken)
+ if skipTlsVerify {
+ client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
+ }
+
+ return client, nil
+}
diff --git a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go
new file mode 100644
index 00000000..658175fb
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go
@@ -0,0 +1,81 @@
+package ratpanelsite_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ratpanel-site"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fApiUrl string
+ fAccessTokenId int64
+ fAccessToken string
+ fSiteName string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_RATPANELSITE_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
+ flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "")
+ flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "")
+ flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./ratpanel_site_test.go -args \
+ --CERTIMATE_DEPLOYER_RATPANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_RATPANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_RATPANELSITE_APIURL="http://127.0.0.1:8888" \
+ --CERTIMATE_DEPLOYER_RATPANELSITE_ACCESSTOKENID="your-access-token-id" \
+ --CERTIMATE_DEPLOYER_RATPANELSITE_ACCESSTOKEN="your-access-token" \
+ --CERTIMATE_DEPLOYER_RATPANELSITE_SITENAME="your-site-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("ACCESSTOKENID: %v", fAccessTokenId),
+ fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken),
+ fmt.Sprintf("SITENAME: %v", fSiteName),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ ApiUrl: fApiUrl,
+ AccessTokenId: int32(fAccessTokenId),
+ AccessToken: fAccessToken,
+ AllowInsecureConnections: true,
+ SiteName: fSiteName,
+ })
+ 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/safeline_test.go b/internal/pkg/core/deployer/providers/safeline/safeline_test.go
index 294086c8..67fe6755 100644
--- a/internal/pkg/core/deployer/providers/safeline/safeline_test.go
+++ b/internal/pkg/core/deployer/providers/safeline/safeline_test.go
@@ -16,7 +16,7 @@ var (
fInputKeyPath string
fApiUrl string
fApiToken string
- fCertificateId int
+ fCertificateId int64
)
func init() {
@@ -26,7 +26,7 @@ func init() {
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
- flag.IntVar(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
+ flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
}
/*
diff --git a/internal/pkg/core/deployer/providers/ssh/ssh.go b/internal/pkg/core/deployer/providers/ssh/ssh.go
index cf09214b..ae6e459f 100644
--- a/internal/pkg/core/deployer/providers/ssh/ssh.go
+++ b/internal/pkg/core/deployer/providers/ssh/ssh.go
@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"log/slog"
+ "net"
"os"
"path/filepath"
@@ -16,6 +17,23 @@ import (
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
)
+type JumpServerConfig struct {
+ // SSH 主机。
+ // 零值时默认为 "localhost"。
+ SshHost string `json:"sshHost,omitempty"`
+ // SSH 端口。
+ // 零值时默认为 22。
+ SshPort int32 `json:"sshPort,omitempty"`
+ // SSH 登录用户名。
+ SshUsername string `json:"sshUsername,omitempty"`
+ // SSH 登录密码。
+ SshPassword string `json:"sshPassword,omitempty"`
+ // SSH 登录私钥。
+ SshKey string `json:"sshKey,omitempty"`
+ // SSH 登录私钥口令。
+ SshKeyPassphrase string `json:"sshKeyPassphrase,omitempty"`
+}
+
type DeployerConfig struct {
// SSH 主机。
// 零值时默认为 "localhost"。
@@ -31,6 +49,8 @@ type DeployerConfig struct {
SshKey string `json:"sshKey,omitempty"`
// SSH 登录私钥口令。
SshKeyPassphrase string `json:"sshKeyPassphrase,omitempty"`
+ // 跳板机配置数组。
+ JumpServers []JumpServerConfig `json:"jumpServers,omitempty"`
// 是否回退使用 SCP。
UseSCP bool `json:"useSCP,omitempty"`
// 前置命令。
@@ -97,8 +117,61 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
return nil, fmt.Errorf("failed to extract certs: %w", err)
}
- // 连接
+ var targetConn net.Conn
+
+ // 连接到跳板机
+ if len(d.config.JumpServers) > 0 {
+ var jumpClient *ssh.Client
+ for i, jumpServerConf := range d.config.JumpServers {
+ d.logger.Info(fmt.Sprintf("connecting to jump server [%d]", i+1), slog.String("host", jumpServerConf.SshHost))
+
+ var jumpConn net.Conn
+ // 第一个连接是主机发起,后续通过跳板机发起
+ if jumpClient == nil {
+ jumpConn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", jumpServerConf.SshHost, jumpServerConf.SshPort))
+ } else {
+ jumpConn, err = jumpClient.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", jumpServerConf.SshHost, jumpServerConf.SshPort))
+ }
+ if err != nil {
+ return nil, fmt.Errorf("failed to connect to jump server [%d]: %w", i+1, err)
+ }
+ defer jumpConn.Close()
+
+ newClient, err := createSshClient(
+ jumpConn,
+ jumpServerConf.SshHost,
+ jumpServerConf.SshPort,
+ jumpServerConf.SshUsername,
+ jumpServerConf.SshPassword,
+ jumpServerConf.SshKey,
+ jumpServerConf.SshKeyPassphrase,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("failed to create jump server ssh client[%d]: %w", i+1, err)
+ }
+ defer newClient.Close()
+
+ jumpClient = newClient
+ d.logger.Info(fmt.Sprintf("jump server connected [%d]", i+1), slog.String("host", jumpServerConf.SshHost))
+ }
+
+ // 通过跳板机发起 TCP 连接到目标服务器
+ targetConn, err = jumpClient.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", d.config.SshHost, d.config.SshPort))
+ if err != nil {
+ return nil, fmt.Errorf("failed to connect to target server: %w", err)
+ }
+ } else {
+ // 直接发起 TCP 连接到目标服务器
+ targetConn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", d.config.SshHost, d.config.SshPort))
+ if err != nil {
+ return nil, fmt.Errorf("failed to connect to target server: %w", err)
+ }
+ }
+ defer targetConn.Close()
+
+ // 通过已有的连接创建目标服务器 SSH 客户端
client, err := createSshClient(
+ targetConn,
d.config.SshHost,
d.config.SshPort,
d.config.SshUsername,
@@ -189,7 +262,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
return &deployer.DeployResult{}, nil
}
-func createSshClient(host string, port int32, username string, password string, key string, keyPassphrase string) (*ssh.Client, error) {
+func createSshClient(conn net.Conn, host string, port int32, username string, password string, key string, keyPassphrase string) (*ssh.Client, error) {
if host == "" {
host = "localhost"
}
@@ -217,11 +290,16 @@ func createSshClient(host string, port int32, username string, password string,
authMethod = ssh.Password(password)
}
- return ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), &ssh.ClientConfig{
+ sshConn, chans, reqs, err := ssh.NewClientConn(conn, fmt.Sprintf("%s:%d", host, port), &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{authMethod},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
})
+ if err != nil {
+ return nil, err
+ }
+
+ return ssh.NewClient(sshConn, chans, reqs), nil
}
func execSshCommand(sshCli *ssh.Client, command string) (string, string, error) {
diff --git a/internal/pkg/core/deployer/providers/ssh/ssh_test.go b/internal/pkg/core/deployer/providers/ssh/ssh_test.go
index b63471d1..ae908185 100644
--- a/internal/pkg/core/deployer/providers/ssh/ssh_test.go
+++ b/internal/pkg/core/deployer/providers/ssh/ssh_test.go
@@ -15,7 +15,7 @@ var (
fInputCertPath string
fInputKeyPath string
fSshHost string
- fSshPort int
+ fSshPort int64
fSshUsername string
fSshPassword string
fOutputCertPath string
@@ -28,7 +28,7 @@ func init() {
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fSshHost, argsPrefix+"SSHHOST", "", "")
- flag.IntVar(&fSshPort, argsPrefix+"SSHPORT", 0, "")
+ flag.Int64Var(&fSshPort, argsPrefix+"SSHPORT", 0, "")
flag.StringVar(&fSshUsername, argsPrefix+"SSHUSERNAME", "", "")
flag.StringVar(&fSshPassword, argsPrefix+"SSHPASSWORD", "", "")
flag.StringVar(&fOutputCertPath, argsPrefix+"OUTPUTCERTPATH", "", "")
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 01f71d9e..a92e4eb1 100644
--- a/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go
+++ b/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go
@@ -2,9 +2,11 @@ package tencentcloudcdn
import (
"context"
+ "errors"
"fmt"
"log/slog"
"strings"
+ "time"
tccdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
@@ -132,6 +134,49 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
}
+
+ // 循环获取部署任务详情,等待任务状态变更
+ // REF: https://cloud.tencent.com.cn/document/api/400/91658
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ default:
+ }
+
+ describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
+ describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
+ describeHostDeployRecordDetailResp, err := d.sdkClients.SSL.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq)
+ d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
+ }
+
+ var runningCount, succeededCount, failedCount, totalCount int64
+ if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
+ return nil, errors.New("unexpected deployment job status")
+ } else {
+ if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
+ runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
+ succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
+ failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
+ totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
+ }
+
+ if succeededCount+failedCount == totalCount {
+ break
+ }
+ }
+
+ d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
+ time.Sleep(time.Second * 5)
+ }
}
return &deployer.DeployResult{}, nil
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 0c2f8902..2f7c0f22 100644
--- a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go
+++ b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log/slog"
+ "time"
tcclb "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb/v20180317"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
@@ -151,6 +152,49 @@ func (d *DeployerProvider) deployViaSslService(ctx context.Context, cloudCertId
return fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
}
+ // 循环获取部署任务详情,等待任务状态变更
+ // REF: https://cloud.tencent.com.cn/document/api/400/91658
+ for {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
+
+ describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
+ describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
+ describeHostDeployRecordDetailResp, err := d.sdkClients.SSL.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq)
+ d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
+ }
+
+ var runningCount, succeededCount, failedCount, totalCount int64
+ if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
+ return errors.New("unexpected deployment job status")
+ } else {
+ if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
+ runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
+ succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
+ failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
+ totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
+ }
+
+ if succeededCount+failedCount == totalCount {
+ break
+ }
+ }
+
+ d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
+ time.Sleep(time.Second * 5)
+ }
+
return nil
}
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 0949c5a3..99ee9b2f 100644
--- a/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go
+++ b/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log/slog"
+ "time"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
@@ -102,6 +103,49 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
}
+ // 循环获取部署任务详情,等待任务状态变更
+ // REF: https://cloud.tencent.com.cn/document/api/400/91658
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ default:
+ }
+
+ describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
+ describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
+ 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, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
+ }
+
+ var runningCount, succeededCount, failedCount, totalCount int64
+ if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
+ return nil, errors.New("unexpected deployment job status")
+ } else {
+ if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
+ runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
+ succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
+ failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
+ totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
+ }
+
+ if succeededCount+failedCount == totalCount {
+ break
+ }
+ }
+
+ d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
+ time.Sleep(time.Second * 5)
+ }
+
return &deployer.DeployResult{}, nil
}
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 ebe87025..88840f4a 100644
--- a/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go
+++ b/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go
@@ -2,9 +2,11 @@ package tencentcloudecdn
import (
"context"
+ "errors"
"fmt"
"log/slog"
"strings"
+ "time"
tccdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
@@ -103,7 +105,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
} else {
d.logger.Info("found ecdn instances to deploy", slog.Any("instanceIds", instanceIds))
- // 证书部署到 ECDN 实例
+ // 证书部署到 CDN 实例
// REF: https://cloud.tencent.com/document/product/400/91667
deployCertificateInstanceReq := tcssl.NewDeployCertificateInstanceRequest()
deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId)
@@ -115,6 +117,49 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
}
+
+ // 循环获取部署任务详情,等待任务状态变更
+ // REF: https://cloud.tencent.com.cn/document/api/400/91658
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ default:
+ }
+
+ describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
+ describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
+ describeHostDeployRecordDetailResp, err := d.sdkClients.SSL.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq)
+ d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
+ }
+
+ var runningCount, succeededCount, failedCount, totalCount int64
+ if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
+ return nil, errors.New("unexpected deployment job status")
+ } else {
+ if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
+ runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
+ succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
+ failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
+ totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
+ }
+
+ if succeededCount+failedCount == totalCount {
+ break
+ }
+ }
+
+ d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
+ time.Sleep(time.Second * 5)
+ }
}
return &deployer.DeployResult{}, nil
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 5f13660d..585000d9 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
@@ -116,30 +116,35 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
- describeHostDeployRecordDetailReq.Limit = common.Uint64Ptr(100)
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, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
}
+ var runningCount, succeededCount, failedCount, totalCount int64
if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
return nil, errors.New("unexpected deployment job status")
} else {
- acc := int64(0)
+ if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
+ runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
+ }
if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
- acc += *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
+ succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
}
if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
- acc += *describeHostDeployRecordDetailResp.Response.FailedTotalCount
+ failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
+ }
+ if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
+ totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
}
- if acc == *describeHostDeployRecordDetailResp.Response.TotalCount {
+ if succeededCount+failedCount == totalCount {
break
}
}
- d.logger.Info("waiting for deployment job completion ...")
+ d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
time.Sleep(time.Second * 5)
}
diff --git a/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go b/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go
new file mode 100644
index 00000000..43c65de2
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go
@@ -0,0 +1,104 @@
+package wangsucdn
+
+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/wangsu-certificate"
+ wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdn"
+)
+
+type DeployerConfig struct {
+ // 网宿云 AccessKeyId。
+ AccessKeyId string `json:"accessKeyId"`
+ // 网宿云 AccessKeySecret。
+ AccessKeySecret string `json:"accessKeySecret"`
+ // 加速域名数组。
+ Domains []string `json:"domains"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient *wangsusdk.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)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create sdk client: %w", err)
+ }
+
+ uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
+ AccessKeyId: config.AccessKeyId,
+ AccessKeySecret: config.AccessKeySecret,
+ })
+ 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
+ }
+ return d
+}
+
+func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
+ // 上传证书到证书管理
+ 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))
+ }
+
+ // 批量修改域名证书配置
+ // REF: https://www.wangsu.com/document/api-doc/37447
+ certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
+ batchUpdateCertificateConfigReq := &wangsusdk.BatchUpdateCertificateConfigRequest{
+ CertificateId: certId,
+ DomainNames: d.config.Domains,
+ }
+ batchUpdateCertificateConfigResp, err := d.sdkClient.BatchUpdateCertificateConfig(batchUpdateCertificateConfigReq)
+ d.logger.Debug("sdk request 'cdn.BatchUpdateCertificateConfig'", slog.Any("request", batchUpdateCertificateConfigReq), slog.Any("response", batchUpdateCertificateConfigResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'cdn.BatchUpdateCertificateConfig': %w", err)
+ }
+
+ return &deployer.DeployResult{}, nil
+}
+
+func createSdkClient(accessKeyId, accessKeySecret string) (*wangsusdk.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 wangsusdk.NewClient(accessKeyId, accessKeySecret), nil
+}
diff --git a/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn_test.go b/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn_test.go
new file mode 100644
index 00000000..99859b85
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn_test.go
@@ -0,0 +1,75 @@
+package wangsucdn_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-cdn"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fAccessKeyId string
+ fAccessKeySecret string
+ fDomain string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_WANGSUCDN_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
+ flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
+ flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./wangsu_cdn_test.go -args \
+ --CERTIMATE_DEPLOYER_WANGSUCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_WANGSUCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_WANGSUCDN_ACCESSKEYID="your-access-key-id" \
+ --CERTIMATE_DEPLOYER_WANGSUCDN_ACCESSKEYSECRET="your-access-key-secret" \
+ --CERTIMATE_DEPLOYER_WANGSUCDN_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("DOMAIN: %v", fDomain),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ AccessKeyId: fAccessKeyId,
+ AccessKeySecret: fAccessKeySecret,
+ Domains: []string{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/wangsu-cdnpro/wangsu_cdnpro.go b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go
index ee16b08a..4d5f2e10 100644
--- a/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go
+++ b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go
@@ -17,7 +17,7 @@ import (
"time"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
- wangsucdn "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdn"
+ wangsucdn "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdnpro"
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
)
@@ -88,9 +88,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
// 查询已部署加速域名的详情
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))
+ d.logger.Debug("sdk request 'cdnpro.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)
+ return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.GetHostnameDetail': %w", err)
}
// 生成网宿云证书参数
@@ -126,9 +126,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
NewVersion: certificateNewVersionInfo,
}
createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq)
- d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
+ d.logger.Debug("sdk request 'cdnpro.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)
+ return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.CreateCertificate': %w", err)
}
wangsuCertUrl = createCertificateResp.CertificateUrl
@@ -149,9 +149,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
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))
+ d.logger.Debug("sdk request 'cdnpro.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)
+ return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.UpdateCertificate': %w", err)
}
wangsuCertUrl = updateCertificateResp.CertificateUrl
@@ -186,9 +186,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
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))
+ d.logger.Debug("sdk request 'cdnpro.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)
+ return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.CreateDeploymentTask': %w", err)
}
// 循环获取部署任务详细信息,等待任务状态变更
@@ -206,9 +206,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
}
getDeploymentTaskDetailResp, err := d.sdkClient.GetDeploymentTaskDetail(wangsuTaskId)
- d.logger.Info("sdk request 'cdn.GetDeploymentTaskDetail'", slog.Any("taskId", wangsuTaskId), slog.Any("response", getDeploymentTaskDetailResp))
+ d.logger.Info("sdk request 'cdnpro.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)
+ return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.GetDeploymentTaskDetail': %w", err)
}
if getDeploymentTaskDetailResp.Status == "failed" {
diff --git a/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate.go b/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate.go
new file mode 100644
index 00000000..3f691489
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate.go
@@ -0,0 +1,109 @@
+package wangsucertificate
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "log/slog"
+ "time"
+
+ "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/wangsu-certificate"
+ wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/certificate"
+ typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
+)
+
+type DeployerConfig struct {
+ // 网宿云 AccessKeyId。
+ AccessKeyId string `json:"accessKeyId"`
+ // 网宿云 AccessKeySecret。
+ AccessKeySecret string `json:"accessKeySecret"`
+ // 证书 ID。
+ // 选填。零值时表示新建证书;否则表示更新证书。
+ CertificateId string `json:"certificateId,omitempty"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient *wangsusdk.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)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create sdk client: %w", err)
+ }
+
+ uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
+ AccessKeyId: config.AccessKeyId,
+ AccessKeySecret: config.AccessKeySecret,
+ })
+ 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
+ }
+ return d
+}
+
+func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
+ if d.config.CertificateId == "" {
+ // 上传证书到证书管理
+ 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 {
+ // 修改证书
+ // REF: https://www.wangsu.com/document/api-doc/25568?productCode=certificatemanagement
+ updateCertificateReq := &wangsusdk.UpdateCertificateRequest{
+ Name: typeutil.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
+ Certificate: typeutil.ToPtr(certPEM),
+ PrivateKey: typeutil.ToPtr(privkeyPEM),
+ Comment: typeutil.ToPtr("upload from certimate"),
+ }
+ updateCertificateResp, err := d.sdkClient.UpdateCertificate(d.config.CertificateId, updateCertificateReq)
+ d.logger.Debug("sdk request 'certificatemanagement.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.CreateCertificate': %w", err)
+ }
+ }
+
+ return &deployer.DeployResult{}, nil
+}
+
+func createSdkClient(accessKeyId, accessKeySecret string) (*wangsusdk.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 wangsusdk.NewClient(accessKeyId, accessKeySecret), nil
+}
diff --git a/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate_test.go b/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate_test.go
new file mode 100644
index 00000000..a6805ec9
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate_test.go
@@ -0,0 +1,75 @@
+package wangsucertificate_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-certificate"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fAccessKeyId string
+ fAccessKeySecret string
+ fCertificateId string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
+ flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
+ flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./wangsu_certificate_test.go -args \
+ --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYID="your-access-key-id" \
+ --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYSECRET="your-access-key-secret" \
+ --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_CERTIFICATEID="your-certificate-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("CERTIFICATEID: %v", fCertificateId),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ AccessKeyId: fAccessKeyId,
+ AccessKeySecret: fAccessKeySecret,
+ CertificateId: 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/webhook/webhook.go b/internal/pkg/core/deployer/providers/webhook/webhook.go
index 418b2c1a..49b07b47 100644
--- a/internal/pkg/core/deployer/providers/webhook/webhook.go
+++ b/internal/pkg/core/deployer/providers/webhook/webhook.go
@@ -159,9 +159,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
// 生成请求
// 其中 GET 请求需转换为查询参数
- req := d.httpClient.R().
- SetContext(ctx).
- SetHeaderMultiValues(webhookHeaders)
+ req := d.httpClient.R().SetHeaderMultiValues(webhookHeaders)
req.URL = webhookUrl.String()
req.Method = webhookMethod
if webhookMethod == http.MethodGet {
@@ -178,7 +176,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
}
// 发送请求
- resp, err := req.SetDebug(true).Send()
+ resp, err := req.Send()
if err != nil {
return nil, fmt.Errorf("failed to send webhook request: %w", err)
} else if resp.IsError() {
diff --git a/internal/pkg/core/notifier/providers/bark/bark.go b/internal/pkg/core/notifier/providers/bark/bark.go
index ccdd5736..97ece0be 100644
--- a/internal/pkg/core/notifier/providers/bark/bark.go
+++ b/internal/pkg/core/notifier/providers/bark/bark.go
@@ -2,10 +2,10 @@ package bark
import (
"context"
+ "fmt"
"log/slog"
- "github.com/nikoksr/notify"
- "github.com/nikoksr/notify/service/bark"
+ "github.com/go-resty/resty/v2"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
@@ -19,8 +19,9 @@ type NotifierConfig struct {
}
type NotifierProvider struct {
- config *NotifierConfig
- logger *slog.Logger
+ config *NotifierConfig
+ logger *slog.Logger
+ httpClient *resty.Client
}
var _ notifier.Notifier = (*NotifierProvider)(nil)
@@ -30,9 +31,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
panic("config is nil")
}
+ client := resty.New()
+
return &NotifierProvider{
- config: config,
- logger: slog.Default(),
+ config: config,
+ logger: slog.Default(),
+ httpClient: client,
}, nil
}
@@ -46,16 +50,25 @@ 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) {
- var srv notify.Notifier
- if n.config.ServerUrl == "" {
- srv = bark.New(n.config.DeviceKey)
- } else {
- srv = bark.NewWithServers(n.config.DeviceKey, n.config.ServerUrl)
+ const defaultServerURL = "https://api.day.app/"
+ serverUrl := defaultServerURL
+ if n.config.ServerUrl != "" {
+ serverUrl = n.config.ServerUrl
}
- err = srv.Send(ctx, subject, message)
+ // REF: https://bark.day.app/#/tutorial
+ req := n.httpClient.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(map[string]any{
+ "title": subject,
+ "body": message,
+ "device_key": n.config.DeviceKey,
+ })
+ resp, err := req.Post(serverUrl)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("bark api error: failed to send request: %w", err)
+ } else if resp.IsError() {
+ return nil, fmt.Errorf("bark api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return ¬ifier.NotifyResult{}, nil
diff --git a/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go b/internal/pkg/core/notifier/providers/dingtalkbot/dingtalkbot.go
similarity index 67%
rename from internal/pkg/core/notifier/providers/dingtalk/dingtalk.go
rename to internal/pkg/core/notifier/providers/dingtalkbot/dingtalkbot.go
index 9eb94dcf..d6d8b096 100644
--- a/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go
+++ b/internal/pkg/core/notifier/providers/dingtalkbot/dingtalkbot.go
@@ -1,4 +1,4 @@
-package dingtalk
+package dingtalkbot
import (
"context"
@@ -6,7 +6,7 @@ import (
"log/slog"
"net/url"
- "github.com/nikoksr/notify/service/dingding"
+ "github.com/blinkbean/dingtalk"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
@@ -48,17 +48,18 @@ 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)
+ return nil, fmt.Errorf("dingtalk api error: invalid webhook url: %w", err)
}
- srv := dingding.New(&dingding.Config{
- Token: webhookUrl.Query().Get("access_token"),
- Secret: n.config.Secret,
- })
+ var bot *dingtalk.DingTalk
+ if n.config.Secret == "" {
+ bot = dingtalk.InitDingTalk([]string{webhookUrl.Query().Get("access_token")}, "")
+ } else {
+ bot = dingtalk.InitDingTalkWithSecret(webhookUrl.Query().Get("access_token"), n.config.Secret)
+ }
- err = srv.Send(ctx, subject, message)
- if err != nil {
- return nil, err
+ if err := bot.SendTextMessage(subject + "\n" + message); err != nil {
+ return nil, fmt.Errorf("dingtalk api error: %w", err)
}
return ¬ifier.NotifyResult{}, nil
diff --git a/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go b/internal/pkg/core/notifier/providers/dingtalkbot/dingtalkbot_test.go
similarity index 62%
rename from internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go
rename to internal/pkg/core/notifier/providers/dingtalkbot/dingtalkbot_test.go
index 086e3a94..de3b6ba0 100644
--- a/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go
+++ b/internal/pkg/core/notifier/providers/dingtalkbot/dingtalkbot_test.go
@@ -1,4 +1,4 @@
-package dingtalk_test
+package dingtalkbot_test
import (
"context"
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
- provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
+ provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
)
const (
@@ -16,22 +16,23 @@ const (
)
var (
- fAccessToken string
- fSecret string
+ fWebhookUrl string
+ fSecret string
)
func init() {
- argsPrefix := "CERTIMATE_NOTIFIER_DINGTALK_"
+ argsPrefix := "CERTIMATE_NOTIFIER_DINGTALKBOT_"
- flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "")
+ flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
flag.StringVar(&fSecret, argsPrefix+"SECRET", "", "")
}
/*
Shell command to run this test:
- go test -v ./dingtalk_test.go -args \
- --CERTIMATE_NOTIFIER_DINGTALK_URL="https://example.com/your-webhook-url"
+ go test -v ./dingtalkbot_test.go -args \
+ --CERTIMATE_NOTIFIER_DINGTALKBOT_WEBHOOKURL="https://example.com/your-webhook-url" \
+ --CERTIMATE_NOTIFIER_DINGTALKBOT_SECRET="your-secret"
*/
func TestNotify(t *testing.T) {
flag.Parse()
@@ -39,13 +40,13 @@ func TestNotify(t *testing.T) {
t.Run("Notify", func(t *testing.T) {
t.Log(strings.Join([]string{
"args:",
- fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken),
+ fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl),
fmt.Sprintf("SECRET: %v", fSecret),
}, "\n"))
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
- AccessToken: fAccessToken,
- Secret: fSecret,
+ WebhookUrl: fWebhookUrl,
+ Secret: fSecret,
})
if err != nil {
t.Errorf("err: %+v", err)
diff --git a/internal/pkg/core/notifier/providers/email/email_test.go b/internal/pkg/core/notifier/providers/email/email_test.go
index 30bfba07..cf0669ca 100644
--- a/internal/pkg/core/notifier/providers/email/email_test.go
+++ b/internal/pkg/core/notifier/providers/email/email_test.go
@@ -17,7 +17,7 @@ const (
var (
fSmtpHost string
- fSmtpPort int
+ fSmtpPort int64
fSmtpTLS bool
fUsername string
fPassword string
@@ -29,7 +29,7 @@ func init() {
argsPrefix := "CERTIMATE_NOTIFIER_EMAIL_"
flag.StringVar(&fSmtpHost, argsPrefix+"SMTPHOST", "", "")
- flag.IntVar(&fSmtpPort, argsPrefix+"SMTPPORT", 0, "")
+ flag.Int64Var(&fSmtpPort, argsPrefix+"SMTPPORT", 0, "")
flag.BoolVar(&fSmtpTLS, argsPrefix+"SMTPTLS", false, "")
flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "")
flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "")
diff --git a/internal/pkg/core/notifier/providers/gotify/gotify.go b/internal/pkg/core/notifier/providers/gotify/gotify.go
index aed6e7c8..81dcb8ad 100644
--- a/internal/pkg/core/notifier/providers/gotify/gotify.go
+++ b/internal/pkg/core/notifier/providers/gotify/gotify.go
@@ -1,20 +1,19 @@
package gotify
import (
- "bytes"
"context"
- "encoding/json"
"fmt"
- "io"
"log/slog"
- "net/http"
+ "strings"
+
+ "github.com/go-resty/resty/v2"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
type NotifierConfig struct {
// Gotify 服务地址。
- Url string `json:"url"`
+ ServerUrl string `json:"serverUrl"`
// Gotify Token。
Token string `json:"token"`
// Gotify 消息优先级。
@@ -24,7 +23,7 @@ type NotifierConfig struct {
type NotifierProvider struct {
config *NotifierConfig
logger *slog.Logger
- httpClient *http.Client
+ httpClient *resty.Client
}
var _ notifier.Notifier = (*NotifierProvider)(nil)
@@ -34,10 +33,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
panic("config is nil")
}
+ client := resty.New()
+
return &NotifierProvider{
config: config,
logger: slog.Default(),
- httpClient: http.DefaultClient,
+ httpClient: client,
}, nil
}
@@ -51,45 +52,22 @@ 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) {
- reqBody := &struct {
- Title string `json:"title"`
- Message string `json:"message"`
- Priority int64 `json:"priority"`
- }{
- Title: subject,
- Message: message,
- Priority: n.config.Priority,
- }
+ serverUrl := strings.TrimRight(n.config.ServerUrl, "/")
- 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)
+ // REF: https://gotify.net/api-docs#/message/createMessage
+ req := n.httpClient.R().
+ SetHeader("Content-Type", "application/json").
+ SetHeader("Authorization", "Bearer "+n.config.Token).
+ SetBody(map[string]any{
+ "title": subject,
+ "message": message,
+ "priority": n.config.Priority,
+ })
+ resp, err := req.Post(fmt.Sprintf("%s/message", serverUrl))
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))
+ } else if resp.IsError() {
+ return nil, fmt.Errorf("gotify api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
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
index 31ad64af..eb0ffd6b 100644
--- a/internal/pkg/core/notifier/providers/gotify/gotify_test.go
+++ b/internal/pkg/core/notifier/providers/gotify/gotify_test.go
@@ -48,9 +48,9 @@ func TestNotify(t *testing.T) {
}, "\n"))
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
- Url: fUrl,
- Token: fToken,
- Priority: fPriority,
+ ServerUrl: fUrl,
+ Token: fToken,
+ Priority: fPriority,
})
if err != nil {
t.Errorf("err: %+v", err)
diff --git a/internal/pkg/core/notifier/providers/lark/lark.go b/internal/pkg/core/notifier/providers/larkbot/larkbot.go
similarity index 65%
rename from internal/pkg/core/notifier/providers/lark/lark.go
rename to internal/pkg/core/notifier/providers/larkbot/larkbot.go
index e8ad7816..7d3e8a55 100644
--- a/internal/pkg/core/notifier/providers/lark/lark.go
+++ b/internal/pkg/core/notifier/providers/larkbot/larkbot.go
@@ -1,10 +1,11 @@
-package lark
+package larkbot
import (
"context"
+ "fmt"
"log/slog"
- "github.com/nikoksr/notify/service/lark"
+ "github.com/go-lark/lark"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
@@ -42,11 +43,17 @@ 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 := lark.NewWebhookService(n.config.WebhookUrl)
-
- err = srv.Send(ctx, subject, message)
+ bot := lark.NewNotificationBot(n.config.WebhookUrl)
+ content := lark.NewPostBuilder().
+ Title(subject).
+ TextTag(message, 1, false).
+ Render()
+ msg := lark.NewMsgBuffer(lark.MsgPost).Post(content)
+ resp, err := bot.PostNotificationV2(msg.Build())
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("lark api error: %w", err)
+ } else if resp.Code != 0 {
+ return nil, fmt.Errorf("lark api error: code='%d', message='%s'", resp.Code, resp.Msg)
}
return ¬ifier.NotifyResult{}, nil
diff --git a/internal/pkg/core/notifier/providers/wecom/wecom_test.go b/internal/pkg/core/notifier/providers/larkbot/larkbot_test.go
similarity index 80%
rename from internal/pkg/core/notifier/providers/wecom/wecom_test.go
rename to internal/pkg/core/notifier/providers/larkbot/larkbot_test.go
index 01646121..2deba768 100644
--- a/internal/pkg/core/notifier/providers/wecom/wecom_test.go
+++ b/internal/pkg/core/notifier/providers/larkbot/larkbot_test.go
@@ -1,4 +1,4 @@
-package serverchan_test
+package larkbot_test
import (
"context"
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
- provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
+ provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
)
const (
@@ -18,7 +18,7 @@ const (
var fWebhookUrl string
func init() {
- argsPrefix := "CERTIMATE_NOTIFIER_WECOM_"
+ argsPrefix := "CERTIMATE_NOTIFIER_LARKBOT_"
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
}
@@ -26,8 +26,8 @@ func init() {
/*
Shell command to run this test:
- go test -v ./wecom_test.go -args \
- --CERTIMATE_NOTIFIER_WECOM_WEBHOOKURL="https://example.com/your-webhook-url" \
+ go test -v ./larkbot_test.go -args \
+ --CERTIMATE_NOTIFIER_LARKBOT_WEBHOOKURL="https://example.com/your-webhook-url"
*/
func TestNotify(t *testing.T) {
flag.Parse()
diff --git a/internal/pkg/core/notifier/providers/mattermost/mattermost.go b/internal/pkg/core/notifier/providers/mattermost/mattermost.go
index ed3a507a..a9b2f4d6 100644
--- a/internal/pkg/core/notifier/providers/mattermost/mattermost.go
+++ b/internal/pkg/core/notifier/providers/mattermost/mattermost.go
@@ -1,15 +1,13 @@
package mattermost
import (
- "bytes"
"context"
- "encoding/json"
- "io"
+ "fmt"
"log/slog"
- "net/http"
"strings"
- "github.com/nikoksr/notify/service/mattermost"
+ "github.com/go-resty/resty/v2"
+
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
@@ -25,8 +23,9 @@ type NotifierConfig struct {
}
type NotifierProvider struct {
- config *NotifierConfig
- logger *slog.Logger
+ config *NotifierConfig
+ logger *slog.Logger
+ httpClient *resty.Client
}
var _ notifier.Notifier = (*NotifierProvider)(nil)
@@ -36,9 +35,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
panic("config is nil")
}
+ client := resty.New()
+
return &NotifierProvider{
- config: config,
- logger: slog.Default(),
+ config: config,
+ logger: slog.Default(),
+ httpClient: client,
}, nil
}
@@ -52,17 +54,29 @@ 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 := mattermost.New(strings.TrimRight(n.config.ServerUrl, "/"))
+ serverUrl := strings.TrimRight(n.config.ServerUrl, "/")
- if err := srv.LoginWithCredentials(ctx, n.config.Username, n.config.Password); err != nil {
- return nil, err
+ // REF: https://developers.mattermost.com/api-documentation/#/operations/Login
+ loginReq := n.httpClient.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(map[string]any{
+ "login_id": n.config.Username,
+ "password": n.config.Password,
+ })
+ loginResp, err := loginReq.Post(fmt.Sprintf("%s/api/v4/users/login", serverUrl))
+ if err != nil {
+ return nil, fmt.Errorf("mattermost api error: failed to send request: %w", err)
+ } else if loginResp.IsError() {
+ return nil, fmt.Errorf("mattermost api error: unexpected status code: %d, resp: %s", loginResp.StatusCode(), loginResp.String())
+ } else if loginResp.Header().Get("Token") == "" {
+ return nil, fmt.Errorf("mattermost api error: received empty login token")
}
- srv.AddReceivers(n.config.ChannelId)
-
- // 复写消息样式
- srv.PreSend(func(req *http.Request) error {
- m := map[string]interface{}{
+ // REF: https://developers.mattermost.com/api-documentation/#/operations/CreatePost
+ postReq := n.httpClient.R().
+ SetHeader("Content-Type", "application/json").
+ SetHeader("Authorization", "Bearer "+loginResp.Header().Get("Token")).
+ SetBody(map[string]any{
"channel_id": n.config.ChannelId,
"props": map[string]interface{}{
"attachments": []map[string]interface{}{
@@ -72,20 +86,12 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
},
},
},
- }
-
- 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
+ })
+ postResp, err := postReq.Post(fmt.Sprintf("%s/api/v4/posts", serverUrl))
+ if err != nil {
+ return nil, fmt.Errorf("mattermost api error: failed to send request: %w", err)
+ } else if postResp.IsError() {
+ return nil, fmt.Errorf("mattermost api error: unexpected status code: %d, resp: %s", postResp.StatusCode(), postResp.String())
}
return ¬ifier.NotifyResult{}, nil
diff --git a/internal/pkg/core/notifier/providers/pushover/pushover.go b/internal/pkg/core/notifier/providers/pushover/pushover.go
index f306df1f..b7f74bba 100644
--- a/internal/pkg/core/notifier/providers/pushover/pushover.go
+++ b/internal/pkg/core/notifier/providers/pushover/pushover.go
@@ -1,13 +1,11 @@
package pushover
import (
- "bytes"
"context"
- "encoding/json"
"fmt"
- "io"
"log/slog"
- "net/http"
+
+ "github.com/go-resty/resty/v2"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
@@ -22,7 +20,7 @@ type NotifierConfig struct {
type NotifierProvider struct {
config *NotifierConfig
logger *slog.Logger
- httpClient *http.Client
+ httpClient *resty.Client
}
var _ notifier.Notifier = (*NotifierProvider)(nil)
@@ -32,10 +30,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
panic("config is nil")
}
+ client := resty.New()
+
return &NotifierProvider{
config: config,
logger: slog.Default(),
- httpClient: http.DefaultClient,
+ httpClient: client,
}, nil
}
@@ -50,46 +50,19 @@ 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) {
// 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)
+ req := n.httpClient.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(map[string]any{
+ "title": subject,
+ "message": message,
+ "token": n.config.Token,
+ "user": n.config.User,
+ })
+ resp, err := req.Post("https://api.pushover.net/1/messages.json")
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))
+ } else if resp.IsError() {
+ return nil, fmt.Errorf("pushover api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return ¬ifier.NotifyResult{}, nil
diff --git a/internal/pkg/core/notifier/providers/pushplus/pushplus.go b/internal/pkg/core/notifier/providers/pushplus/pushplus.go
index a0ef4c7f..834f9683 100644
--- a/internal/pkg/core/notifier/providers/pushplus/pushplus.go
+++ b/internal/pkg/core/notifier/providers/pushplus/pushplus.go
@@ -1,13 +1,12 @@
package pushplus
import (
- "bytes"
"context"
"encoding/json"
"fmt"
- "io"
"log/slog"
- "net/http"
+
+ "github.com/go-resty/resty/v2"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
@@ -20,7 +19,7 @@ type NotifierConfig struct {
type NotifierProvider struct {
config *NotifierConfig
logger *slog.Logger
- httpClient *http.Client
+ httpClient *resty.Client
}
var _ notifier.Notifier = (*NotifierProvider)(nil)
@@ -30,10 +29,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
panic("config is nil")
}
+ client := resty.New()
+
return &NotifierProvider{
config: config,
logger: slog.Default(),
- httpClient: http.DefaultClient,
+ httpClient: client,
}, nil
}
@@ -47,55 +48,29 @@ 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) {
- // 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)
+ // REF: https://pushplus.plus/doc/guide/api.html#%E4%B8%80%E3%80%81%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF%E6%8E%A5%E5%8F%A3
+ req := n.httpClient.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(map[string]any{
+ "title": subject,
+ "content": message,
+ "token": n.config.Token,
+ })
+ resp, err := req.Post("https://www.pushplus.plus/send")
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))
+ } else if resp.IsError() {
+ return nil, fmt.Errorf("pushplus api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
var errorResponse struct {
- Code int `json:"code"`
- Msg string `json:"msg"`
+ Code int `json:"code"`
+ Message string `json:"msg"`
}
- if err := json.Unmarshal(result, &errorResponse); err != nil {
- return nil, fmt.Errorf("pushplus api error: failed to decode response: %w", err)
+ if err := json.Unmarshal(resp.Body(), &errorResponse); err != nil {
+ return nil, fmt.Errorf("pushplus api error: failed to unmarshal 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 nil, fmt.Errorf("pushplus api error: code='%d', message='%s'", errorResponse.Code, errorResponse.Message)
}
return ¬ifier.NotifyResult{}, nil
diff --git a/internal/pkg/core/notifier/providers/serverchan/serverchan.go b/internal/pkg/core/notifier/providers/serverchan/serverchan.go
index 89724b08..d74b2fcc 100644
--- a/internal/pkg/core/notifier/providers/serverchan/serverchan.go
+++ b/internal/pkg/core/notifier/providers/serverchan/serverchan.go
@@ -2,22 +2,23 @@ package serverchan
import (
"context"
+ "fmt"
"log/slog"
- "net/http"
- notifyHttp "github.com/nikoksr/notify/service/http"
+ "github.com/go-resty/resty/v2"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
type NotifierConfig struct {
// ServerChan 服务地址。
- Url string `json:"url"`
+ ServerUrl string `json:"serverUrl"`
}
type NotifierProvider struct {
- config *NotifierConfig
- logger *slog.Logger
+ config *NotifierConfig
+ logger *slog.Logger
+ httpClient *resty.Client
}
var _ notifier.Notifier = (*NotifierProvider)(nil)
@@ -27,9 +28,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
panic("config is nil")
}
+ client := resty.New()
+
return &NotifierProvider{
- config: config,
- logger: slog.Default(),
+ config: config,
+ logger: slog.Default(),
+ httpClient: client,
}, nil
}
@@ -43,24 +47,18 @@ 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 := notifyHttp.New()
-
- srv.AddReceivers(¬ifyHttp.Webhook{
- URL: n.config.Url,
- Header: http.Header{},
- ContentType: "application/json",
- Method: http.MethodPost,
- BuildPayload: func(subject, message string) (payload any) {
- return map[string]string{
- "text": subject,
- "desp": message,
- }
- },
- })
-
- err = srv.Send(ctx, subject, message)
+ // REF: https://sct.ftqq.com/
+ req := n.httpClient.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(map[string]any{
+ "text": subject,
+ "desp": message,
+ })
+ resp, err := req.Post(n.config.ServerUrl)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("serverchan api error: failed to send request: %w", err)
+ } else if resp.IsError() {
+ return nil, fmt.Errorf("serverchan api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return ¬ifier.NotifyResult{}, nil
diff --git a/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go b/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go
index 991b4050..5684a593 100644
--- a/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go
+++ b/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go
@@ -39,7 +39,7 @@ func TestNotify(t *testing.T) {
}, "\n"))
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
- Url: fUrl,
+ ServerUrl: fUrl,
})
if err != nil {
t.Errorf("err: %+v", err)
diff --git a/internal/pkg/core/notifier/providers/telegram/telegram.go b/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go
similarity index 51%
rename from internal/pkg/core/notifier/providers/telegram/telegram.go
rename to internal/pkg/core/notifier/providers/telegrambot/telegrambot.go
index 218f7ee3..99b86a38 100644
--- a/internal/pkg/core/notifier/providers/telegram/telegram.go
+++ b/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go
@@ -1,10 +1,11 @@
-package telegram
+package telegrambot
import (
"context"
+ "fmt"
"log/slog"
- "github.com/nikoksr/notify/service/telegram"
+ "github.com/go-resty/resty/v2"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
@@ -17,8 +18,9 @@ type NotifierConfig struct {
}
type NotifierProvider struct {
- config *NotifierConfig
- logger *slog.Logger
+ config *NotifierConfig
+ logger *slog.Logger
+ httpClient *resty.Client
}
var _ notifier.Notifier = (*NotifierProvider)(nil)
@@ -28,9 +30,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
panic("config is nil")
}
+ client := resty.New()
+
return &NotifierProvider{
- config: config,
- logger: slog.Default(),
+ config: config,
+ logger: slog.Default(),
+ httpClient: client,
}, nil
}
@@ -44,16 +49,18 @@ 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.BotToken)
+ // REF: https://core.telegram.org/bots/api#sendmessage
+ req := n.httpClient.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(map[string]any{
+ "chat_id": n.config.ChatId,
+ "text": subject + "\n" + message,
+ })
+ resp, err := req.Post(fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", n.config.BotToken))
if err != nil {
- return nil, err
- }
-
- srv.AddReceivers(n.config.ChatId)
-
- err = srv.Send(ctx, subject, message)
- if err != nil {
- return nil, err
+ return nil, fmt.Errorf("telegram api error: failed to send request: %w", err)
+ } else if resp.IsError() {
+ return nil, fmt.Errorf("telegram api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return ¬ifier.NotifyResult{}, nil
diff --git a/internal/pkg/core/notifier/providers/telegram/telegram_test.go b/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go
similarity index 79%
rename from internal/pkg/core/notifier/providers/telegram/telegram_test.go
rename to internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go
index e9a7d10b..3a207384 100644
--- a/internal/pkg/core/notifier/providers/telegram/telegram_test.go
+++ b/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go
@@ -1,4 +1,4 @@
-package telegram_test
+package telegrambot_test
import (
"context"
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
- provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
+ provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
)
const (
@@ -21,7 +21,7 @@ var (
)
func init() {
- argsPrefix := "CERTIMATE_NOTIFIER_TELEGRAM_"
+ argsPrefix := "CERTIMATE_NOTIFIER_TELEGRAMBOT_"
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
flag.Int64Var(&fChartId, argsPrefix+"CHATID", 0, "")
@@ -30,9 +30,9 @@ func init() {
/*
Shell command to run this test:
- go test -v ./telegram_test.go -args \
- --CERTIMATE_NOTIFIER_TELEGRAM_APITOKEN="your-api-token" \
- --CERTIMATE_NOTIFIER_TELEGRAM_CHATID=123456
+ go test -v ./telegrambot_test.go -args \
+ --CERTIMATE_NOTIFIER_TELEGRAMBOT_APITOKEN="your-api-token" \
+ --CERTIMATE_NOTIFIER_TELEGRAMBOT_CHATID=123456
*/
func TestNotify(t *testing.T) {
flag.Parse()
diff --git a/internal/pkg/core/notifier/providers/webhook/webhook.go b/internal/pkg/core/notifier/providers/webhook/webhook.go
index 0e7caaa5..5f62f170 100644
--- a/internal/pkg/core/notifier/providers/webhook/webhook.go
+++ b/internal/pkg/core/notifier/providers/webhook/webhook.go
@@ -139,9 +139,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
// 生成请求
// 其中 GET 请求需转换为查询参数
- req := n.httpClient.R().
- SetContext(ctx).
- SetHeaderMultiValues(webhookHeaders)
+ req := n.httpClient.R().SetHeaderMultiValues(webhookHeaders)
req.URL = webhookUrl.String()
req.Method = webhookMethod
if webhookMethod == http.MethodGet {
@@ -160,12 +158,12 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
// 发送请求
resp, err := req.Send()
if err != nil {
- return nil, fmt.Errorf("failed to send webhook request: %w", err)
+ return nil, fmt.Errorf("webhook error: failed to send request: %w", err)
} else if resp.IsError() {
- return nil, fmt.Errorf("unexpected webhook response status code: %d", resp.StatusCode())
+ return nil, fmt.Errorf("webhook error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
- n.logger.Debug("webhook responded", slog.Any("response", resp.String()))
+ n.logger.Debug("webhook responded", slog.String("response", resp.String()))
return ¬ifier.NotifyResult{}, nil
}
diff --git a/internal/pkg/core/notifier/providers/wecom/wecom.go b/internal/pkg/core/notifier/providers/wecombot/wecombot.go
similarity index 51%
rename from internal/pkg/core/notifier/providers/wecom/wecom.go
rename to internal/pkg/core/notifier/providers/wecombot/wecombot.go
index 413f0d8d..36c179d4 100644
--- a/internal/pkg/core/notifier/providers/wecom/wecom.go
+++ b/internal/pkg/core/notifier/providers/wecombot/wecombot.go
@@ -1,11 +1,11 @@
-package serverchan
+package wecombot
import (
"context"
+ "fmt"
"log/slog"
- "net/http"
- notifyHttp "github.com/nikoksr/notify/service/http"
+ "github.com/go-resty/resty/v2"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
@@ -16,8 +16,9 @@ type NotifierConfig struct {
}
type NotifierProvider struct {
- config *NotifierConfig
- logger *slog.Logger
+ config *NotifierConfig
+ logger *slog.Logger
+ httpClient *resty.Client
}
var _ notifier.Notifier = (*NotifierProvider)(nil)
@@ -27,8 +28,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
panic("config is nil")
}
+ client := resty.New()
+
return &NotifierProvider{
- config: config,
+ config: config,
+ logger: slog.Default(),
+ httpClient: client,
}, nil
}
@@ -42,26 +47,20 @@ 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 := notifyHttp.New()
-
- srv.AddReceivers(¬ifyHttp.Webhook{
- URL: n.config.WebhookUrl,
- Header: http.Header{},
- ContentType: "application/json",
- Method: http.MethodPost,
- BuildPayload: func(subject, message string) (payload any) {
- return map[string]any{
- "msgtype": "text",
- "text": map[string]string{
- "content": subject + "\n\n" + message,
- },
- }
- },
- })
-
- err = srv.Send(ctx, subject, message)
+ // REF: https://developer.work.weixin.qq.com/document/path/91770
+ req := n.httpClient.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(map[string]any{
+ "msgtype": "text",
+ "text": map[string]string{
+ "content": subject + "\n\n" + message,
+ },
+ })
+ resp, err := req.Post(n.config.WebhookUrl)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("wecom api error: failed to send request: %w", err)
+ } else if resp.IsError() {
+ return nil, fmt.Errorf("wecom api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return ¬ifier.NotifyResult{}, nil
diff --git a/internal/pkg/core/notifier/providers/lark/lark_test.go b/internal/pkg/core/notifier/providers/wecombot/wecombot_test.go
similarity index 80%
rename from internal/pkg/core/notifier/providers/lark/lark_test.go
rename to internal/pkg/core/notifier/providers/wecombot/wecombot_test.go
index f72ca443..261f2158 100644
--- a/internal/pkg/core/notifier/providers/lark/lark_test.go
+++ b/internal/pkg/core/notifier/providers/wecombot/wecombot_test.go
@@ -1,4 +1,4 @@
-package lark_test
+package wecombot_test
import (
"context"
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
- provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
+ provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
)
const (
@@ -18,7 +18,7 @@ const (
var fWebhookUrl string
func init() {
- argsPrefix := "CERTIMATE_NOTIFIER_LARK_"
+ argsPrefix := "CERTIMATE_NOTIFIER_WECOMBOT_"
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
}
@@ -26,8 +26,8 @@ func init() {
/*
Shell command to run this test:
- go test -v ./lark_test.go -args \
- --CERTIMATE_NOTIFIER_LARK_WEBHOOKURL="https://example.com/your-webhook-url"
+ go test -v ./wecombot_test.go -args \
+ --CERTIMATE_NOTIFIER_WECOMBOT_WEBHOOKURL="https://example.com/your-webhook-url" \
*/
func TestNotify(t *testing.T) {
flag.Parse()
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 e5a0b0ba..63900125 100644
--- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go
+++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go
@@ -10,12 +10,14 @@ import (
"time"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
- opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
+ onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
)
type UploaderConfig struct {
// 1Panel 地址。
ApiUrl string `json:"apiUrl"`
+ // 1Panel 版本。
+ ApiVersion string `json:"apiVersion"`
// 1Panel 接口密钥。
ApiKey string `json:"apiKey"`
}
@@ -23,7 +25,7 @@ type UploaderConfig struct {
type UploaderProvider struct {
config *UploaderConfig
logger *slog.Logger
- sdkClient *opsdk.Client
+ sdkClient *onepanelsdk.Client
}
var _ uploader.Uploader = (*UploaderProvider)(nil)
@@ -33,7 +35,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) {
panic("config is nil")
}
- client, err := createSdkClient(config.ApiUrl, config.ApiKey)
+ client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey)
if err != nil {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
@@ -67,7 +69,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
// 上传证书
- uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{
+ uploadWebsiteSSLReq := &onepanelsdk.UploadWebsiteSSLRequest{
Type: "paste",
Description: certName,
Certificate: certPEM,
@@ -99,7 +101,7 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string,
default:
}
- searchWebsiteSSLReq := &opsdk.SearchWebsiteSSLRequest{
+ searchWebsiteSSLReq := &onepanelsdk.SearchWebsiteSSLRequest{
Page: searchWebsiteSSLPageNumber,
PageSize: searchWebsiteSSLPageSize,
}
@@ -130,15 +132,19 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string,
return nil, nil
}
-func createSdkClient(apiUrl, apiKey string) (*opsdk.Client, error) {
+func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid 1panel api url")
}
+ if apiVersion == "" {
+ return nil, errors.New("invalid 1panel api version")
+ }
+
if apiKey == "" {
return nil, errors.New("invalid 1panel api key")
}
- client := opsdk.NewClient(apiUrl, apiKey)
+ client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
return client, nil
}
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 257030f5..cfb250be 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
@@ -16,6 +16,7 @@ var (
fInputCertPath string
fInputKeyPath string
fApiUrl string
+ fApiVersion string
fApiKey string
)
@@ -25,6 +26,7 @@ func init() {
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
+ flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
}
@@ -35,6 +37,7 @@ Shell command to run this test:
--CERTIMATE_UPLOADER_1PANELSSL_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_UPLOADER_1PANELSSL_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_UPLOADER_1PANELSSL_APIURL="http://127.0.0.1:20410" \
+ --CERTIMATE_UPLOADER_1PANELSSL_APIVERSION="v1" \
--CERTIMATE_UPLOADER_1PANELSSL_APIKEY="your-api-key"
*/
func TestDeploy(t *testing.T) {
@@ -46,12 +49,14 @@ func TestDeploy(t *testing.T) {
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("APIURL: %v", fApiUrl),
+ fmt.Sprintf("APIVERSION: %v", fApiVersion),
fmt.Sprintf("APIKEY: %v", fApiKey),
}, "\n"))
uploader, err := provider.NewUploader(&provider.UploaderConfig{
- ApiUrl: fApiUrl,
- ApiKey: fApiKey,
+ ApiUrl: fApiUrl,
+ ApiVersion: fApiVersion,
+ ApiKey: fApiKey,
})
if err != nil {
t.Errorf("err: %+v", err)
diff --git a/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go
new file mode 100644
index 00000000..b512be09
--- /dev/null
+++ b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go
@@ -0,0 +1,143 @@
+package jdcloudssl
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "log/slog"
+ "regexp"
+ "strings"
+ "time"
+
+ wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/certificate"
+
+ "github.com/usual2970/certimate/internal/pkg/core/uploader"
+ certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
+ typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
+)
+
+type UploaderConfig struct {
+ // 网宿云 AccessKeyId。
+ AccessKeyId string `json:"accessKeyId"`
+ // 网宿云 AccessKeySecret。
+ AccessKeySecret string `json:"accessKeySecret"`
+}
+
+type UploaderProvider struct {
+ config *UploaderConfig
+ logger *slog.Logger
+ sdkClient *wangsusdk.Client
+}
+
+var _ uploader.Uploader = (*UploaderProvider)(nil)
+
+func NewUploader(config *UploaderConfig) (*UploaderProvider, 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 &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) {
+ // 解析证书内容
+ certX509, err := certutil.ParseCertificateFromPEM(certPEM)
+ if err != nil {
+ return nil, err
+ }
+
+ // 查询证书列表,避免重复上传
+ // REF: https://www.wangsu.com/document/api-doc/26426
+ listCertificatesResp, err := u.sdkClient.ListCertificates()
+ u.logger.Debug("sdk request 'certificatemanagement.ListCertificates'", slog.Any("response", listCertificatesResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.ListCertificates': %w", err)
+ }
+
+ if listCertificatesResp.Certificates != nil {
+ for _, certificate := range listCertificatesResp.Certificates {
+ // 对比证书序列号
+ if !strings.EqualFold(certX509.SerialNumber.Text(16), certificate.Serial) {
+ continue
+ }
+
+ // 再对比证书有效期
+ cstzone := time.FixedZone("CST", 8*60*60)
+ oldCertNotBefore, _ := time.ParseInLocation(time.DateTime, certificate.ValidityFrom, cstzone)
+ oldCertNotAfter, _ := time.ParseInLocation(time.DateTime, certificate.ValidityTo, cstzone)
+ if !certX509.NotBefore.Equal(oldCertNotBefore) || !certX509.NotAfter.Equal(oldCertNotAfter) {
+ continue
+ }
+
+ // 如果以上信息都一致,则视为已存在相同证书,直接返回
+ u.logger.Info("ssl certificate already exists")
+ return &uploader.UploadResult{
+ CertId: certificate.CertificateId,
+ CertName: certificate.Name,
+ }, nil
+ }
+ }
+
+ // 生成新证书名(需符合网宿云命名规则)
+ var certId string
+ certName := fmt.Sprintf("certimate_%d", time.Now().UnixMilli())
+
+ // 新增证书
+ // REF: https://www.wangsu.com/document/api-doc/25199?productCode=certificatemanagement
+ createCertificateReq := &wangsusdk.CreateCertificateRequest{
+ Name: typeutil.ToPtr(certName),
+ Certificate: typeutil.ToPtr(certPEM),
+ PrivateKey: typeutil.ToPtr(privkeyPEM),
+ Comment: typeutil.ToPtr("upload from certimate"),
+ }
+ createCertificateResp, err := u.sdkClient.CreateCertificate(createCertificateReq)
+ u.logger.Debug("sdk request 'certificatemanagement.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.CreateCertificate': %w", err)
+ }
+
+ // 网宿云证书 URL 中包含证书 ID
+ // 格式:
+ // https://open.chinanetcenter.com/api/certificate/100001
+ wangsuCertIdMatches := regexp.MustCompile(`/certificate/([0-9]+)`).FindStringSubmatch(createCertificateResp.CertificateUrl)
+ if len(wangsuCertIdMatches) > 1 {
+ certId = wangsuCertIdMatches[1]
+ } else {
+ return nil, fmt.Errorf("received empty certificate id")
+ }
+
+ return &uploader.UploadResult{
+ CertId: certId,
+ CertName: certName,
+ }, nil
+}
+
+func createSdkClient(accessKeyId, accessKeySecret string) (*wangsusdk.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 wangsusdk.NewClient(accessKeyId, accessKeySecret), nil
+}
diff --git a/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go
new file mode 100644
index 00000000..bdec8cfe
--- /dev/null
+++ b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go
@@ -0,0 +1,72 @@
+package jdcloudssl_test
+
+import (
+ "context"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/wangsu-certificate"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fAccessKeyId string
+ fAccessKeySecret string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_UPLOADER_JDCLOUDSSL_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
+ flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./wangsu_certificate_test.go -args \
+ --CERTIMATE_UPLOADER_WANGSUCERTIFICATE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_UPLOADER_WANGSUCERTIFICATE_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYID="your-access-key-id" \
+ --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYSECRET="your-access-key-secret"
+*/
+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),
+ }, "\n"))
+
+ uploader, err := provider.NewUploader(&provider.UploaderConfig{
+ AccessKeyId: fAccessKeyId,
+ AccessKeySecret: fAccessKeySecret,
+ })
+ 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/sdk3rd/1panel/client.go b/internal/pkg/sdk3rd/1panel/client.go
index 0afbe2a5..003203d3 100644
--- a/internal/pkg/sdk3rd/1panel/client.go
+++ b/internal/pkg/sdk3rd/1panel/client.go
@@ -14,19 +14,30 @@ import (
)
type Client struct {
- apiHost string
- apiKey string
+ apiKey string
client *resty.Client
}
-func NewClient(apiHost, apiKey string) *Client {
- client := resty.New()
+func NewClient(apiHost, apiVersion, apiKey string) *Client {
+ if apiVersion == "" {
+ apiVersion = "v1"
+ }
+
+ client := resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/") + "/api/" + apiVersion).
+ SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
+ timestamp := fmt.Sprintf("%d", time.Now().Unix())
+ tokenMd5 := md5.Sum([]byte("1panel" + apiKey + timestamp))
+ tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
+ req.Header.Set("1Panel-Timestamp", timestamp)
+ req.Header.Set("1Panel-Token", tokenMd5Hex)
+
+ return nil
+ })
return &Client{
- apiHost: strings.TrimRight(apiHost, "/"),
- apiKey: apiKey,
- client: client,
+ client: client,
}
}
@@ -40,16 +51,8 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client {
return c
}
-func (c *Client) generateToken(timestamp string) string {
- tokenMd5 := md5.Sum([]byte("1panel" + c.apiKey + timestamp))
- tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
- return tokenMd5Hex
-}
-
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
req := c.client.R()
- req.Method = method
- req.URL = c.apiHost + "/api/v1" + path
if strings.EqualFold(method, http.MethodGet) {
qs := make(map[string]string)
if params != nil {
@@ -65,21 +68,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
req = req.SetQueryParams(qs)
} else {
- req = req.
- SetHeader("Content-Type", "application/json").
- SetBody(params)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- timestamp := fmt.Sprintf("%d", time.Now().Unix())
- token := c.generateToken(timestamp)
- req.SetHeader("1Panel-Timestamp", timestamp)
- req.SetHeader("1Panel-Token", token)
-
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("1panel api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -95,7 +91,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("1panel api error: failed to parse response: %w", err)
+ return fmt.Errorf("1panel api error: failed to unmarshal response: %w", err)
} else if errcode := result.GetCode(); errcode/100 != 2 {
return fmt.Errorf("1panel api error: code='%d', message='%s'", errcode, result.GetMessage())
}
diff --git a/internal/pkg/sdk3rd/baishan/client.go b/internal/pkg/sdk3rd/baishan/client.go
index 408bcbc9..b3e428ee 100644
--- a/internal/pkg/sdk3rd/baishan/client.go
+++ b/internal/pkg/sdk3rd/baishan/client.go
@@ -13,17 +13,16 @@ import (
)
type Client struct {
- apiToken string
-
client *resty.Client
}
func NewClient(apiToken string) *Client {
- client := resty.New()
+ client := resty.New().
+ SetBaseURL("https://cdn.api.baishan.com").
+ SetHeader("token", apiToken)
return &Client{
- apiToken: apiToken,
- client: client,
+ client: client,
}
}
@@ -34,8 +33,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
req := c.client.R()
- req.Method = method
- req.URL = "https://cdn.api.baishan.com" + path
if strings.EqualFold(method, http.MethodGet) {
qs := url.Values{}
if params != nil {
@@ -61,21 +58,16 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
}
}
- req = req.
- SetQueryParam("token", c.apiToken).
- SetQueryParamsFromValues(qs)
+ req = req.SetQueryParamsFromValues(qs)
} else {
- req = req.
- SetHeader("Content-Type", "application/json").
- SetQueryParam("token", c.apiToken).
- SetBody(params)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("baishan api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -91,7 +83,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("baishan api error: failed to parse response: %w", err)
+ return fmt.Errorf("baishan api error: failed to unmarshal response: %w", err)
} else if errcode := result.GetCode(); errcode != 0 {
return fmt.Errorf("baishan api error: code='%d', message='%s'", errcode, result.GetMessage())
}
diff --git a/internal/pkg/sdk3rd/btpanel/client.go b/internal/pkg/sdk3rd/btpanel/client.go
index 781e9a75..1da625da 100644
--- a/internal/pkg/sdk3rd/btpanel/client.go
+++ b/internal/pkg/sdk3rd/btpanel/client.go
@@ -14,19 +14,18 @@ import (
)
type Client struct {
- apiHost string
- apiKey string
+ apiKey string
client *resty.Client
}
func NewClient(apiHost, apiKey string) *Client {
- client := resty.New()
+ client := resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/"))
return &Client{
- apiHost: strings.TrimRight(apiHost, "/"),
- apiKey: apiKey,
- client: client,
+ apiKey: apiKey,
+ client: client,
}
}
@@ -78,15 +77,14 @@ func (c *Client) sendRequest(path string, params interface{}) (*resty.Response,
data["request_time"] = fmt.Sprintf("%d", timestamp)
data["request_token"] = c.generateSignature(fmt.Sprintf("%d", timestamp))
- url := c.apiHost + path
req := c.client.R().
SetHeader("Content-Type", "application/x-www-form-urlencoded").
SetFormData(data)
- resp, err := req.Post(url)
+ resp, err := req.Post(path)
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, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("baota api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -99,7 +97,7 @@ func (c *Client) sendRequestWithResult(path string, params interface{}, result B
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("baota api error: failed to parse response: %w", err)
+ return fmt.Errorf("baota api error: failed to unmarshal response: %w", err)
} else if errstatus := result.GetStatus(); errstatus != nil && !*errstatus {
if result.GetMessage() == nil {
return fmt.Errorf("baota api error: unknown error")
diff --git a/internal/pkg/sdk3rd/btwaf/api.go b/internal/pkg/sdk3rd/btwaf/api.go
new file mode 100644
index 00000000..bc35dee5
--- /dev/null
+++ b/internal/pkg/sdk3rd/btwaf/api.go
@@ -0,0 +1,19 @@
+package btwaf
+
+func (c *Client) GetSiteList(req *GetSiteListRequest) (*GetSiteListResponse, error) {
+ resp := &GetSiteListResponse{}
+ err := c.sendRequestWithResult("/wafmastersite/get_site_list", req, resp)
+ return resp, err
+}
+
+func (c *Client) ModifySite(req *ModifySiteRequest) (*ModifySiteResponse, error) {
+ resp := &ModifySiteResponse{}
+ err := c.sendRequestWithResult("/wafmastersite/modify_site", req, resp)
+ return resp, err
+}
+
+func (c *Client) ConfigSetSSL(req *ConfigSetSSLRequest) (*ConfigSetSSLResponse, error) {
+ resp := &ConfigSetSSLResponse{}
+ err := c.sendRequestWithResult("/config/set_cert", req, resp)
+ return resp, err
+}
diff --git a/internal/pkg/sdk3rd/btwaf/client.go b/internal/pkg/sdk3rd/btwaf/client.go
new file mode 100644
index 00000000..5ae545cc
--- /dev/null
+++ b/internal/pkg/sdk3rd/btwaf/client.go
@@ -0,0 +1,77 @@
+package btwaf
+
+import (
+ "crypto/md5"
+ "crypto/tls"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/go-resty/resty/v2"
+)
+
+type Client struct {
+ client *resty.Client
+}
+
+func NewClient(apiHost, apiKey string) *Client {
+ client := resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/") + "/api").
+ SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
+ timestamp := fmt.Sprintf("%d", time.Now().Unix())
+ keyMd5 := md5.Sum([]byte(apiKey))
+ keyMd5Hex := strings.ToLower(hex.EncodeToString(keyMd5[:]))
+ signMd5 := md5.Sum([]byte(timestamp + keyMd5Hex))
+ signMd5Hex := strings.ToLower(hex.EncodeToString(signMd5[:]))
+ req.Header.Set("waf_request_time", timestamp)
+ req.Header.Set("waf_request_token", signMd5Hex)
+
+ return nil
+ })
+
+ return &Client{
+ 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(path string, params interface{}) (*resty.Response, error) {
+ req := c.client.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(params)
+ resp, err := req.Post(path)
+ 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, resp: %s", resp.StatusCode(), resp.String())
+ }
+
+ return resp, nil
+}
+
+func (c *Client) sendRequestWithResult(path string, params interface{}, result BaseResponse) error {
+ resp, err := c.sendRequest(path, params)
+ if err != nil {
+ return err
+ }
+
+ if err := json.Unmarshal(resp.Body(), &result); err != nil {
+ return fmt.Errorf("baota api error: failed to unmarshal response: %w", err)
+ } else if errcode := result.GetCode(); errcode != 0 {
+ return fmt.Errorf("baota api error: code='%d'", errcode)
+ }
+
+ return nil
+}
diff --git a/internal/pkg/sdk3rd/btwaf/models.go b/internal/pkg/sdk3rd/btwaf/models.go
new file mode 100644
index 00000000..16290e88
--- /dev/null
+++ b/internal/pkg/sdk3rd/btwaf/models.go
@@ -0,0 +1,67 @@
+package btwaf
+
+type BaseResponse interface {
+ GetCode() int32
+}
+
+type baseResponse struct {
+ Code *int32 `json:"code,omitempty"`
+}
+
+func (r *baseResponse) GetCode() int32 {
+ if r.Code != nil {
+ return *r.Code
+ }
+ return 0
+}
+
+type GetSiteListRequest struct {
+ Page *int32 `json:"p,omitempty"`
+ PageSize *int32 `json:"p_size,omitempty"`
+ SiteName *string `json:"site_name,omitempty"`
+}
+
+type GetSiteListResponse struct {
+ baseResponse
+ Result *struct {
+ List []*struct {
+ SiteId string `json:"site_id"`
+ SiteName string `json:"site_name"`
+ Type string `json:"types"`
+ Status int32 `json:"status"`
+ CreateTime int64 `json:"create_time"`
+ UpdateTime int64 `json:"update_time"`
+ } `json:"list"`
+ Total int32 `json:"total"`
+ } `json:"res,omitempty"`
+}
+
+type SiteServerInfo struct {
+ ListenSSLPort *int32 `json:"listen_ssl_port,omitempty"`
+ SSL *SiteServerSSLInfo `json:"ssl,omitempty"`
+}
+
+type SiteServerSSLInfo struct {
+ IsSSL *int32 `json:"is_ssl,omitempty"`
+ FullChain *string `json:"full_chain,omitempty"`
+ PrivateKey *string `json:"private_key,omitempty"`
+}
+
+type ModifySiteRequest struct {
+ SiteId string `json:"site_id"`
+ Type *string `json:"types,omitempty"`
+ Server *SiteServerInfo `json:"server,omitempty"`
+}
+
+type ModifySiteResponse struct {
+ baseResponse
+}
+
+type ConfigSetSSLRequest struct {
+ CertContent string `json:"certContent"`
+ KeyContent string `json:"keyContent"`
+}
+
+type ConfigSetSSLResponse struct {
+ baseResponse
+}
diff --git a/internal/pkg/sdk3rd/bunny/client.go b/internal/pkg/sdk3rd/bunny/client.go
index 2b1ed4d1..8d50e1fc 100644
--- a/internal/pkg/sdk3rd/bunny/client.go
+++ b/internal/pkg/sdk3rd/bunny/client.go
@@ -11,17 +11,16 @@ import (
)
type Client struct {
- apiToken string
-
client *resty.Client
}
func NewClient(apiToken string) *Client {
- client := resty.New()
+ client := resty.New().
+ SetBaseURL("https://api.bunny.net").
+ SetHeader("AccessKey", apiToken)
return &Client{
- apiToken: apiToken,
- client: client,
+ client: client,
}
}
@@ -32,9 +31,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
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 {
@@ -50,16 +46,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
req = req.SetQueryParams(qs)
} else {
- req = req.
- SetHeader("Content-Type", "application/json").
- SetBody(params)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, fmt.Errorf("bunny api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
diff --git a/internal/pkg/sdk3rd/cachefly/client.go b/internal/pkg/sdk3rd/cachefly/client.go
index b1777ea9..342e329d 100644
--- a/internal/pkg/sdk3rd/cachefly/client.go
+++ b/internal/pkg/sdk3rd/cachefly/client.go
@@ -11,17 +11,16 @@ import (
)
type Client struct {
- apiToken string
-
client *resty.Client
}
func NewClient(apiToken string) *Client {
- client := resty.New()
+ client := resty.New().
+ SetBaseURL("https://api.cachefly.com/api/2.5").
+ SetHeader("x-cf-authorization", "Bearer "+apiToken)
return &Client{
- apiToken: apiToken,
- client: client,
+ client: client,
}
}
@@ -32,9 +31,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
req := c.client.R()
- req.Method = method
- req.URL = "https://api.cachefly.com/api/2.5" + path
- req = req.SetHeader("x-cf-authorization", "Bearer "+c.apiToken)
if strings.EqualFold(method, http.MethodGet) {
qs := make(map[string]string)
if params != nil {
@@ -50,16 +46,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
req = req.SetQueryParams(qs)
} else {
- req = req.
- SetHeader("Content-Type", "application/json").
- SetBody(params)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("cachefly api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -75,7 +69,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("cachefly api error: failed to parse response: %w", err)
+ return fmt.Errorf("cachefly api error: failed to unmarshal response: %w", err)
}
return nil
diff --git a/internal/pkg/sdk3rd/cdnfly/client.go b/internal/pkg/sdk3rd/cdnfly/client.go
index 0061e363..c8753ed5 100644
--- a/internal/pkg/sdk3rd/cdnfly/client.go
+++ b/internal/pkg/sdk3rd/cdnfly/client.go
@@ -12,21 +12,17 @@ import (
)
type Client struct {
- apiHost string
- apiKey string
- apiSecret string
-
client *resty.Client
}
func NewClient(apiHost, apiKey, apiSecret string) *Client {
- client := resty.New()
+ client := resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/")).
+ SetHeader("api-key", apiKey).
+ SetHeader("api-secret", apiSecret)
return &Client{
- apiHost: strings.TrimRight(apiHost, "/"),
- apiKey: apiKey,
- apiSecret: apiSecret,
- client: client,
+ client: client,
}
}
@@ -42,11 +38,6 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client {
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
req := c.client.R()
- req.Method = method
- req.URL = c.apiHost + path
- req = req.
- SetHeader("api-key", c.apiKey).
- SetHeader("api-secret", c.apiSecret)
if strings.EqualFold(method, http.MethodGet) {
qs := make(map[string]string)
if params != nil {
@@ -62,16 +53,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
req = req.SetQueryParams(qs)
} else {
- req = req.
- SetHeader("Content-Type", "application/json").
- SetBody(params)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("cdnfly api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -87,7 +76,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("cdnfly api error: failed to parse response: %w", err)
+ return fmt.Errorf("cdnfly api error: failed to unmarshal response: %w", err)
} else if errcode := result.GetCode(); errcode != "" && errcode != "0" {
return fmt.Errorf("cdnfly api error: code='%s', message='%s'", errcode, result.GetMessage())
}
diff --git a/internal/pkg/sdk3rd/dnsla/client.go b/internal/pkg/sdk3rd/dnsla/client.go
index 7dfbd00a..d9a86fc5 100644
--- a/internal/pkg/sdk3rd/dnsla/client.go
+++ b/internal/pkg/sdk3rd/dnsla/client.go
@@ -11,19 +11,16 @@ import (
)
type Client struct {
- apiId string
- apiSecret string
-
client *resty.Client
}
func NewClient(apiId, apiSecret string) *Client {
- client := resty.New()
+ client := resty.New().
+ SetBaseURL("https://api.dns.la/api").
+ SetBasicAuth(apiId, apiSecret)
return &Client{
- apiId: apiId,
- apiSecret: apiSecret,
- client: client,
+ client: client,
}
}
@@ -33,9 +30,7 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
}
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
- req := c.client.R().SetBasicAuth(c.apiId, c.apiSecret)
- req.Method = method
- req.URL = "https://api.dns.la/api" + path
+ req := c.client.R()
if strings.EqualFold(method, http.MethodGet) {
qs := make(map[string]string)
if params != nil {
@@ -51,16 +46,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
req = req.SetQueryParams(qs)
} else {
- req = req.
- SetHeader("Content-Type", "application/json").
- SetBody(params)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("dnsla api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -76,7 +69,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("dnsla api error: failed to parse response: %w", err)
+ return fmt.Errorf("dnsla api error: failed to unmarshal response: %w", err)
} else if errcode := result.GetCode(); errcode/100 != 2 {
return fmt.Errorf("dnsla api error: code='%d', message='%s'", errcode, result.GetMessage())
}
diff --git a/internal/pkg/sdk3rd/dogecloud/client.go b/internal/pkg/sdk3rd/dogecloud/client.go
index 46f3513d..75342907 100644
--- a/internal/pkg/sdk3rd/dogecloud/client.go
+++ b/internal/pkg/sdk3rd/dogecloud/client.go
@@ -164,8 +164,8 @@ func (c *Client) sendReq(method string, path string, data map[string]interface{}
if err != nil {
return nil, err
}
- req.Header.Add("Content-Type", mime)
- req.Header.Add("Authorization", auth)
+ req.Header.Set("Content-Type", mime)
+ req.Header.Set("Authorization", auth)
client := http.Client{}
resp, err := client.Do(req)
@@ -174,10 +174,10 @@ func (c *Client) sendReq(method string, path string, data map[string]interface{}
}
defer resp.Body.Close()
- r, err := io.ReadAll(resp.Body)
+ bytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
- return r, nil
+ return bytes, nil
}
diff --git a/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client.go b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client.go
index fb7b7cf7..02ded6fd 100644
--- a/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client.go
+++ b/internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace/applications/v7/edgio_client.go
@@ -140,7 +140,7 @@ func (c *EdgioClient) GetProperties(page int, pageSize int, organizationID strin
}
if resp.IsError() {
- return nil, fmt.Errorf("unexpected status code for getProperties: %d, %s", resp.StatusCode(), resp.Body())
+ return nil, fmt.Errorf("unexpected status code for getProperties: %d, %s", resp.StatusCode(), resp.String())
}
return &propertiesResp, nil
@@ -512,7 +512,7 @@ func (c *EdgioClient) UploadCdnConfiguration(config *dtos.CDNConfiguration) (*dt
}
if resp.IsError() {
- return nil, fmt.Errorf("unexpected status code for uploadCdnConfiguration: %d, %s", resp.StatusCode(), resp.Body())
+ return nil, fmt.Errorf("unexpected status code for uploadCdnConfiguration: %d, %s", resp.StatusCode(), resp.String())
}
return &response, nil
diff --git a/internal/pkg/sdk3rd/flexcdn/api.go b/internal/pkg/sdk3rd/flexcdn/api.go
new file mode 100644
index 00000000..5008fdf4
--- /dev/null
+++ b/internal/pkg/sdk3rd/flexcdn/api.go
@@ -0,0 +1,48 @@
+package flexcdn
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "time"
+)
+
+func (c *Client) ensureAccessTokenExists() error {
+ c.accessTokenMtx.Lock()
+ defer c.accessTokenMtx.Unlock()
+ if c.accessToken != "" && c.accessTokenExp.After(time.Now()) {
+ return nil
+ }
+
+ req := &getAPIAccessTokenRequest{
+ Type: c.apiRole,
+ 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("flexcdn api error: failed to unmarshal response: %w", err)
+ } else if resp.GetCode() != 200 {
+ return fmt.Errorf("flexcdn get access token failed: code='%d', message='%s'", resp.GetCode(), resp.GetMessage())
+ }
+
+ c.accessToken = resp.Data.Token
+ c.accessTokenExp = time.Unix(resp.Data.ExpiresAt, 0)
+
+ return nil
+}
+
+func (c *Client) UpdateSSLCert(req *UpdateSSLCertRequest) (*UpdateSSLCertResponse, error) {
+ if err := c.ensureAccessTokenExists(); 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/flexcdn/client.go b/internal/pkg/sdk3rd/flexcdn/client.go
new file mode 100644
index 00000000..beae469a
--- /dev/null
+++ b/internal/pkg/sdk3rd/flexcdn/client.go
@@ -0,0 +1,102 @@
+package flexcdn
+
+import (
+ "crypto/tls"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/go-resty/resty/v2"
+)
+
+type Client struct {
+ apiRole string
+ accessKeyId string
+ accessKey string
+
+ accessToken string
+ accessTokenExp time.Time
+ accessTokenMtx sync.Mutex
+
+ client *resty.Client
+}
+
+func NewClient(apiHost, apiRole, accessKeyId, accessKey string) *Client {
+ client := &Client{
+ apiRole: apiRole,
+ accessKeyId: accessKeyId,
+ accessKey: accessKey,
+ }
+ client.client = resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/")).
+ SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
+ if client.accessToken != "" {
+ req.Header.Set("X-Cloud-Access-Token", client.accessToken)
+ }
+
+ return nil
+ })
+
+ return 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()
+ 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.Execute(method, path)
+ if err != nil {
+ return resp, fmt.Errorf("flexcdn api error: failed to send request: %w", err)
+ } else if resp.IsError() {
+ return resp, fmt.Errorf("flexcdn api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
+ }
+
+ 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("flexcdn api error: failed to unmarshal response: %w", err)
+ } else if errcode := result.GetCode(); errcode != 200 {
+ return fmt.Errorf("flexcdn api error: code='%d', message='%s'", errcode, result.GetMessage())
+ }
+
+ return nil
+}
diff --git a/internal/pkg/sdk3rd/flexcdn/models.go b/internal/pkg/sdk3rd/flexcdn/models.go
new file mode 100644
index 00000000..c976eccc
--- /dev/null
+++ b/internal/pkg/sdk3rd/flexcdn/models.go
@@ -0,0 +1,52 @@
+package flexcdn
+
+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/sdk3rd/gname/client.go b/internal/pkg/sdk3rd/gname/client.go
index 9424e8ec..ef00e699 100644
--- a/internal/pkg/sdk3rd/gname/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, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("gname api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -95,7 +95,7 @@ func (c *Client) sendRequestWithResult(path string, params interface{}, result B
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("gname api error: failed to parse response: %w", err)
+ return fmt.Errorf("gname api error: failed to unmarshal response: %w", err)
} else if errcode := result.GetCode(); errcode != 1 {
return fmt.Errorf("gname api error: code='%d', message='%s'", errcode, result.GetMessage())
}
diff --git a/internal/pkg/sdk3rd/goedge/api.go b/internal/pkg/sdk3rd/goedge/api.go
index c217e8ae..4589f70c 100644
--- a/internal/pkg/sdk3rd/goedge/api.go
+++ b/internal/pkg/sdk3rd/goedge/api.go
@@ -7,7 +7,13 @@ import (
"time"
)
-func (c *Client) getAccessToken() error {
+func (c *Client) ensureAccessTokenExists() error {
+ c.accessTokenMtx.Lock()
+ defer c.accessTokenMtx.Unlock()
+ if c.accessToken != "" && c.accessTokenExp.After(time.Now()) {
+ return nil
+ }
+
req := &getAPIAccessTokenRequest{
Type: c.apiRole,
AccessKeyId: c.accessKeyId,
@@ -20,24 +26,20 @@ func (c *Client) getAccessToken() error {
resp := &getAPIAccessTokenResponse{}
if err := json.Unmarshal(res.Body(), &resp); err != nil {
- return fmt.Errorf("goedge api error: failed to parse response: %w", err)
+ return fmt.Errorf("goedge api error: failed to unmarshal response: %w", err)
} else if resp.GetCode() != 200 {
- return fmt.Errorf("goedge get access token failed: code: %d, message: %s", resp.GetCode(), resp.GetMessage())
+ 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
- }
+ if err := c.ensureAccessTokenExists(); err != nil {
+ return nil, err
}
resp := &UpdateSSLCertResponse{}
diff --git a/internal/pkg/sdk3rd/goedge/client.go b/internal/pkg/sdk3rd/goedge/client.go
index c42b798b..3dc961e3 100644
--- a/internal/pkg/sdk3rd/goedge/client.go
+++ b/internal/pkg/sdk3rd/goedge/client.go
@@ -13,7 +13,6 @@ import (
)
type Client struct {
- apiHost string
apiRole string
accessKeyId string
accessKey string
@@ -26,15 +25,22 @@ type Client struct {
}
func NewClient(apiHost, apiRole, accessKeyId, accessKey string) *Client {
- client := resty.New()
-
- return &Client{
- apiHost: strings.TrimRight(apiHost, "/"),
+ client := &Client{
apiRole: apiRole,
accessKeyId: accessKeyId,
accessKey: accessKey,
- client: client,
}
+ client.client = resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/")).
+ SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
+ if client.accessToken != "" {
+ req.Header.Set("X-Edge-Access-Token", client.accessToken)
+ }
+
+ return nil
+ })
+
+ return client
}
func (c *Client) WithTimeout(timeout time.Duration) *Client {
@@ -48,9 +54,7 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client {
}
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
+ req := c.client.R()
if strings.EqualFold(method, http.MethodGet) {
qs := make(map[string]string)
if params != nil {
@@ -64,21 +68,16 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
}
}
- req = req.
- SetQueryParams(qs).
- SetHeader("X-Edge-Access-Token", c.accessToken)
+ req = req.SetQueryParams(qs)
} else {
- req = req.
- SetHeader("Content-Type", "application/json").
- SetHeader("X-Edge-Access-Token", c.accessToken).
- SetBody(params)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, fmt.Errorf("goedge api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -94,7 +93,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("goedge api error: failed to parse response: %w", err)
+ return fmt.Errorf("goedge api error: failed to unmarshal response: %w", err)
} else if errcode := result.GetCode(); errcode != 200 {
return fmt.Errorf("goedge api error: code='%d', message='%s'", errcode, result.GetMessage())
}
diff --git a/internal/pkg/sdk3rd/lecdn/v3/client/api.go b/internal/pkg/sdk3rd/lecdn/v3/client/api.go
new file mode 100644
index 00000000..89f9cdc0
--- /dev/null
+++ b/internal/pkg/sdk3rd/lecdn/v3/client/api.go
@@ -0,0 +1,50 @@
+package client
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+)
+
+func (c *Client) ensureAccessTokenExists() error {
+ c.accessTokenMtx.Lock()
+ defer c.accessTokenMtx.Unlock()
+ if c.accessToken != "" {
+ return nil
+ }
+
+ req := &loginRequest{
+ Email: c.username,
+ Username: c.username,
+ Password: c.password,
+ }
+ res, err := c.sendRequest(http.MethodPost, "/login", req)
+ if err != nil {
+ return err
+ }
+
+ resp := &loginResponse{}
+ if err := json.Unmarshal(res.Body(), &resp); err != nil {
+ return fmt.Errorf("lecdn api error: failed to unmarshal response: %w", err)
+ } else if resp.GetCode() != 200 {
+ return fmt.Errorf("lecdn get token failed: code='%d', message='%s'", resp.GetCode(), resp.GetMessage())
+ }
+
+ c.accessToken = resp.Data.Token
+
+ return nil
+}
+
+func (c *Client) UpdateCertificate(certId int64, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) {
+ if certId == 0 {
+ return nil, fmt.Errorf("lecdn api error: invalid parameter: CertId")
+ }
+
+ if err := c.ensureAccessTokenExists(); err != nil {
+ return nil, err
+ }
+
+ resp := &UpdateCertificateResponse{}
+ err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/certificate/%d", certId), req, resp)
+ return resp, err
+}
diff --git a/internal/pkg/sdk3rd/lecdn/v3/client/client.go b/internal/pkg/sdk3rd/lecdn/v3/client/client.go
new file mode 100644
index 00000000..ad3a752e
--- /dev/null
+++ b/internal/pkg/sdk3rd/lecdn/v3/client/client.go
@@ -0,0 +1,99 @@
+package client
+
+import (
+ "crypto/tls"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/go-resty/resty/v2"
+)
+
+type Client struct {
+ username string
+ password string
+
+ accessToken string
+ accessTokenMtx sync.Mutex
+
+ client *resty.Client
+}
+
+func NewClient(apiHost, username, password string) *Client {
+ client := &Client{
+ username: username,
+ password: password,
+ }
+ client.client = resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/") + "/prod-api").
+ SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
+ if client.accessToken != "" {
+ req.Header.Set("Authorization", "Bearer "+client.accessToken)
+ }
+
+ return nil
+ })
+
+ return 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()
+ 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.Execute(method, path)
+ if err != nil {
+ return resp, fmt.Errorf("lecdn api error: failed to send request: %w", err)
+ } else if resp.IsError() {
+ return resp, fmt.Errorf("lecdn api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
+ }
+
+ 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("lecdn api error: failed to unmarshal response: %w", err)
+ } else if errcode := result.GetCode(); errcode != 200 {
+ return fmt.Errorf("lecdn api error: code='%d', message='%s'", errcode, result.GetMessage())
+ }
+
+ return nil
+}
diff --git a/internal/pkg/sdk3rd/lecdn/v3/client/models.go b/internal/pkg/sdk3rd/lecdn/v3/client/models.go
new file mode 100644
index 00000000..6d63ea79
--- /dev/null
+++ b/internal/pkg/sdk3rd/lecdn/v3/client/models.go
@@ -0,0 +1,47 @@
+package client
+
+type BaseResponse interface {
+ GetCode() int32
+ GetMessage() string
+}
+
+type baseResponse struct {
+ Code int32 `json:"code"`
+ Message string `json:"msg"`
+}
+
+func (r *baseResponse) GetCode() int32 {
+ return r.Code
+}
+
+func (r *baseResponse) GetMessage() string {
+ return r.Message
+}
+
+type loginRequest struct {
+ Email string `json:"email"`
+ Username string `json:"username"`
+ Password string `json:"password"`
+}
+
+type loginResponse struct {
+ baseResponse
+ Data *struct {
+ UserId int64 `json:"user_id"`
+ Username string `json:"username"`
+ Token string `json:"token"`
+ } `json:"data,omitempty"`
+}
+
+type UpdateCertificateRequest struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Type string `json:"type"`
+ SSLPEM string `json:"ssl_pem"`
+ SSLKey string `json:"ssl_key"`
+ AutoRenewal bool `json:"auto_renewal"`
+}
+
+type UpdateCertificateResponse struct {
+ baseResponse
+}
diff --git a/internal/pkg/sdk3rd/lecdn/v3/master/api.go b/internal/pkg/sdk3rd/lecdn/v3/master/api.go
new file mode 100644
index 00000000..00f24a70
--- /dev/null
+++ b/internal/pkg/sdk3rd/lecdn/v3/master/api.go
@@ -0,0 +1,49 @@
+package master
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+)
+
+func (c *Client) ensureAccessTokenExists() error {
+ c.accessTokenMtx.Lock()
+ defer c.accessTokenMtx.Unlock()
+ if c.accessToken != "" {
+ return nil
+ }
+
+ req := &loginRequest{
+ Username: c.username,
+ Password: c.password,
+ }
+ res, err := c.sendRequest(http.MethodPost, "/auth/login", req)
+ if err != nil {
+ return err
+ }
+
+ resp := &loginResponse{}
+ if err := json.Unmarshal(res.Body(), &resp); err != nil {
+ return fmt.Errorf("lecdn api error: failed to unmarshal response: %w", err)
+ } else if resp.GetCode() != 200 {
+ return fmt.Errorf("lecdn get token failed: code='%d', message='%s'", resp.GetCode(), resp.GetMessage())
+ }
+
+ c.accessToken = resp.Data.Token
+
+ return nil
+}
+
+func (c *Client) UpdateCertificate(certId int64, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) {
+ if certId == 0 {
+ return nil, fmt.Errorf("lecdn api error: invalid parameter: CertId")
+ }
+
+ if err := c.ensureAccessTokenExists(); err != nil {
+ return nil, err
+ }
+
+ resp := &UpdateCertificateResponse{}
+ err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/certificate/%d", certId), req, resp)
+ return resp, err
+}
diff --git a/internal/pkg/sdk3rd/lecdn/v3/master/client.go b/internal/pkg/sdk3rd/lecdn/v3/master/client.go
new file mode 100644
index 00000000..2da0c0c4
--- /dev/null
+++ b/internal/pkg/sdk3rd/lecdn/v3/master/client.go
@@ -0,0 +1,99 @@
+package master
+
+import (
+ "crypto/tls"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/go-resty/resty/v2"
+)
+
+type Client struct {
+ username string
+ password string
+
+ accessToken string
+ accessTokenMtx sync.Mutex
+
+ client *resty.Client
+}
+
+func NewClient(apiHost, username, password string) *Client {
+ client := &Client{
+ username: username,
+ password: password,
+ }
+ client.client = resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/") + "/prod-api").
+ SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
+ if client.accessToken != "" {
+ req.Header.Set("Authorization", "Bearer "+client.accessToken)
+ }
+
+ return nil
+ })
+
+ return 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()
+ 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.Execute(method, path)
+ if err != nil {
+ return resp, fmt.Errorf("lecdn api error: failed to send request: %w", err)
+ } else if resp.IsError() {
+ return resp, fmt.Errorf("lecdn api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
+ }
+
+ 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("lecdn api error: failed to unmarshal response: %w", err)
+ } else if errcode := result.GetCode(); errcode != 200 {
+ return fmt.Errorf("lecdn api error: code='%d', message='%s'", errcode, result.GetMessage())
+ }
+
+ return nil
+}
diff --git a/internal/pkg/sdk3rd/lecdn/v3/master/models.go b/internal/pkg/sdk3rd/lecdn/v3/master/models.go
new file mode 100644
index 00000000..2e896f42
--- /dev/null
+++ b/internal/pkg/sdk3rd/lecdn/v3/master/models.go
@@ -0,0 +1,47 @@
+package master
+
+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 loginRequest struct {
+ Username string `json:"username"`
+ Password string `json:"password"`
+}
+
+type loginResponse struct {
+ baseResponse
+ Data *struct {
+ UserId int64 `json:"user_id"`
+ Username string `json:"username"`
+ Token string `json:"token"`
+ } `json:"data,omitempty"`
+}
+
+type UpdateCertificateRequest struct {
+ ClientId int64 `json:"client_id"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Type string `json:"type"`
+ SSLPEM string `json:"ssl_pem"`
+ SSLKey string `json:"ssl_key"`
+ AutoRenewal bool `json:"auto_renewal"`
+}
+
+type UpdateCertificateResponse struct {
+ baseResponse
+}
diff --git a/internal/pkg/sdk3rd/netlify/client.go b/internal/pkg/sdk3rd/netlify/client.go
index 5f8bc6c2..d270e35e 100644
--- a/internal/pkg/sdk3rd/netlify/client.go
+++ b/internal/pkg/sdk3rd/netlify/client.go
@@ -11,17 +11,16 @@ import (
)
type Client struct {
- apiToken string
-
client *resty.Client
}
func NewClient(apiToken string) *Client {
- client := resty.New()
+ client := resty.New().
+ SetBaseURL("https://api.netlify.com/api/v1").
+ SetHeader("Authorization", "Bearer "+apiToken)
return &Client{
- apiToken: apiToken,
- client: client,
+ client: client,
}
}
@@ -31,9 +30,7 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
}
func (c *Client) sendRequest(method string, path string, queryParams interface{}, payloadParams interface{}) (*resty.Response, error) {
- req := c.client.R().SetHeader("Authorization", "Bearer "+c.apiToken)
- req.Method = method
- req.URL = "https://api.netlify.com/api/v1" + path
+ req := c.client.R()
if queryParams != nil {
qs := make(map[string]string)
@@ -63,16 +60,14 @@ func (c *Client) sendRequest(method string, path string, queryParams interface{}
req = req.SetQueryParams(qs)
} else {
- req = req.
- SetHeader("Content-Type", "application/json").
- SetBody(payloadParams)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(payloadParams)
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
if err != nil {
return resp, fmt.Errorf("netlify api error: failed to send request: %w", err)
} else if resp.IsError() {
- return resp, fmt.Errorf("netlify api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("netlify api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -88,7 +83,7 @@ func (c *Client) sendRequestWithResult(method string, path string, queryParams i
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("netlify api error: failed to parse response: %w", err)
+ return fmt.Errorf("netlify api error: failed to unmarshal response: %w", err)
} else if errcode := result.GetCode(); errcode != 0 {
return fmt.Errorf("netlify api error: code='%d', message='%s'", errcode, result.GetMessage())
}
diff --git a/internal/pkg/sdk3rd/rainyun/client.go b/internal/pkg/sdk3rd/rainyun/client.go
index 0ead39cd..80113f0d 100644
--- a/internal/pkg/sdk3rd/rainyun/client.go
+++ b/internal/pkg/sdk3rd/rainyun/client.go
@@ -11,16 +11,15 @@ import (
)
type Client struct {
- apiKey string
-
client *resty.Client
}
func NewClient(apiKey string) *Client {
- client := resty.New()
+ client := resty.New().
+ SetBaseURL("https://api.v2.rainyun.com").
+ SetHeader("x-api-key", apiKey)
return &Client{
- apiKey: apiKey,
client: client,
}
}
@@ -31,25 +30,21 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
}
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
+ req := c.client.R()
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)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, fmt.Errorf("rainyun api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -65,7 +60,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("rainyun api error: failed to parse response: %w", err)
+ return fmt.Errorf("rainyun api error: failed to unmarshal response: %w", err)
} else if errcode := result.GetCode(); errcode/100 != 2 {
return fmt.Errorf("rainyun api error: code='%d', message='%s'", errcode, result.GetMessage())
}
diff --git a/internal/pkg/sdk3rd/ratpanel/api.go b/internal/pkg/sdk3rd/ratpanel/api.go
new file mode 100644
index 00000000..17f8110f
--- /dev/null
+++ b/internal/pkg/sdk3rd/ratpanel/api.go
@@ -0,0 +1,15 @@
+package ratpanelsdk
+
+import "net/http"
+
+func (c *Client) SettingCert(req *SettingCertRequest) (*SettingCertResponse, error) {
+ resp := &SettingCertResponse{}
+ err := c.sendRequestWithResult(http.MethodPost, "/setting/cert", req, resp)
+ return resp, err
+}
+
+func (c *Client) WebsiteCert(req *WebsiteCertRequest) (*WebsiteCertResponse, error) {
+ resp := &WebsiteCertResponse{}
+ err := c.sendRequestWithResult(http.MethodPost, "/website/cert", req, resp)
+ return resp, err
+}
diff --git a/internal/pkg/sdk3rd/ratpanel/client.go b/internal/pkg/sdk3rd/ratpanel/client.go
new file mode 100644
index 00000000..47202a04
--- /dev/null
+++ b/internal/pkg/sdk3rd/ratpanel/client.go
@@ -0,0 +1,141 @@
+package ratpanelsdk
+
+import (
+ "bytes"
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/tls"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/go-resty/resty/v2"
+)
+
+type Client struct {
+ client *resty.Client
+}
+
+func NewClient(apiHost string, accessTokenId int32, accessToken string) *Client {
+ client := resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/")+"/api").
+ SetHeader("Accept", "application/json").
+ SetHeader("Content-Type", "application/json").
+ SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
+ var body []byte
+ var err error
+
+ if req.Body != nil {
+ body, err = io.ReadAll(req.Body)
+ if err != nil {
+ return err
+ }
+ req.Body = io.NopCloser(bytes.NewReader(body))
+ }
+
+ canonicalPath := req.URL.Path
+ if !strings.HasPrefix(canonicalPath, "/api") {
+ index := strings.Index(canonicalPath, "/api")
+ if index != -1 {
+ canonicalPath = canonicalPath[index:]
+ }
+ }
+
+ canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s",
+ req.Method,
+ canonicalPath,
+ req.URL.Query().Encode(),
+ sha256Sum(string(body)))
+
+ timestamp := time.Now().Unix()
+ req.Header.Set("X-Timestamp", fmt.Sprintf("%d", timestamp))
+
+ stringToSign := fmt.Sprintf("%s\n%d\n%s",
+ "HMAC-SHA256",
+ timestamp,
+ sha256Sum(canonicalRequest))
+ signature := hmacSha256(stringToSign, accessToken)
+ req.Header.Set("Authorization", fmt.Sprintf("HMAC-SHA256 Credential=%d, Signature=%s", accessTokenId, signature))
+
+ return nil
+ })
+
+ return &Client{
+ 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()
+ 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.Execute(method, path)
+ if err != nil {
+ return resp, fmt.Errorf("ratpanel api error: failed to send request: %w", err)
+ } else if resp.IsError() {
+ return resp, fmt.Errorf("ratpanel 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("ratpanel api error: failed to unmarshal response: %w", err)
+ } else if errmsg := result.GetMessage(); errmsg != "success" {
+ return fmt.Errorf("ratpanel api error: message='%s'", errmsg)
+ }
+
+ return nil
+}
+
+func sha256Sum(str string) string {
+ sum := sha256.Sum256([]byte(str))
+ dst := make([]byte, hex.EncodedLen(len(sum)))
+ hex.Encode(dst, sum[:])
+ return string(dst)
+}
+
+func hmacSha256(data string, secret string) string {
+ h := hmac.New(sha256.New, []byte(secret))
+ h.Write([]byte(data))
+ return hex.EncodeToString(h.Sum(nil))
+}
diff --git a/internal/pkg/sdk3rd/ratpanel/models.go b/internal/pkg/sdk3rd/ratpanel/models.go
new file mode 100644
index 00000000..bf5f53fb
--- /dev/null
+++ b/internal/pkg/sdk3rd/ratpanel/models.go
@@ -0,0 +1,35 @@
+package ratpanelsdk
+
+type BaseResponse interface {
+ GetMessage() string
+}
+
+type baseResponse struct {
+ Message *string `json:"msg,omitempty"`
+}
+
+func (r *baseResponse) GetMessage() string {
+ if r.Message != nil {
+ return *r.Message
+ }
+ return ""
+}
+
+type SettingCertRequest struct {
+ Certificate string `json:"cert"`
+ PrivateKey string `json:"key"`
+}
+
+type SettingCertResponse struct {
+ baseResponse
+}
+
+type WebsiteCertRequest struct {
+ SiteName string `json:"name"`
+ Certificate string `json:"cert"`
+ PrivateKey string `json:"key"`
+}
+
+type WebsiteCertResponse struct {
+ baseResponse
+}
diff --git a/internal/pkg/sdk3rd/safeline/client.go b/internal/pkg/sdk3rd/safeline/client.go
index 4705d74e..efcd3bd6 100644
--- a/internal/pkg/sdk3rd/safeline/client.go
+++ b/internal/pkg/sdk3rd/safeline/client.go
@@ -11,19 +11,16 @@ import (
)
type Client struct {
- apiHost string
- apiToken string
-
client *resty.Client
}
func NewClient(apiHost, apiToken string) *Client {
- client := resty.New()
+ client := resty.New().
+ SetBaseURL(strings.TrimRight(apiHost, "/")).
+ SetHeader("X-SLCE-API-TOKEN", apiToken)
return &Client{
- apiHost: strings.TrimRight(apiHost, "/"),
- apiToken: apiToken,
- client: client,
+ client: client,
}
}
@@ -38,16 +35,14 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client {
}
func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, error) {
- url := c.apiHost + path
req := c.client.R().
SetHeader("Content-Type", "application/json").
- SetHeader("X-SLCE-API-TOKEN", c.apiToken).
SetBody(params)
- resp, err := req.Post(url)
+ resp, err := req.Post(path)
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, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("safeline api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -63,7 +58,7 @@ func (c *Client) sendRequestWithResult(path string, params interface{}, result B
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("safeline api error: failed to parse response: %w", err)
+ return fmt.Errorf("safeline api error: failed to unmarshal response: %w", err)
} else if errcode := result.GetErrCode(); errcode != nil && *errcode != "" {
if result.GetErrMsg() == nil {
return fmt.Errorf("safeline api error: code='%s'", *errcode)
diff --git a/internal/pkg/sdk3rd/upyun/console/api.go b/internal/pkg/sdk3rd/upyun/console/api.go
index 28ccd734..ce62d3a6 100644
--- a/internal/pkg/sdk3rd/upyun/console/api.go
+++ b/internal/pkg/sdk3rd/upyun/console/api.go
@@ -7,7 +7,11 @@ import (
"net/http"
)
-func (c *Client) getCookie() error {
+func (c *Client) ensureCookieExists() error {
+ if c.loginCookie != "" {
+ return nil
+ }
+
req := &signinRequest{Username: c.username, Password: c.password}
res, err := c.sendRequest(http.MethodPost, "/accounts/signin/", req)
if err != nil {
@@ -16,7 +20,7 @@ func (c *Client) getCookie() error {
resp := &signinResponse{}
if err := json.Unmarshal(res.Body(), &resp); err != nil {
- return fmt.Errorf("upyun api error: failed to parse response: %w", err)
+ return fmt.Errorf("upyun api error: failed to unmarshal response: %w", err)
} else if !resp.Data.Result {
return errors.New("upyun console signin failed")
}
@@ -27,10 +31,8 @@ func (c *Client) getCookie() error {
}
func (c *Client) UploadHttpsCertificate(req *UploadHttpsCertificateRequest) (*UploadHttpsCertificateResponse, error) {
- if c.loginCookie == "" {
- if err := c.getCookie(); err != nil {
- return nil, err
- }
+ if err := c.ensureCookieExists(); err != nil {
+ return nil, err
}
resp := &UploadHttpsCertificateResponse{}
@@ -39,10 +41,8 @@ func (c *Client) UploadHttpsCertificate(req *UploadHttpsCertificateRequest) (*Up
}
func (c *Client) GetHttpsCertificateManager(certificateId string) (*GetHttpsCertificateManagerResponse, error) {
- if c.loginCookie == "" {
- if err := c.getCookie(); err != nil {
- return nil, err
- }
+ if err := c.ensureCookieExists(); err != nil {
+ return nil, err
}
req := &GetHttpsCertificateManagerRequest{CertificateId: certificateId}
@@ -52,10 +52,8 @@ func (c *Client) GetHttpsCertificateManager(certificateId string) (*GetHttpsCert
}
func (c *Client) UpdateHttpsCertificateManager(req *UpdateHttpsCertificateManagerRequest) (*UpdateHttpsCertificateManagerResponse, error) {
- if c.loginCookie == "" {
- if err := c.getCookie(); err != nil {
- return nil, err
- }
+ if err := c.ensureCookieExists(); err != nil {
+ return nil, err
}
resp := &UpdateHttpsCertificateManagerResponse{}
@@ -64,10 +62,8 @@ func (c *Client) UpdateHttpsCertificateManager(req *UpdateHttpsCertificateManage
}
func (c *Client) GetHttpsServiceManager(domain string) (*GetHttpsServiceManagerResponse, error) {
- if c.loginCookie == "" {
- if err := c.getCookie(); err != nil {
- return nil, err
- }
+ if err := c.ensureCookieExists(); err != nil {
+ return nil, err
}
req := &GetHttpsServiceManagerRequest{Domain: domain}
@@ -77,10 +73,8 @@ func (c *Client) GetHttpsServiceManager(domain string) (*GetHttpsServiceManagerR
}
func (c *Client) MigrateHttpsDomain(req *MigrateHttpsDomainRequest) (*MigrateHttpsDomainResponse, error) {
- if c.loginCookie == "" {
- if err := c.getCookie(); err != nil {
- return nil, err
- }
+ if err := c.ensureCookieExists(); err != nil {
+ return nil, err
}
resp := &MigrateHttpsDomainResponse{}
diff --git a/internal/pkg/sdk3rd/upyun/console/client.go b/internal/pkg/sdk3rd/upyun/console/client.go
index 90d74bc3..b207549e 100644
--- a/internal/pkg/sdk3rd/upyun/console/client.go
+++ b/internal/pkg/sdk3rd/upyun/console/client.go
@@ -20,13 +20,21 @@ type Client struct {
}
func NewClient(username, password string) *Client {
- client := resty.New()
-
- return &Client{
+ client := &Client{
username: username,
password: password,
- client: client,
}
+ client.client = resty.New().
+ SetBaseURL("https://console.upyun.com").
+ SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
+ if client.loginCookie != "" {
+ req.Header.Set("Cookie", client.loginCookie)
+ }
+
+ return nil
+ })
+
+ return client
}
func (c *Client) WithTimeout(timeout time.Duration) *Client {
@@ -35,9 +43,7 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
}
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
- req := c.client.R().SetBasicAuth(c.username, c.password)
- req.Method = method
- req.URL = "https://console.upyun.com" + path
+ req := c.client.R()
if strings.EqualFold(method, http.MethodGet) {
qs := make(map[string]string)
if params != nil {
@@ -51,21 +57,16 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
}
}
- req = req.
- SetQueryParams(qs).
- SetHeader("Cookie", c.loginCookie)
+ req = req.SetQueryParams(qs)
} else {
- req = req.
- SetHeader("Content-Type", "application/json").
- SetHeader("Cookie", c.loginCookie).
- SetBody(params)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, resp: %s", resp.StatusCode(), resp.Body())
+ return resp, fmt.Errorf("upyun api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -81,12 +82,12 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
}
if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return fmt.Errorf("upyun api error: failed to parse response: %w", err)
+ return fmt.Errorf("upyun api error: failed to unmarshal response: %w", err)
}
tresp := &baseResponse{}
if err := json.Unmarshal(resp.Body(), &tresp); err != nil {
- return fmt.Errorf("upyun api error: failed to parse response: %w", err)
+ return fmt.Errorf("upyun api error: failed to unmarshal response: %w", err)
} else if tdata := tresp.GetData(); tdata == nil {
return fmt.Errorf("upyun api error: empty data")
} else if errcode := tdata.GetErrorCode(); errcode > 0 {
diff --git a/internal/pkg/sdk3rd/wangsu/cdn/api.go b/internal/pkg/sdk3rd/wangsu/cdn/api.go
index 0da647c8..997c05bf 100644
--- a/internal/pkg/sdk3rd/wangsu/cdn/api.go
+++ b/internal/pkg/sdk3rd/wangsu/cdn/api.go
@@ -1,70 +1,15 @@
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))
- })
+func (c *Client) BatchUpdateCertificateConfig(req *BatchUpdateCertificateConfigRequest) (*BatchUpdateCertificateConfigResponse, error) {
+ resp := &BatchUpdateCertificateConfigResponse{}
+ _, err := c.client.SendRequestWithResult(http.MethodPut, "/api/config/certificate/batch", req, resp)
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/models.go b/internal/pkg/sdk3rd/wangsu/cdn/models.go
index a9a9ec74..5bf934af 100644
--- a/internal/pkg/sdk3rd/wangsu/cdn/models.go
+++ b/internal/pkg/sdk3rd/wangsu/cdn/models.go
@@ -5,7 +5,7 @@ import (
)
type baseResponse struct {
- RequestId *string `json:"-"`
+ RequestId *string `json:"requestId,omitempty"`
Code *string `json:"code,omitempty"`
Message *string `json:"message,omitempty"`
}
@@ -16,93 +16,11 @@ 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 BatchUpdateCertificateConfigRequest struct {
+ CertificateId int64 `json:"certificateId" required:"true"`
+ DomainNames []string `json:"domainNames" required:"true"`
}
-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 {
+type BatchUpdateCertificateConfigResponse 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/cdnpro/api.go b/internal/pkg/sdk3rd/wangsu/cdnpro/api.go
new file mode 100644
index 00000000..c6f8da04
--- /dev/null
+++ b/internal/pkg/sdk3rd/wangsu/cdnpro/api.go
@@ -0,0 +1,70 @@
+package cdnpro
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+
+ "github.com/go-resty/resty/v2"
+)
+
+func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
+ resp := &CreateCertificateResponse{}
+ rres, 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 = rres.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{}
+ rres, 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 = rres.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{}
+ rres, err := c.client.SendRequestWithResult(http.MethodPost, "/cdn/deploymentTasks", req, resp)
+ if err != nil {
+ return resp, err
+ }
+
+ resp.DeploymentTaskUrl = rres.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/cdnpro/client.go b/internal/pkg/sdk3rd/wangsu/cdnpro/client.go
new file mode 100644
index 00000000..b5c0f530
--- /dev/null
+++ b/internal/pkg/sdk3rd/wangsu/cdnpro/client.go
@@ -0,0 +1,20 @@
+package cdnpro
+
+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/cdnpro/models.go b/internal/pkg/sdk3rd/wangsu/cdnpro/models.go
new file mode 100644
index 00000000..9cb1e648
--- /dev/null
+++ b/internal/pkg/sdk3rd/wangsu/cdnpro/models.go
@@ -0,0 +1,108 @@
+package cdnpro
+
+import (
+ "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/openapi"
+)
+
+type baseResponse struct {
+ RequestId *string `json:"requestId,omitempty"`
+ 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/certificate/api.go b/internal/pkg/sdk3rd/wangsu/certificate/api.go
new file mode 100644
index 00000000..037fb6e7
--- /dev/null
+++ b/internal/pkg/sdk3rd/wangsu/certificate/api.go
@@ -0,0 +1,42 @@
+package certificate
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+)
+
+func (c *Client) ListCertificates() (*ListCertificatesResponse, error) {
+ resp := &ListCertificatesResponse{}
+ _, err := c.client.SendRequestWithResult(http.MethodGet, "/api/certificate", nil, resp)
+ if err != nil {
+ return resp, err
+ }
+
+ return resp, err
+}
+
+func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
+ resp := &CreateCertificateResponse{}
+ rres, err := c.client.SendRequestWithResult(http.MethodPost, "/api/certificate", req, resp)
+ if err != nil {
+ return resp, err
+ }
+
+ resp.CertificateUrl = rres.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{}
+ _, err := c.client.SendRequestWithResult(http.MethodPut, fmt.Sprintf("/api/certificate/%s", url.PathEscape(certificateId)), req, resp)
+ if err != nil {
+ return resp, err
+ }
+
+ return resp, err
+}
diff --git a/internal/pkg/sdk3rd/wangsu/certificate/client.go b/internal/pkg/sdk3rd/wangsu/certificate/client.go
new file mode 100644
index 00000000..19f4cfaa
--- /dev/null
+++ b/internal/pkg/sdk3rd/wangsu/certificate/client.go
@@ -0,0 +1,20 @@
+package certificate
+
+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/certificate/models.go b/internal/pkg/sdk3rd/wangsu/certificate/models.go
new file mode 100644
index 00000000..4e882e7c
--- /dev/null
+++ b/internal/pkg/sdk3rd/wangsu/certificate/models.go
@@ -0,0 +1,52 @@
+package certificate
+
+import (
+ "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/openapi"
+)
+
+type baseResponse struct {
+ RequestId *string `json:"requestId,omitempty"`
+ 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 CreateCertificateRequest struct {
+ Name *string `json:"name,omitempty" required:"true"`
+ Certificate *string `json:"certificate,omitempty" required:"true"`
+ PrivateKey *string `json:"privateKey,omitempty"`
+ Comment *string `json:"comment,omitempty" `
+}
+
+type CreateCertificateResponse struct {
+ baseResponse
+ CertificateUrl string `json:"location,omitempty"`
+}
+
+type UpdateCertificateRequest struct {
+ Name *string `json:"name,omitempty" required:"true"`
+ Certificate *string `json:"certificate,omitempty"`
+ PrivateKey *string `json:"privateKey,omitempty"`
+ Comment *string `json:"comment,omitempty" `
+}
+
+type UpdateCertificateResponse struct {
+ baseResponse
+}
+
+type ListCertificatesResponse struct {
+ baseResponse
+ Certificates []*struct {
+ CertificateId string `json:"certificate-id"`
+ Name string `json:"name"`
+ Comment string `json:"comment"`
+ ValidityFrom string `json:"certificate-validity-from"`
+ ValidityTo string `json:"certificate-validity-to"`
+ Serial string `json:"certificate-serial"`
+ } `json:"ssl-certificates,omitempty"`
+}
diff --git a/internal/pkg/sdk3rd/wangsu/openapi/client.go b/internal/pkg/sdk3rd/wangsu/openapi/client.go
index d63c20c7..a8f4f2af 100644
--- a/internal/pkg/sdk3rd/wangsu/openapi/client.go
+++ b/internal/pkg/sdk3rd/wangsu/openapi/client.go
@@ -134,8 +134,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
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 {
@@ -151,18 +149,20 @@ func (c *Client) sendRequest(method string, path string, params interface{}, con
req = req.SetQueryParams(qs)
} else {
- req = req.SetBody(params)
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
}
- for _, fn := range configureReq {
- fn(req)
+ if configureReq != nil {
+ for _, fn := range configureReq {
+ fn(req)
+ }
}
- resp, err := req.Send()
+ resp, err := req.Execute(method, path)
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, fmt.Errorf("wangsu api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
}
return resp, nil
@@ -181,7 +181,7 @@ func (c *Client) SendRequestWithResult(method string, path string, params interf
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)
+ return resp, fmt.Errorf("wangsu api error: failed to unmarshal response: %w", err)
}
}
diff --git a/internal/pkg/utils/map/getter.go b/internal/pkg/utils/map/getter.go
index f30f6d33..512da3ee 100644
--- a/internal/pkg/utils/map/getter.go
+++ b/internal/pkg/utils/map/getter.go
@@ -68,31 +68,42 @@ func GetOrDefaultInt32(dict map[string]any, key string, defaultValue int32) int3
}
if value, ok := dict[key]; ok {
- if result, ok := value.(int32); ok {
- if result != 0 {
- return result
+ var result int32
+
+ switch v := value.(type) {
+ case int:
+ result = int32(v)
+ case int8:
+ result = int32(v)
+ case int16:
+ result = int32(v)
+ case int32:
+ result = v
+ case int64:
+ result = int32(v)
+ case uint:
+ result = int32(v)
+ case uint8:
+ result = int32(v)
+ case uint16:
+ result = int32(v)
+ case uint32:
+ result = int32(v)
+ case uint64:
+ result = int32(v)
+ case float32:
+ result = int32(v)
+ case float64:
+ result = int32(v)
+ case string:
+ // 兼容字符串类型的值
+ if t, err := strconv.ParseInt(v, 10, 32); err == nil {
+ result = int32(t)
}
}
- 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 {
- if result != 0 {
- return int32(result)
- }
- }
+ if result != 0 {
+ return int32(result)
}
}
@@ -126,31 +137,42 @@ func GetOrDefaultInt64(dict map[string]any, key string, defaultValue int64) int6
}
if value, ok := dict[key]; ok {
- if result, ok := value.(int64); ok {
- if result != 0 {
- return result
+ var result int64
+
+ switch v := value.(type) {
+ case int:
+ result = int64(v)
+ case int8:
+ result = int64(v)
+ case int16:
+ result = int64(v)
+ case int32:
+ result = int64(v)
+ case int64:
+ result = v
+ case uint:
+ result = int64(v)
+ case uint8:
+ result = int64(v)
+ case uint16:
+ result = int64(v)
+ case uint32:
+ result = int64(v)
+ case uint64:
+ result = int64(v)
+ case float32:
+ result = int64(v)
+ case float64:
+ result = int64(v)
+ case string:
+ // 兼容字符串类型的值
+ if t, err := strconv.ParseInt(v, 10, 32); err == nil {
+ result = t
}
}
- if result, ok := value.(int32); ok {
- if result != 0 {
- return int64(result)
- }
- }
-
- 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 {
- if result != 0 {
- return result
- }
- }
+ if result != 0 {
+ return int64(result)
}
}
diff --git a/internal/repository/certificate.go b/internal/repository/certificate.go
index 13d2c094..95bfd713 100644
--- a/internal/repository/certificate.go
+++ b/internal/repository/certificate.go
@@ -101,7 +101,7 @@ func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Ce
record.Set("serialNumber", certificate.SerialNumber)
record.Set("certificate", certificate.Certificate)
record.Set("privateKey", certificate.PrivateKey)
- record.Set("issuer", certificate.Issuer)
+ record.Set("issuerOrg", certificate.IssuerOrg)
record.Set("issuerCertificate", certificate.IssuerCertificate)
record.Set("keyAlgorithm", string(certificate.KeyAlgorithm))
record.Set("effectAt", certificate.EffectAt)
@@ -162,7 +162,7 @@ func (r *CertificateRepository) castRecordToModel(record *core.Record) (*domain.
SerialNumber: record.GetString("serialNumber"),
Certificate: record.GetString("certificate"),
PrivateKey: record.GetString("privateKey"),
- Issuer: record.GetString("issuer"),
+ IssuerOrg: record.GetString("issuerOrg"),
IssuerCertificate: record.GetString("issuerCertificate"),
KeyAlgorithm: domain.CertificateKeyAlgorithmType(record.GetString("keyAlgorithm")),
EffectAt: record.GetDateTime("effectAt").Time(),
diff --git a/internal/workflow/node-processor/apply_node.go b/internal/workflow/node-processor/apply_node.go
index 97b7575d..ff8c573d 100644
--- a/internal/workflow/node-processor/apply_node.go
+++ b/internal/workflow/node-processor/apply_node.go
@@ -66,14 +66,14 @@ func (n *applyNode) Process(ctx context.Context) error {
}
// 解析证书并生成实体
- certX509, err := certutil.ParseCertificateFromPEM(applyResult.CertificateFullChain)
+ certX509, err := certutil.ParseCertificateFromPEM(applyResult.FullChainCertificate)
if err != nil {
n.logger.Warn("failed to parse certificate, may be the CA responded error")
return err
}
certificate := &domain.Certificate{
Source: domain.CertificateSourceTypeWorkflow,
- Certificate: applyResult.CertificateFullChain,
+ Certificate: applyResult.FullChainCertificate,
PrivateKey: applyResult.PrivateKey,
IssuerCertificate: applyResult.IssuerCertificate,
ACMEAccountUrl: applyResult.ACMEAccountUrl,
diff --git a/migrations/1747389600_upgrade.go b/migrations/1747389600_upgrade.go
new file mode 100644
index 00000000..a145679a
--- /dev/null
+++ b/migrations/1747389600_upgrade.go
@@ -0,0 +1,73 @@
+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 `certificate`
+ {
+ collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np")
+ if err != nil {
+ return err
+ }
+
+ if err := collection.Fields.AddMarshaledJSONAt(6, []byte(`{
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text2910474005",
+ "max": 0,
+ "min": 0,
+ "name": "issuerOrg",
+ "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") == "1panel" {
+ config := make(map[string]any)
+ if err := access.UnmarshalJSONField("config", &config); err != nil {
+ return err
+ }
+
+ config["apiVersion"] = "v1"
+ 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/public/imgs/providers/acmeca.svg b/ui/public/imgs/providers/acmeca.svg
new file mode 100644
index 00000000..260530c9
--- /dev/null
+++ b/ui/public/imgs/providers/acmeca.svg
@@ -0,0 +1 @@
+
diff --git a/ui/public/imgs/providers/baotawaf.svg b/ui/public/imgs/providers/baotawaf.svg
new file mode 100644
index 00000000..34ab8ec8
--- /dev/null
+++ b/ui/public/imgs/providers/baotawaf.svg
@@ -0,0 +1 @@
+
diff --git a/ui/public/imgs/providers/flexcdn.png b/ui/public/imgs/providers/flexcdn.png
new file mode 100644
index 00000000..00805598
Binary files /dev/null and b/ui/public/imgs/providers/flexcdn.png differ
diff --git a/ui/public/imgs/providers/lecdn.svg b/ui/public/imgs/providers/lecdn.svg
new file mode 100644
index 00000000..f9c18fa7
--- /dev/null
+++ b/ui/public/imgs/providers/lecdn.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ui/public/imgs/providers/ratpanel.png b/ui/public/imgs/providers/ratpanel.png
new file mode 100644
index 00000000..f89808dd
Binary files /dev/null and b/ui/public/imgs/providers/ratpanel.png differ
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index 0dd2828a..fdf4f93f 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -12,6 +12,7 @@ import { ACCESS_PROVIDERS, ACCESS_USAGES, type AccessProvider } from "@/domain/p
import { useAntdForm, useAntdFormName } from "@/hooks";
import AccessForm1PanelConfig from "./AccessForm1PanelConfig";
+import AccessFormACMECAConfig from "./AccessFormACMECAConfig";
import AccessFormACMEHttpReqConfig from "./AccessFormACMEHttpReqConfig";
import AccessFormAliyunConfig from "./AccessFormAliyunConfig";
import AccessFormAWSConfig from "./AccessFormAWSConfig";
@@ -19,6 +20,7 @@ import AccessFormAzureConfig from "./AccessFormAzureConfig";
import AccessFormBaiduCloudConfig from "./AccessFormBaiduCloudConfig";
import AccessFormBaishanConfig from "./AccessFormBaishanConfig";
import AccessFormBaotaPanelConfig from "./AccessFormBaotaPanelConfig";
+import AccessFormBaotaWAFConfig from "./AccessFormBaotaWAFConfig";
import AccessFormBunnyConfig from "./AccessFormBunnyConfig";
import AccessFormBytePlusConfig from "./AccessFormBytePlusConfig";
import AccessFormCacheFlyConfig from "./AccessFormCacheFlyConfig";
@@ -33,6 +35,7 @@ import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig";
import AccessFormDynv6Config from "./AccessFormDynv6Config";
import AccessFormEdgioConfig from "./AccessFormEdgioConfig";
import AccessFormEmailConfig from "./AccessFormEmailConfig";
+import AccessFormFlexCDNConfig from "./AccessFormFlexCDNConfig";
import AccessFormGcoreConfig from "./AccessFormGcoreConfig";
import AccessFormGnameConfig from "./AccessFormGnameConfig";
import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig";
@@ -42,6 +45,7 @@ import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig";
import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig";
import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig";
import AccessFormLarkBotConfig from "./AccessFormLarkBotConfig";
+import AccessFormLeCDNConfig from "./AccessFormLeCDNConfig";
import AccessFormMattermostConfig from "./AccessFormMattermostConfig";
import AccessFormNamecheapConfig from "./AccessFormNamecheapConfig";
import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig";
@@ -54,10 +58,11 @@ import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig";
import AccessFormProxmoxVEConfig from "./AccessFormProxmoxVEConfig";
import AccessFormQiniuConfig from "./AccessFormQiniuConfig";
import AccessFormRainYunConfig from "./AccessFormRainYunConfig";
+import AccessFormRatPanelConfig from "./AccessFormRatPanelConfig";
import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig";
import AccessFormSSHConfig from "./AccessFormSSHConfig";
import AccessFormSSLComConfig from "./AccessFormSSLComConfig";
-import AccessFormTelegramConfig from "./AccessFormTelegramConfig";
+import AccessFormTelegramBotConfig from "./AccessFormTelegramBotConfig";
import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig";
import AccessFormUCloudConfig from "./AccessFormUCloudConfig";
import AccessFormUpyunConfig from "./AccessFormUpyunConfig";
@@ -176,6 +181,8 @@ const AccessForm = forwardRef(({ className,
switch (fieldProvider) {
case ACCESS_PROVIDERS["1PANEL"]:
return ;
+ case ACCESS_PROVIDERS.ACMECA:
+ return ;
case ACCESS_PROVIDERS.ACMEHTTPREQ:
return ;
case ACCESS_PROVIDERS.ALIYUN:
@@ -190,6 +197,8 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.BAOTAPANEL:
return ;
+ case ACCESS_PROVIDERS.BAOTAWAF:
+ return ;
case ACCESS_PROVIDERS.BUNNY:
return ;
case ACCESS_PROVIDERS.BYTEPLUS:
@@ -214,6 +223,12 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.DYNV6:
return ;
+ case ACCESS_PROVIDERS.EDGIO:
+ return ;
+ case ACCESS_PROVIDERS.EMAIL:
+ return ;
+ case ACCESS_PROVIDERS.FLEXCDN:
+ return ;
case ACCESS_PROVIDERS.GCORE:
return ;
case ACCESS_PROVIDERS.GNAME:
@@ -224,10 +239,6 @@ const AccessForm = forwardRef(({ className,
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:
@@ -236,6 +247,8 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.LARKBOT:
return ;
+ case ACCESS_PROVIDERS.LECDN:
+ return ;
case ACCESS_PROVIDERS.MATTERMOST:
return ;
case ACCESS_PROVIDERS.NAMECHEAP:
@@ -260,12 +273,14 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.RAINYUN:
return ;
+ case ACCESS_PROVIDERS.RATPANEL:
+ return ;
case ACCESS_PROVIDERS.SAFELINE:
return ;
case ACCESS_PROVIDERS.SSH:
return ;
- case ACCESS_PROVIDERS.TELEGRAM:
- return ;
+ case ACCESS_PROVIDERS.TELEGRAMBOT:
+ return ;
case ACCESS_PROVIDERS.SSLCOM:
return ;
case ACCESS_PROVIDERS.TENCENTCLOUD:
diff --git a/ui/src/components/access/AccessForm1PanelConfig.tsx b/ui/src/components/access/AccessForm1PanelConfig.tsx
index c0762bbd..29481f15 100644
--- a/ui/src/components/access/AccessForm1PanelConfig.tsx
+++ b/ui/src/components/access/AccessForm1PanelConfig.tsx
@@ -1,5 +1,5 @@
import { useTranslation } from "react-i18next";
-import { Form, type FormInstance, Input, Switch } from "antd";
+import { Form, type FormInstance, Input, Select, Switch } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
@@ -18,6 +18,7 @@ export type AccessForm1PanelConfigProps = {
const initFormModel = (): AccessForm1PanelConfigFieldValues => {
return {
apiUrl: "http://:20410/",
+ apiVersion: "v1",
apiKey: "",
};
};
@@ -27,6 +28,7 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
+ apiVersion: z.string().nonempty(t("access.form.1panel_api_version.placeholder")),
apiKey: z
.string()
.min(1, t("access.form.1panel_api_key.placeholder"))
@@ -53,6 +55,10 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal
+
+
+
;
+
+export type AccessFormACMECAConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormACMECAConfigFieldValues;
+ onValuesChange?: (values: AccessFormACMECAConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormACMECAConfigFieldValues => {
+ return {
+ endpoint: "https://example.com/acme/directory",
+ };
+};
+
+const AccessFormACMECAConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormACMECAConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ endpoint: z.string().url(t("common.errmsg.url_invalid")),
+ eabKid: z.string().trim().nullish(),
+ eabHmacKey: z.string().trim().nullish(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ );
+};
+
+export default AccessFormACMECAConfig;
diff --git a/ui/src/components/access/AccessFormACMEHttpReqConfig.tsx b/ui/src/components/access/AccessFormACMEHttpReqConfig.tsx
index 03cf163a..57cbc22d 100644
--- a/ui/src/components/access/AccessFormACMEHttpReqConfig.tsx
+++ b/ui/src/components/access/AccessFormACMEHttpReqConfig.tsx
@@ -84,7 +84,7 @@ const AccessFormACMEHttpReqConfig = ({ form: formInst, formName, disabled, initi
rules={[formRule]}
tooltip={}
>
-
+
}
>
-
+
);
diff --git a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx
index d03c0f1b..dd355b5e 100644
--- a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx
+++ b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx
@@ -27,11 +27,7 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia
const formSchema = z.object({
apiUrl: z.string().url(t("common.errmsg.url_invalid")),
- apiKey: z
- .string()
- .min(1, t("access.form.baotapanel_api_key.placeholder"))
- .max(64, t("common.errmsg.string_max", { max: 64 }))
- .trim(),
+ apiKey: z.string().nonempty(t("access.form.baotapanel_api_key.placeholder")).trim(),
allowInsecureConnections: z.boolean().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
diff --git a/ui/src/components/access/AccessFormBaotaWAFConfig.tsx b/ui/src/components/access/AccessFormBaotaWAFConfig.tsx
new file mode 100644
index 00000000..e87ed596
--- /dev/null
+++ b/ui/src/components/access/AccessFormBaotaWAFConfig.tsx
@@ -0,0 +1,71 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Switch } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { type AccessConfigForBaotaWAF } from "@/domain/access";
+
+type AccessFormBaotaWAFConfigFieldValues = Nullish;
+
+export type AccessFormBaotaWAFConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormBaotaWAFConfigFieldValues;
+ onValuesChange?: (values: AccessFormBaotaWAFConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormBaotaWAFConfigFieldValues => {
+ return {
+ apiUrl: "http://:8379/",
+ apiKey: "",
+ };
+};
+
+const AccessFormBaotaWAFConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormBaotaWAFConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ apiUrl: z.string().url(t("common.errmsg.url_invalid")),
+ apiKey: z.string().nonempty(t("access.form.baotawaf_api_key.placeholder")).trim(),
+ allowInsecureConnections: z.boolean().nullish(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+ }
+ >
+
+
+
+
+
+
+
+ );
+};
+
+export default AccessFormBaotaWAFConfig;
diff --git a/ui/src/components/access/AccessFormCloudflareConfig.tsx b/ui/src/components/access/AccessFormCloudflareConfig.tsx
index a06d753d..79b33e3a 100644
--- a/ui/src/components/access/AccessFormCloudflareConfig.tsx
+++ b/ui/src/components/access/AccessFormCloudflareConfig.tsx
@@ -66,7 +66,7 @@ const AccessFormCloudflareConfig = ({ form: formInst, formName, disabled, initia
rules={[formRule]}
tooltip={}
>
-
+
);
diff --git a/ui/src/components/access/AccessFormFlexCDNConfig.tsx b/ui/src/components/access/AccessFormFlexCDNConfig.tsx
new file mode 100644
index 00000000..6ca020bf
--- /dev/null
+++ b/ui/src/components/access/AccessFormFlexCDNConfig.tsx
@@ -0,0 +1,90 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Radio, Switch } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { type AccessConfigForFlexCDN } from "@/domain/access";
+
+type AccessFormFlexCDNConfigFieldValues = Nullish;
+
+export type AccessFormFlexCDNConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormFlexCDNConfigFieldValues;
+ onValuesChange?: (values: AccessFormFlexCDNConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormFlexCDNConfigFieldValues => {
+ return {
+ apiUrl: "http://:8000/",
+ apiRole: "user",
+ accessKeyId: "",
+ accessKey: "",
+ };
+};
+
+const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormFlexCDNConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ apiUrl: z.string().url(t("common.errmsg.url_invalid")),
+ role: z.union([z.literal("user"), z.literal("admin")], {
+ message: t("access.form.flexcdn_api_role.placeholder"),
+ }),
+ accessKeyId: z.string().nonempty(t("access.form.flexcdn_access_key_id.placeholder")).trim(),
+ accessKey: z.string().nonempty(t("access.form.flexcdn_access_key.placeholder")).trim(),
+ allowInsecureConnections: z.boolean().nullish(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+
+ ({ label: t(`access.form.flexcdn_api_role.option.${s}.label`), value: s }))} />
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+
+
+
+
+ );
+};
+
+export default AccessFormFlexCDNConfig;
diff --git a/ui/src/components/access/AccessFormGoEdgeConfig.tsx b/ui/src/components/access/AccessFormGoEdgeConfig.tsx
index ced9b09a..9c03f2be 100644
--- a/ui/src/components/access/AccessFormGoEdgeConfig.tsx
+++ b/ui/src/components/access/AccessFormGoEdgeConfig.tsx
@@ -32,16 +32,8 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal
role: z.union([z.literal("user"), z.literal("admin")], {
message: t("access.form.goedge_api_role.placeholder"),
}),
- 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(),
+ accessKeyId: z.string().nonempty(t("access.form.goedge_access_key_id.placeholder")).trim(),
+ accessKey: z.string().nonempty(t("access.form.goedge_access_key.placeholder")).trim(),
allowInsecureConnections: z.boolean().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
diff --git a/ui/src/components/access/AccessFormLeCDNConfig.tsx b/ui/src/components/access/AccessFormLeCDNConfig.tsx
new file mode 100644
index 00000000..4af5a639
--- /dev/null
+++ b/ui/src/components/access/AccessFormLeCDNConfig.tsx
@@ -0,0 +1,85 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Radio, Select, Switch } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { type AccessConfigForLeCDN } from "@/domain/access";
+
+type AccessFormLeCDNConfigFieldValues = Nullish;
+
+export type AccessFormLeCDNConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormLeCDNConfigFieldValues;
+ onValuesChange?: (values: AccessFormLeCDNConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormLeCDNConfigFieldValues => {
+ return {
+ apiUrl: "http://:5090/",
+ apiVersion: "v3",
+ apiRole: "user",
+ username: "",
+ password: "",
+ };
+};
+
+const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormLeCDNConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ apiUrl: z.string().url(t("common.errmsg.url_invalid")),
+ role: z.union([z.literal("client"), z.literal("master")], {
+ message: t("access.form.lecdn_api_role.placeholder"),
+ }),
+ username: z.string().nonempty(t("access.form.lecdn_username.placeholder")).trim(),
+ password: z.string().nonempty(t("access.form.lecdn_password.placeholder")).trim(),
+ allowInsecureConnections: z.boolean().nullish(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ ({ label: t(`access.form.lecdn_api_role.option.${s}.label`), value: s }))} />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AccessFormLeCDNConfig;
diff --git a/ui/src/components/access/AccessFormProxmoxVEConfig.tsx b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx
index afdc02de..d0a66745 100644
--- a/ui/src/components/access/AccessFormProxmoxVEConfig.tsx
+++ b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx
@@ -65,7 +65,7 @@ const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initial
rules={[formRule]}
tooltip={}
>
-
+
diff --git a/ui/src/components/access/AccessFormRatPanelConfig.tsx b/ui/src/components/access/AccessFormRatPanelConfig.tsx
new file mode 100644
index 00000000..ca3d2182
--- /dev/null
+++ b/ui/src/components/access/AccessFormRatPanelConfig.tsx
@@ -0,0 +1,82 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Switch } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { type AccessConfigForRatPanel } from "@/domain/access";
+
+type AccessFormRatPanelConfigFieldValues = Nullish;
+
+export type AccessFormRatPanelConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormRatPanelConfigFieldValues;
+ onValuesChange?: (values: AccessFormRatPanelConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormRatPanelConfigFieldValues => {
+ return {
+ apiUrl: "http://:8888/",
+ accessTokenId: 1,
+ accessToken: "",
+ };
+};
+
+const AccessFormRatPanelConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormRatPanelConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ apiUrl: z.string().url(t("common.errmsg.url_invalid")),
+ accessTokenId: z.preprocess((v) => Number(v), z.number().positive(t("access.form.ratpanel_access_token_id.placeholder"))),
+ accessToken: z.string().nonempty(t("access.form.ratpanel_access_token.placeholder")).trim(),
+ allowInsecureConnections: z.boolean().nullish(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+
+
+
+
+ );
+};
+
+export default AccessFormRatPanelConfig;
diff --git a/ui/src/components/access/AccessFormSSHConfig.tsx b/ui/src/components/access/AccessFormSSHConfig.tsx
index db1790a4..32d1b8bc 100644
--- a/ui/src/components/access/AccessFormSSHConfig.tsx
+++ b/ui/src/components/access/AccessFormSSHConfig.tsx
@@ -1,5 +1,6 @@
import { useTranslation } from "react-i18next";
-import { Form, type FormInstance, Input, InputNumber } from "antd";
+import { ArrowDownOutlined, ArrowUpOutlined, CloseOutlined, PlusOutlined } from "@ant-design/icons";
+import { Button, Collapse, Form, type FormInstance, Input, InputNumber, Space } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
@@ -39,7 +40,7 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
),
username: z
.string()
- .min(1, "access.form.ssh_username.placeholder")
+ .min(1, t("access.form.ssh_username.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 })),
password: z
.string()
@@ -54,6 +55,46 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
.nullish()
.refine((v) => !v || formInst.getFieldValue("key"), t("access.form.ssh_key.placeholder")),
+ jumpServers: z
+ .array(
+ z
+ .object({
+ 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, t("access.form.ssh_username.placeholder"))
+ .max(64, t("common.errmsg.string_max", { max: 64 })),
+ password: z
+ .string()
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .nullish(),
+ key: z
+ .string()
+ .max(20480, t("common.errmsg.string_max", { max: 20480 }))
+ .nullish(),
+ keyPassphrase: z
+ .string()
+ .max(20480, t("common.errmsg.string_max", { max: 20480 }))
+ .nullish(),
+ })
+ .superRefine((data, ctx) => {
+ if (data.keyPassphrase && !data.key) {
+ ctx.addIssue({
+ path: ["keyPassphrase"],
+ code: z.ZodIssueCode.custom,
+ message: t("access.form.ssh_key.placeholder"),
+ });
+ }
+ })
+ )
+ .nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
@@ -114,6 +155,123 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
>
+
+
+
+ {(fields, { add, remove, move }) => (
+
+ {fields?.length > 0 ? (
+ {
+ const Label = () => {
+ const host = Form.useWatch(["jumpServers", field.name, "host"], formInst);
+ const port = Form.useWatch(["jumpServers", field.name, "port"], formInst);
+ const addr = !!host && !!port ? `${host}:${port}` : host ? host : port ? `:${port}` : "unknown";
+ return (
+
+ [{t("access.form.ssh_jump_servers.item.label")} {field.name + 1}] {addr}
+
+ );
+ };
+
+ return {
+ key: field.key,
+ label: ,
+ extra: (
+
+ }
+ color="default"
+ disabled={disabled || index === 0}
+ size="small"
+ type="text"
+ onClick={(e) => {
+ move(index, index - 1);
+ e.stopPropagation();
+ }}
+ />
+ }
+ color="default"
+ disabled={disabled || index === fields.length - 1}
+ size="small"
+ type="text"
+ onClick={(e) => {
+ move(index, index + 1);
+ e.stopPropagation();
+ }}
+ />
+ }
+ color="default"
+ disabled={disabled}
+ size="small"
+ type="text"
+ onClick={(e) => {
+ remove(field.name);
+ e.stopPropagation();
+ }}
+ />
+
+ ),
+ children: (
+ <>
+
+
+
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+ >
+ ),
+ };
+ })}
+ />
+ ) : null}
+ } onClick={() => add()}>
+ {t("access.form.ssh_jump_servers.add")}
+
+
+ )}
+
+
);
};
diff --git a/ui/src/components/access/AccessFormTelegramConfig.tsx b/ui/src/components/access/AccessFormTelegramBotConfig.tsx
similarity index 65%
rename from ui/src/components/access/AccessFormTelegramConfig.tsx
rename to ui/src/components/access/AccessFormTelegramBotConfig.tsx
index a4eccafb..3181c71d 100644
--- a/ui/src/components/access/AccessFormTelegramConfig.tsx
+++ b/ui/src/components/access/AccessFormTelegramBotConfig.tsx
@@ -3,25 +3,25 @@ import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
-import { type AccessConfigForTelegram } from "@/domain/access";
+import { type AccessConfigForTelegramBot } from "@/domain/access";
-type AccessFormTelegramConfigFieldValues = Nullish;
+type AccessFormTelegramBotConfigFieldValues = Nullish;
-export type AccessFormTelegramConfigProps = {
+export type AccessFormTelegramBotConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
- initialValues?: AccessFormTelegramConfigFieldValues;
- onValuesChange?: (values: AccessFormTelegramConfigFieldValues) => void;
+ initialValues?: AccessFormTelegramBotConfigFieldValues;
+ onValuesChange?: (values: AccessFormTelegramBotConfigFieldValues) => void;
};
-const initFormModel = (): AccessFormTelegramConfigFieldValues => {
+const initFormModel = (): AccessFormTelegramBotConfigFieldValues => {
return {
botToken: "",
};
};
-const AccessFormTelegramConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormTelegramConfigProps) => {
+const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormTelegramBotConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
@@ -37,8 +37,8 @@ const AccessFormTelegramConfig = ({ form: formInst, formName, disabled, initialV
.nullish()
.refine((v) => {
if (v == null || v + "" === "") return true;
- return /^\d+$/.test(v + "") && +v! > 0;
- }, t("access.form.telegram_default_chat_id.placeholder"))
+ return !Number.isNaN(+v!) && +v! !== 0;
+ }, t("access.form.telegram_bot_default_chat_id.placeholder"))
)
.nullish(),
});
@@ -68,14 +68,14 @@ const AccessFormTelegramConfig = ({ form: formInst, formName, disabled, initialV
}
+ tooltip={}
>
-
+
);
};
-export default AccessFormTelegramConfig;
+export default AccessFormTelegramBotConfig;
diff --git a/ui/src/components/access/AccessFormUCloudConfig.tsx b/ui/src/components/access/AccessFormUCloudConfig.tsx
index 495d21b9..fd623925 100644
--- a/ui/src/components/access/AccessFormUCloudConfig.tsx
+++ b/ui/src/components/access/AccessFormUCloudConfig.tsx
@@ -81,7 +81,7 @@ const AccessFormUCloudConfig = ({ form: formInst, formName, disabled, initialVal
rules={[formRule]}
tooltip={}
>
-
+
);
diff --git a/ui/src/components/access/AccessFormVercelConfig.tsx b/ui/src/components/access/AccessFormVercelConfig.tsx
index b1ed7b6f..4483a9f9 100644
--- a/ui/src/components/access/AccessFormVercelConfig.tsx
+++ b/ui/src/components/access/AccessFormVercelConfig.tsx
@@ -66,7 +66,7 @@ const AccessFormVercelConfig = ({ form: formInst, formName, disabled, initialVal
rules={[formRule]}
tooltip={}
>
-
+
);
diff --git a/ui/src/components/access/AccessFormWebhookConfig.tsx b/ui/src/components/access/AccessFormWebhookConfig.tsx
index 69286aa8..6e6ec87a 100644
--- a/ui/src/components/access/AccessFormWebhookConfig.tsx
+++ b/ui/src/components/access/AccessFormWebhookConfig.tsx
@@ -67,7 +67,6 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
}
return true;
}, t("access.form.webhook_headers.errmsg.invalid")),
- allowInsecureConnections: z.boolean().nullish(),
defaultDataForDeployment: z
.string()
.nullish()
@@ -96,11 +95,12 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
return false;
}
}, t("access.form.webhook_default_data.errmsg.json_invalid")),
+ allowInsecureConnections: z.boolean().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
- const handleWebhookHeadersBlur = (e: React.FocusEvent) => {
- let value = e.target.value;
+ const handleWebhookHeadersBlur = () => {
+ let value = formInst.getFieldValue("headers");
value = value.trim();
value = value.replace(/(?",
- device_keys: "",
+ device_key: "",
},
null,
2
@@ -280,7 +279,13 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
rules={[formRule]}
tooltip={}
>
-
+
diff --git a/ui/src/components/certificate/CertificateDetail.tsx b/ui/src/components/certificate/CertificateDetail.tsx
index 1023bf16..2fc0d9d0 100644
--- a/ui/src/components/certificate/CertificateDetail.tsx
+++ b/ui/src/components/certificate/CertificateDetail.tsx
@@ -42,7 +42,7 @@ const CertificateDetail = ({ data, ...props }: CertificateDetailProps) => {
-
+
diff --git a/ui/src/components/provider/DeploymentProviderPicker.tsx b/ui/src/components/provider/DeploymentProviderPicker.tsx
index 0ea5a97c..b1bcd6fe 100644
--- a/ui/src/components/provider/DeploymentProviderPicker.tsx
+++ b/ui/src/components/provider/DeploymentProviderPicker.tsx
@@ -72,8 +72,10 @@ const DeploymentProviderPicker = ({ className, style, autoFocus, filter, placeho
DEPLOYMENT_CATEGORIES.LOADBALANCE,
DEPLOYMENT_CATEGORIES.FIREWALL,
DEPLOYMENT_CATEGORIES.AV,
+ DEPLOYMENT_CATEGORIES.APIGATEWAY,
DEPLOYMENT_CATEGORIES.SERVERLESS,
DEPLOYMENT_CATEGORIES.WEBSITE,
+ DEPLOYMENT_CATEGORIES.SSL,
DEPLOYMENT_CATEGORIES.NAS,
DEPLOYMENT_CATEGORIES.OTHER,
].map((key) => ({
diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
index 9e4ade18..49ed12ec 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
@@ -27,6 +27,7 @@ import DeployNodeConfigFormAliyunDCDNConfig from "./DeployNodeConfigFormAliyunDC
import DeployNodeConfigFormAliyunDDoSConfig from "./DeployNodeConfigFormAliyunDDoSConfig";
import DeployNodeConfigFormAliyunESAConfig from "./DeployNodeConfigFormAliyunESAConfig";
import DeployNodeConfigFormAliyunFCConfig from "./DeployNodeConfigFormAliyunFCConfig";
+import DeployNodeConfigFormAliyunGAConfig from "./DeployNodeConfigFormAliyunGAConfig";
import DeployNodeConfigFormAliyunLiveConfig from "./DeployNodeConfigFormAliyunLiveConfig";
import DeployNodeConfigFormAliyunNLBConfig from "./DeployNodeConfigFormAliyunNLBConfig";
import DeployNodeConfigFormAliyunOSSConfig from "./DeployNodeConfigFormAliyunOSSConfig";
@@ -41,11 +42,13 @@ import DeployNodeConfigFormBaiduCloudCDNConfig from "./DeployNodeConfigFormBaidu
import DeployNodeConfigFormBaishanCDNConfig from "./DeployNodeConfigFormBaishanCDNConfig";
import DeployNodeConfigFormBaotaPanelConsoleConfig from "./DeployNodeConfigFormBaotaPanelConsoleConfig";
import DeployNodeConfigFormBaotaPanelSiteConfig from "./DeployNodeConfigFormBaotaPanelSiteConfig";
+import DeployNodeConfigFormBaotaWAFSiteConfig from "./DeployNodeConfigFormBaotaWAFSiteConfig";
import DeployNodeConfigFormBunnyCDNConfig from "./DeployNodeConfigFormBunnyCDNConfig.tsx";
import DeployNodeConfigFormBytePlusCDNConfig from "./DeployNodeConfigFormBytePlusCDNConfig";
import DeployNodeConfigFormCdnflyConfig from "./DeployNodeConfigFormCdnflyConfig";
import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig";
import DeployNodeConfigFormEdgioApplicationsConfig from "./DeployNodeConfigFormEdgioApplicationsConfig";
+import DeployNodeConfigFormFlexCDNConfig from "./DeployNodeConfigFormFlexCDNConfig";
import DeployNodeConfigFormGcoreCDNConfig from "./DeployNodeConfigFormGcoreCDNConfig";
import DeployNodeConfigFormGoEdgeConfig from "./DeployNodeConfigFormGoEdgeConfig";
import DeployNodeConfigFormHuaweiCloudCDNConfig from "./DeployNodeConfigFormHuaweiCloudCDNConfig";
@@ -56,6 +59,7 @@ import DeployNodeConfigFormJDCloudCDNConfig from "./DeployNodeConfigFormJDCloudC
import DeployNodeConfigFormJDCloudLiveConfig from "./DeployNodeConfigFormJDCloudLiveConfig";
import DeployNodeConfigFormJDCloudVODConfig from "./DeployNodeConfigFormJDCloudVODConfig";
import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig";
+import DeployNodeConfigFormLeCDNConfig from "./DeployNodeConfigFormLeCDNConfig";
import DeployNodeConfigFormLocalConfig from "./DeployNodeConfigFormLocalConfig";
import DeployNodeConfigFormNetlifySiteConfig from "./DeployNodeConfigFormNetlifySiteConfig";
import DeployNodeConfigFormProxmoxVEConfig from "./DeployNodeConfigFormProxmoxVEConfig";
@@ -63,6 +67,7 @@ import DeployNodeConfigFormQiniuCDNConfig from "./DeployNodeConfigFormQiniuCDNCo
import DeployNodeConfigFormQiniuKodoConfig from "./DeployNodeConfigFormQiniuKodoConfig";
import DeployNodeConfigFormQiniuPiliConfig from "./DeployNodeConfigFormQiniuPiliConfig";
import DeployNodeConfigFormRainYunRCDNConfig from "./DeployNodeConfigFormRainYunRCDNConfig";
+import DeployNodeConfigFormRatPanelSiteConfig from "./DeployNodeConfigFormRatPanelSiteConfig";
import DeployNodeConfigFormSafeLineConfig from "./DeployNodeConfigFormSafeLineConfig";
import DeployNodeConfigFormSSHConfig from "./DeployNodeConfigFormSSHConfig.tsx";
import DeployNodeConfigFormTencentCloudCDNConfig from "./DeployNodeConfigFormTencentCloudCDNConfig.tsx";
@@ -87,7 +92,9 @@ import DeployNodeConfigFormVolcEngineDCDNConfig from "./DeployNodeConfigFormVolc
import DeployNodeConfigFormVolcEngineImageXConfig from "./DeployNodeConfigFormVolcEngineImageXConfig.tsx";
import DeployNodeConfigFormVolcEngineLiveConfig from "./DeployNodeConfigFormVolcEngineLiveConfig.tsx";
import DeployNodeConfigFormVolcEngineTOSConfig from "./DeployNodeConfigFormVolcEngineTOSConfig.tsx";
+import DeployNodeConfigFormWangsuCDNConfig from "./DeployNodeConfigFormWangsuCDNConfig.tsx";
import DeployNodeConfigFormWangsuCDNProConfig from "./DeployNodeConfigFormWangsuCDNProConfig.tsx";
+import DeployNodeConfigFormWangsuCertificateConfig from "./DeployNodeConfigFormWangsuCertificateConfig.tsx";
import DeployNodeConfigFormWebhookConfig from "./DeployNodeConfigFormWebhookConfig.tsx";
type DeployNodeConfigFormFieldValues = Partial;
@@ -201,6 +208,8 @@ const DeployNodeConfigForm = forwardRef;
case DEPLOYMENT_PROVIDERS.ALIYUN_FC:
return ;
+ case DEPLOYMENT_PROVIDERS.ALIYUN_GA:
+ return ;
case DEPLOYMENT_PROVIDERS.ALIYUN_LIVE:
return ;
case DEPLOYMENT_PROVIDERS.ALIYUN_NLB:
@@ -229,6 +238,8 @@ const DeployNodeConfigForm = forwardRef;
case DEPLOYMENT_PROVIDERS.BAOTAPANEL_SITE:
return ;
+ case DEPLOYMENT_PROVIDERS.BAOTAWAF_SITE:
+ return ;
case DEPLOYMENT_PROVIDERS.BUNNY_CDN:
return ;
case DEPLOYMENT_PROVIDERS.BYTEPLUS_CDN:
@@ -239,6 +250,8 @@ const DeployNodeConfigForm = forwardRef;
case DEPLOYMENT_PROVIDERS.EDGIO_APPLICATIONS:
return ;
+ case DEPLOYMENT_PROVIDERS.FLEXCDN:
+ return ;
case DEPLOYMENT_PROVIDERS.GCORE_CDN:
return ;
case DEPLOYMENT_PROVIDERS.GOEDGE:
@@ -259,6 +272,8 @@ const DeployNodeConfigForm = forwardRef;
case DEPLOYMENT_PROVIDERS.KUBERNETES_SECRET:
return ;
+ case DEPLOYMENT_PROVIDERS.LECDN:
+ return ;
case DEPLOYMENT_PROVIDERS.LOCAL:
return ;
case DEPLOYMENT_PROVIDERS.NETLIFY_SITE:
@@ -273,6 +288,8 @@ const DeployNodeConfigForm = forwardRef;
case DEPLOYMENT_PROVIDERS.RAINYUN_RCDN:
return ;
+ case DEPLOYMENT_PROVIDERS.RATPANEL_SITE:
+ return ;
case DEPLOYMENT_PROVIDERS.SAFELINE:
return ;
case DEPLOYMENT_PROVIDERS.SSH:
@@ -321,8 +338,12 @@ const DeployNodeConfigForm = forwardRef;
case DEPLOYMENT_PROVIDERS.VOLCENGINE_TOS:
return ;
+ case DEPLOYMENT_PROVIDERS.WANGSU_CDN:
+ return ;
case DEPLOYMENT_PROVIDERS.WANGSU_CDNPRO:
return ;
+ case DEPLOYMENT_PROVIDERS.WANGSU_CERTIFICATE:
+ return ;
case DEPLOYMENT_PROVIDERS.WEBHOOK:
return ;
}
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAWSACMConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAWSACMConfig.tsx
index f0964493..2e539453 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormAWSACMConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormAWSACMConfig.tsx
@@ -28,7 +28,7 @@ const DeployNodeConfigFormAWSACMConfig = ({ form: formInst, formName, disabled,
.string({ message: t("workflow_node.deploy.form.aws_acm_region.placeholder") })
.nonempty(t("workflow_node.deploy.form.aws_acm_region.placeholder"))
.trim(),
- certificateArn: z.string({ message: t("workflow_node.deploy.form.aws_acm_certificate_arn.placeholder") }).nullish(),
+ certificateArn: z.string().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
@@ -60,7 +60,7 @@ const DeployNodeConfigFormAWSACMConfig = ({ form: formInst, formName, disabled,
rules={[formRule]}
tooltip={}
>
-
+
);
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunALBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunALBConfig.tsx
index 3afcb7a1..bbfca5e6 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunALBConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunALBConfig.tsx
@@ -132,7 +132,7 @@ const DeployNodeConfigFormAliyunALBConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCLBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCLBConfig.tsx
index 2c2e43b6..e666800e 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCLBConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCLBConfig.tsx
@@ -132,7 +132,7 @@ const DeployNodeConfigFormAliyunCLBConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunGAConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunGAConfig.tsx
new file mode 100644
index 00000000..20dd1ae1
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunGAConfig.tsx
@@ -0,0 +1,118 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Select } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import Show from "@/components/Show";
+import { validDomainName } from "@/utils/validators";
+
+type DeployNodeConfigFormAliyunGAConfigFieldValues = Nullish<{
+ resourceType: string;
+ acceleratorId?: string;
+ listenerId?: string;
+ domain?: string;
+}>;
+
+export type DeployNodeConfigFormAliyunGAConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormAliyunGAConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormAliyunGAConfigFieldValues) => void;
+};
+
+const RESOURCE_TYPE_ACCELERATOR = "accelerator" as const;
+const RESOURCE_TYPE_LISTENER = "listener" as const;
+
+const initFormModel = (): DeployNodeConfigFormAliyunGAConfigFieldValues => {
+ return {};
+};
+
+const DeployNodeConfigFormAliyunGAConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormAliyunGAConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ resourceType: z.union([z.literal(RESOURCE_TYPE_ACCELERATOR), z.literal(RESOURCE_TYPE_LISTENER)], {
+ message: t("workflow_node.deploy.form.aliyun_ga_resource_type.placeholder"),
+ }),
+ acceleratorId: z
+ .string()
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .trim(),
+ listenerId: z
+ .string()
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .trim()
+ .nullish()
+ .refine((v) => fieldResourceType !== RESOURCE_TYPE_LISTENER || !!v?.trim(), t("workflow_node.deploy.form.aliyun_ga_listener_id.placeholder")),
+ domain: z
+ .string()
+ .nullish()
+ .refine((v) => {
+ if (![RESOURCE_TYPE_ACCELERATOR, RESOURCE_TYPE_LISTENER].includes(fieldResourceType)) return true;
+ return !v || validDomainName(v!);
+ }, t("common.errmsg.domain_invalid")),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const fieldResourceType = Form.useWatch("resourceType", formInst);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+ }
+ >
+
+
+
+
+ }
+ >
+
+
+
+
+
+ }
+ >
+
+
+
+
+ );
+};
+
+export default DeployNodeConfigFormAliyunGAConfig;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunWAFConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunWAFConfig.tsx
index 5f81cf71..a46c7327 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunWAFConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunWAFConfig.tsx
@@ -102,7 +102,7 @@ const DeployNodeConfigFormAliyunWAFConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
);
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAzureKeyVaultConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAzureKeyVaultConfig.tsx
index 2a54bb99..bd2347df 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormAzureKeyVaultConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormAzureKeyVaultConfig.tsx
@@ -35,7 +35,7 @@ const DeployNodeConfigFormAzureKeyVaultConfig = ({
.nonempty(t("workflow_node.deploy.form.azure_keyvault_name.placeholder"))
.trim(),
certificateName: z
- .string({ message: t("workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder") })
+ .string()
.nullish()
.refine((v) => {
if (!v) return true;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudAppBLBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudAppBLBConfig.tsx
index 7bd40b82..875d254b 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudAppBLBConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudAppBLBConfig.tsx
@@ -135,7 +135,7 @@ const DeployNodeConfigFormBaiduCloudAppBLBConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudBLBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudBLBConfig.tsx
index 20bb22f1..99c0b059 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudBLBConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudBLBConfig.tsx
@@ -135,7 +135,7 @@ const DeployNodeConfigFormBaiduCloudBLBConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx
index ad05b6a8..7d32bef5 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx
@@ -73,7 +73,7 @@ const DeployNodeConfigFormBaishanCDNConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
);
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaWAFSiteConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaWAFSiteConfig.tsx
new file mode 100644
index 00000000..6f992fb8
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaWAFSiteConfig.tsx
@@ -0,0 +1,78 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, InputNumber } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { validPortNumber } from "@/utils/validators";
+
+type DeployNodeConfigFormBaotaWAFSiteConfigFieldValues = Nullish<{
+ siteName: string;
+ sitePort: number;
+}>;
+
+export type DeployNodeConfigFormBaotaWAFSiteConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormBaotaWAFSiteConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormBaotaWAFSiteConfigFieldValues) => void;
+};
+
+const initFormModel = (): DeployNodeConfigFormBaotaWAFSiteConfigFieldValues => {
+ return {
+ siteName: "",
+ sitePort: 443,
+ };
+};
+
+const DeployNodeConfigFormBaotaWAFSiteConfig = ({
+ form: formInst,
+ formName,
+ disabled,
+ initialValues,
+ onValuesChange,
+}: DeployNodeConfigFormBaotaWAFSiteConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ siteName: z.string().nonempty(t("workflow_node.deploy.form.baotawaf_site_name.placeholder")).trim(),
+ sitePort: z.preprocess(
+ (v) => Number(v),
+ z
+ .number()
+ .int(t("workflow_node.deploy.form.baotawaf_site_port.placeholder"))
+ .refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
+ ),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+ }
+ >
+
+
+
+
+
+
+
+ );
+};
+
+export default DeployNodeConfigFormBaotaWAFSiteConfig;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx
index 45662e75..4d6ae25c 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx
@@ -79,13 +79,23 @@ const DeployNodeConfigFormCdnflyConfig = ({ form: formInst, formName, disabled,
-
+ }
+ >
-
+ }
+ >
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormFlexCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormFlexCDNConfig.tsx
new file mode 100644
index 00000000..e24652be
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormFlexCDNConfig.tsx
@@ -0,0 +1,84 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Select } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import Show from "@/components/Show";
+
+type DeployNodeConfigFormFlexCDNConfigFieldValues = Nullish<{
+ resourceType: string;
+ certificateId?: string | number;
+}>;
+
+export type DeployNodeConfigFormFlexCDNConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormFlexCDNConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormFlexCDNConfigFieldValues) => void;
+};
+
+const RESOURCE_TYPE_CERTIFICATE = "certificate" as const;
+
+const initFormModel = (): DeployNodeConfigFormFlexCDNConfigFieldValues => {
+ return {
+ resourceType: RESOURCE_TYPE_CERTIFICATE,
+ certificateId: "",
+ };
+};
+
+const DeployNodeConfigFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormFlexCDNConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ resourceType: z.literal(RESOURCE_TYPE_CERTIFICATE, {
+ message: t("workflow_node.deploy.form.flexcdn_resource_type.placeholder"),
+ }),
+ certificateId: z
+ .union([z.string(), z.number().int()])
+ .nullish()
+ .refine((v) => {
+ if (fieldResourceType !== RESOURCE_TYPE_CERTIFICATE) return true;
+ return /^\d+$/.test(v + "") && +v! > 0;
+ }, t("workflow_node.deploy.form.flexcdn_certificate_id.placeholder")),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const fieldResourceType = Form.useWatch("resourceType", formInst);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+
+ }
+ >
+
+
+
+
+ );
+};
+
+export default DeployNodeConfigFormFlexCDNConfig;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx
index 4d548949..f21a4bb9 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx
@@ -67,7 +67,7 @@ const DeployNodeConfigFormGcoreCDNConfig = ({ form: formInst, formName, disabled
rules={[formRule]}
tooltip={}
>
-
+
);
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormGoEdgeConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormGoEdgeConfig.tsx
index 89dffb5f..5e0f7f85 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormGoEdgeConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormGoEdgeConfig.tsx
@@ -68,7 +68,12 @@ const DeployNodeConfigFormGoEdgeConfig = ({ form: formInst, formName, disabled,
-
+ }
+ >
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx
index f54477ce..22c5bf08 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx
@@ -132,7 +132,7 @@ const DeployNodeConfigFormJDCloudALBConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormLeCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormLeCDNConfig.tsx
new file mode 100644
index 00000000..0636cedf
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormLeCDNConfig.tsx
@@ -0,0 +1,103 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Select } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import Show from "@/components/Show";
+
+type DeployNodeConfigFormLeCDNConfigFieldValues = Nullish<{
+ resourceType: string;
+ certificateId?: string | number;
+ clientId?: string | number;
+}>;
+
+export type DeployNodeConfigFormLeCDNConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormLeCDNConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormLeCDNConfigFieldValues) => void;
+};
+
+const RESOURCE_TYPE_CERTIFICATE = "certificate" as const;
+
+const initFormModel = (): DeployNodeConfigFormLeCDNConfigFieldValues => {
+ return {
+ resourceType: RESOURCE_TYPE_CERTIFICATE,
+ certificateId: "",
+ clientId: "",
+ };
+};
+
+const DeployNodeConfigFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormLeCDNConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ resourceType: z.literal(RESOURCE_TYPE_CERTIFICATE, {
+ message: t("workflow_node.deploy.form.lecdn_resource_type.placeholder"),
+ }),
+ certificateId: z
+ .union([z.string(), z.number().int()])
+ .nullish()
+ .refine((v) => {
+ if (fieldResourceType !== RESOURCE_TYPE_CERTIFICATE) return true;
+ return /^\d+$/.test(v + "") && +v! > 0;
+ }, t("workflow_node.deploy.form.lecdn_certificate_id.placeholder")),
+ clientId: z
+ .union([z.string(), z.number().int()])
+ .nullish()
+ .refine((v) => {
+ if (fieldResourceType !== RESOURCE_TYPE_CERTIFICATE) return true;
+ if (v == null || v === "") return true;
+ return /^\d+$/.test(v + "") && +v! > 0;
+ }, t("workflow_node.deploy.form.lecdn_client_id.placeholder")),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const fieldResourceType = Form.useWatch("resourceType", formInst);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+
+ );
+};
+
+export default DeployNodeConfigFormLeCDNConfig;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx
index 75853eb7..282503e5 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx
@@ -351,7 +351,7 @@ const DeployNodeConfigFormLocalConfig = ({ form: formInst, formName, disabled, i
rules={[formRule]}
tooltip={}
>
-
+
}
>
-
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormRatPanelSiteConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormRatPanelSiteConfig.tsx
new file mode 100644
index 00000000..03f86913
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormRatPanelSiteConfig.tsx
@@ -0,0 +1,63 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+type DeployNodeConfigFormRatPanelSiteConfigFieldValues = Nullish<{
+ siteName: string;
+}>;
+
+export type DeployNodeConfigFormRatPanelSiteConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormRatPanelSiteConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormRatPanelSiteConfigFieldValues) => void;
+};
+
+const initFormModel = (): DeployNodeConfigFormRatPanelSiteConfigFieldValues => {
+ return {
+ siteName: "",
+ };
+};
+
+const DeployNodeConfigFormRatPanelSiteConfig = ({
+ form: formInst,
+ formName,
+ disabled,
+ initialValues,
+ onValuesChange,
+}: DeployNodeConfigFormRatPanelSiteConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ siteName: z.string().nonempty(t("workflow_node.deploy.form.ratpanel_site_name.placeholder")),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+ }
+ >
+
+
+
+ );
+};
+
+export default DeployNodeConfigFormRatPanelSiteConfig;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx
index e99a2431..49110ce9 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx
@@ -363,7 +363,7 @@ const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, ini
rules={[formRule]}
tooltip={}
>
-
+
}
>
-
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx
index 2da3ef16..c5905e14 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx
@@ -68,7 +68,12 @@ const DeployNodeConfigFormSafeLineConfig = ({ form: formInst, formName, disabled
-
+ }
+ >
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudCLBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudCLBConfig.tsx
index 2e7dc127..760c6fac 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudCLBConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudCLBConfig.tsx
@@ -144,7 +144,7 @@ const DeployNodeConfigFormTencentCloudCLBConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineALBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineALBConfig.tsx
index 348f4d8d..650323ab 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineALBConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineALBConfig.tsx
@@ -140,7 +140,7 @@ const DeployNodeConfigFormVolcEngineALBConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx
new file mode 100644
index 00000000..57d0d381
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx
@@ -0,0 +1,146 @@
+import { memo } from "react";
+import { useTranslation } from "react-i18next";
+import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons";
+import { Button, Form, type FormInstance, Input, Space } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import ModalForm from "@/components/ModalForm";
+import MultipleInput from "@/components/MultipleInput";
+import { useAntdForm } from "@/hooks";
+import { validDomainName } from "@/utils/validators";
+
+type DeployNodeConfigFormWangsuCDNConfigFieldValues = Nullish<{
+ domains: string;
+}>;
+
+export type DeployNodeConfigFormWangsuCDNConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormWangsuCDNConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormWangsuCDNConfigFieldValues) => void;
+};
+
+const MULTIPLE_INPUT_DELIMITER = ";";
+
+const initFormModel = (): DeployNodeConfigFormWangsuCDNConfigFieldValues => {
+ return {
+ domains: "",
+ };
+};
+
+const DeployNodeConfigFormWangsuCDNConfig = ({
+ form: formInst,
+ formName,
+ disabled,
+ initialValues,
+ onValuesChange,
+}: DeployNodeConfigFormWangsuCDNConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ domains: z
+ .string()
+ .nullish()
+ .refine((v) => {
+ if (!v) return false;
+ return String(v)
+ .split(MULTIPLE_INPUT_DELIMITER)
+ .every((e) => validDomainName(e));
+ }, t("workflow_node.deploy.form.wangsu_cdn_domains.placeholder")),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const fieldDomains = Form.useWatch("domains", formInst);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+ }
+ >
+
+
+ {
+ formInst.setFieldValue("domains", e.target.value);
+ }}
+ onClear={() => {
+ formInst.setFieldValue("domains", "");
+ }}
+ />
+
+
+
+
+ }
+ onChange={(value) => {
+ formInst.setFieldValue("domains", value);
+ }}
+ />
+
+
+
+ );
+};
+
+const SiteNamesModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ domains: z.array(z.string()).refine((v) => {
+ return v.every((e) => validDomainName(e));
+ }, t("workflow_node.deploy.form.wangsu_cdn_domains.errmsg.invalid")),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+ const { form: formInst, formProps } = useAntdForm({
+ name: "workflowNodeDeployConfigFormWangsuCDNNamesModalInput",
+ initialValues: { domains: value?.split(MULTIPLE_INPUT_DELIMITER) },
+ onSubmit: (values) => {
+ onChange?.(
+ values.domains
+ .map((e) => e.trim())
+ .filter((e) => !!e)
+ .join(MULTIPLE_INPUT_DELIMITER)
+ );
+ },
+ });
+
+ return (
+
+
+
+
+
+ );
+});
+
+export default DeployNodeConfigFormWangsuCDNConfig;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNProConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNProConfig.tsx
index 90bdb064..e89e1e8d 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNProConfig.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNProConfig.tsx
@@ -5,37 +5,37 @@ import { z } from "zod";
import { validDomainName } from "@/utils/validators";
-type DeployNodeConfigFormBaishanCDNConfigFieldValues = Nullish<{
+type DeployNodeConfigFormWangsuCDNProConfigFieldValues = Nullish<{
environment: string;
domain: string;
certificateId?: string;
webhookId?: string;
}>;
-export type DeployNodeConfigFormBaishanCDNConfigProps = {
+export type DeployNodeConfigFormWangsuCDNProConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
- initialValues?: DeployNodeConfigFormBaishanCDNConfigFieldValues;
- onValuesChange?: (values: DeployNodeConfigFormBaishanCDNConfigFieldValues) => void;
+ initialValues?: DeployNodeConfigFormWangsuCDNProConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormWangsuCDNProConfigFieldValues) => void;
};
const ENVIRONMENT_PRODUCTION = "production" as const;
const ENVIRONMENT_STAGING = "stating" as const;
-const initFormModel = (): DeployNodeConfigFormBaishanCDNConfigFieldValues => {
+const initFormModel = (): DeployNodeConfigFormWangsuCDNProConfigFieldValues => {
return {
environment: ENVIRONMENT_PRODUCTION,
};
};
-const DeployNodeConfigFormBaishanCDNConfig = ({
+const DeployNodeConfigFormWangsuCDNProConfig = ({
form: formInst,
formName,
disabled,
initialValues,
onValuesChange,
-}: DeployNodeConfigFormBaishanCDNConfigProps) => {
+}: DeployNodeConfigFormWangsuCDNProConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
@@ -89,7 +89,7 @@ const DeployNodeConfigFormBaishanCDNConfig = ({
rules={[formRule]}
tooltip={}
>
-
+
}
>
-
+
);
};
-export default DeployNodeConfigFormBaishanCDNConfig;
+export default DeployNodeConfigFormWangsuCDNProConfig;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCertificateConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCertificateConfig.tsx
new file mode 100644
index 00000000..739e4b28
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCertificateConfig.tsx
@@ -0,0 +1,61 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+type DeployNodeConfigFormWangsuCertificateConfigFieldValues = Nullish<{
+ certificateId?: string;
+}>;
+
+export type DeployNodeConfigFormWangsuCertificateConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormWangsuCertificateConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormWangsuCertificateConfigFieldValues) => void;
+};
+
+const initFormModel = (): DeployNodeConfigFormWangsuCertificateConfigFieldValues => {
+ return {};
+};
+
+const DeployNodeConfigFormWangsuCertificateConfig = ({
+ form: formInst,
+ formName,
+ disabled,
+ initialValues,
+ onValuesChange,
+}: DeployNodeConfigFormWangsuCertificateConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ certificateId: z.string().nullish(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+ }
+ >
+
+
+
+ );
+};
+
+export default DeployNodeConfigFormWangsuCertificateConfig;
diff --git a/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx b/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx
index 32488aeb..d303c2ba 100644
--- a/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx
@@ -19,7 +19,7 @@ import { useNotifyChannelsStore } from "@/stores/notify";
import NotifyNodeConfigFormEmailConfig from "./NotifyNodeConfigFormEmailConfig";
import NotifyNodeConfigFormMattermostConfig from "./NotifyNodeConfigFormMattermostConfig";
-import NotifyNodeConfigFormTelegramConfig from "./NotifyNodeConfigFormTelegramConfig";
+import NotifyNodeConfigFormTelegramBotConfig from "./NotifyNodeConfigFormTelegramBotConfig";
import NotifyNodeConfigFormWebhookConfig from "./NotifyNodeConfigFormWebhookConfig";
type NotifyNodeConfigFormFieldValues = Partial;
@@ -114,8 +114,8 @@ const NotifyNodeConfigForm = forwardRef;
case NOTIFICATION_PROVIDERS.MATTERMOST:
return ;
- case NOTIFICATION_PROVIDERS.TELEGRAM:
- return ;
+ case NOTIFICATION_PROVIDERS.TELEGRAMBOT:
+ return ;
case NOTIFICATION_PROVIDERS.WEBHOOK:
return ;
}
diff --git a/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramConfig.tsx b/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx
similarity index 55%
rename from ui/src/components/workflow/node/NotifyNodeConfigFormTelegramConfig.tsx
rename to ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx
index 07774413..a40142ee 100644
--- a/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramConfig.tsx
+++ b/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx
@@ -3,23 +3,29 @@ import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
-type NotifyNodeConfigFormTelegramConfigFieldValues = Nullish<{
+type NotifyNodeConfigFormTelegramBotConfigFieldValues = Nullish<{
chatId?: string | number;
}>;
-export type NotifyNodeConfigFormTelegramConfigProps = {
+export type NotifyNodeConfigFormTelegramBotConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
- initialValues?: NotifyNodeConfigFormTelegramConfigFieldValues;
- onValuesChange?: (values: NotifyNodeConfigFormTelegramConfigFieldValues) => void;
+ initialValues?: NotifyNodeConfigFormTelegramBotConfigFieldValues;
+ onValuesChange?: (values: NotifyNodeConfigFormTelegramBotConfigFieldValues) => void;
};
-const initFormModel = (): NotifyNodeConfigFormTelegramConfigFieldValues => {
+const initFormModel = (): NotifyNodeConfigFormTelegramBotConfigFieldValues => {
return {};
};
-const NotifyNodeConfigFormTelegramConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: NotifyNodeConfigFormTelegramConfigProps) => {
+const NotifyNodeConfigFormTelegramBotConfig = ({
+ form: formInst,
+ formName,
+ disabled,
+ initialValues,
+ onValuesChange,
+}: NotifyNodeConfigFormTelegramBotConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
@@ -31,8 +37,8 @@ const NotifyNodeConfigFormTelegramConfig = ({ form: formInst, formName, disabled
.nullish()
.refine((v) => {
if (v == null || v + "" === "") return true;
- return /^\d+$/.test(v + "") && +v! > 0;
- }, t("workflow_node.notify.form.telegram_chat_id.placeholder"))
+ return !Number.isNaN(+v!) && +v! !== 0;
+ }, t("workflow_node.notify.form.telegram_bot_chat_id.placeholder"))
)
.nullish(),
});
@@ -53,14 +59,14 @@ const NotifyNodeConfigFormTelegramConfig = ({ form: formInst, formName, disabled
>
}
+ tooltip={}
>
-
+
);
};
-export default NotifyNodeConfigFormTelegramConfig;
+export default NotifyNodeConfigFormTelegramBotConfig;
diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts
index 0d516385..f60f39c8 100644
--- a/ui/src/domain/access.ts
+++ b/ui/src/domain/access.ts
@@ -7,6 +7,7 @@ export interface AccessModel extends BaseModel {
*/ Record &
(
| AccessConfigFor1Panel
+ | AccessConfigForACMECA
| AccessConfigForACMEHttpReq
| AccessConfigForAliyun
| AccessConfigForAWS
@@ -14,6 +15,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForBaiduCloud
| AccessConfigForBaishan
| AccessConfigForBaotaPanel
+ | AccessConfigForBaotaWAF
| AccessConfigForBunny
| AccessConfigForBytePlus
| AccessConfigForCacheFly
@@ -28,6 +30,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForDynv6
| AccessConfigForEdgio
| AccessConfigForEmail
+ | AccessConfigForFlexCDN
| AccessConfigForGcore
| AccessConfigForGname
| AccessConfigForGoDaddy
@@ -37,6 +40,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForJDCloud
| AccessConfigForKubernetes
| AccessConfigForLarkBot
+ | AccessConfigForLeCDN
| AccessConfigForMattermost
| AccessConfigForNamecheap
| AccessConfigForNameDotCom
@@ -48,10 +52,11 @@ export interface AccessModel extends BaseModel {
| AccessConfigForProxmoxVE
| AccessConfigForQiniu
| AccessConfigForRainYun
+ | AccessConfigForRatPanel
| AccessConfigForSafeLine
| AccessConfigForSSH
| AccessConfigForSSLCom
- | AccessConfigForTelegram
+ | AccessConfigForTelegramBot
| AccessConfigForTencentCloud
| AccessConfigForUCloud
| AccessConfigForUpyun
@@ -69,10 +74,17 @@ export interface AccessModel extends BaseModel {
// #region AccessConfig
export type AccessConfigFor1Panel = {
apiUrl: string;
+ apiVersion: string;
apiKey: string;
allowInsecureConnections?: boolean;
};
+export type AccessConfigForACMECA = {
+ endpoint: string;
+ eabKid?: string;
+ eabHmacKey?: string;
+};
+
export type AccessConfigForACMEHttpReq = {
endpoint: string;
mode?: string;
@@ -112,6 +124,12 @@ export type AccessConfigForBaotaPanel = {
allowInsecureConnections?: boolean;
};
+export type AccessConfigForBaotaWAF = {
+ apiUrl: string;
+ apiKey: string;
+ allowInsecureConnections?: boolean;
+};
+
export type AccessConfigForBunny = {
apiKey: string;
};
@@ -185,6 +203,14 @@ export type AccessConfigForEmail = {
defaultReceiverAddress?: string;
};
+export type AccessConfigForFlexCDN = {
+ apiUrl: string;
+ apiRole: string;
+ accessKeyId: string;
+ accessKey: string;
+ allowInsecureConnections?: boolean;
+};
+
export type AccessConfigForGcore = {
apiToken: string;
};
@@ -230,6 +256,15 @@ export type AccessConfigForLarkBot = {
webhookUrl: string;
};
+export type AccessConfigForLeCDN = {
+ apiUrl: string;
+ apiVersion: string;
+ apiRole: string;
+ username: string;
+ password: string;
+ allowInsecureConnections?: boolean;
+};
+
export type AccessConfigForMattermost = {
serverUrl: string;
username: string;
@@ -292,6 +327,13 @@ export type AccessConfigForRainYun = {
apiKey: string;
};
+export type AccessConfigForRatPanel = {
+ apiUrl: string;
+ accessTokenId: number;
+ accessToken: string;
+ allowInsecureConnections?: boolean;
+};
+
export type AccessConfigForSafeLine = {
apiUrl: string;
apiToken: string;
@@ -312,7 +354,7 @@ export type AccessConfigForSSLCom = {
eabHmacKey: string;
};
-export type AccessConfigForTelegram = {
+export type AccessConfigForTelegramBot = {
botToken: string;
defaultChatId?: number;
};
diff --git a/ui/src/domain/certificate.ts b/ui/src/domain/certificate.ts
index 563d2476..c4bc8710 100644
--- a/ui/src/domain/certificate.ts
+++ b/ui/src/domain/certificate.ts
@@ -6,7 +6,7 @@ export interface CertificateModel extends BaseModel {
serialNumber: string;
certificate: string;
privateKey: string;
- issuer: string;
+ issuerOrg: string;
keyAlgorithm: string;
effectAt: ISO8601String;
expireAt: ISO8601String;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index fbac84ba..15542455 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -5,6 +5,7 @@
*/
export const ACCESS_PROVIDERS = Object.freeze({
["1PANEL"]: "1panel",
+ ACMECA: "acmeca",
ACMEHTTPREQ: "acmehttpreq",
ALIYUN: "aliyun",
AWS: "aws",
@@ -12,6 +13,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
BAIDUCLOUD: "baiducloud",
BAISHAN: "baishan",
BAOTAPANEL: "baotapanel",
+ BAOTAWAF: "baotawaf",
BUNNY: "bunny",
BYTEPLUS: "byteplus",
BUYPASS: "buypass",
@@ -27,6 +29,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
DYNV6: "dynv6",
EDGIO: "edgio",
EMAIL: "email",
+ FLEXCDN: "flexcdn",
GCORE: "gcore",
GNAME: "gname",
GODADDY: "godaddy",
@@ -36,6 +39,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
JDCLOUD: "jdcloud",
KUBERNETES: "k8s",
LARKBOT: "larkbot",
+ LECDN: "lecdn",
LETSENCRYPT: "letsencrypt",
LETSENCRYPTSTAGING: "letsencryptstaging",
LOCAL: "local",
@@ -51,10 +55,11 @@ export const ACCESS_PROVIDERS = Object.freeze({
PROXMOXVE: "proxmoxve",
QINIU: "qiniu",
RAINYUN: "rainyun",
+ RATPANEL: "ratpanel",
SAFELINE: "safeline",
SSH: "ssh",
SSLCOM: "sslcom",
- TELEGRAM: "telegram",
+ TELEGRAMBOT: "telegrambot",
TENCENTCLOUD: "tencentcloud",
UCLOUD: "ucloud",
UPYUN: "upyun",
@@ -120,10 +125,14 @@ export const accessProvidersMap: Map [
e[0] as string,
{
@@ -177,6 +187,7 @@ export const accessProvidersMap: Map = new
[CA_PROVIDERS.GOOGLETRUSTSERVICES],
[CA_PROVIDERS.SSLCOM],
[CA_PROVIDERS.ZEROSSL],
+ [CA_PROVIDERS.ACMECA],
].map(([type, builtin]) => [
type,
{
@@ -345,6 +357,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
ALIYUN_DDOS: `${ACCESS_PROVIDERS.ALIYUN}-ddospro`,
ALIYUN_ESA: `${ACCESS_PROVIDERS.ALIYUN}-esa`,
ALIYUN_FC: `${ACCESS_PROVIDERS.ALIYUN}-fc`,
+ ALIYUN_GA: `${ACCESS_PROVIDERS.ALIYUN}-ga`,
ALIYUN_LIVE: `${ACCESS_PROVIDERS.ALIYUN}-live`,
ALIYUN_NLB: `${ACCESS_PROVIDERS.ALIYUN}-nlb`,
ALIYUN_OSS: `${ACCESS_PROVIDERS.ALIYUN}-oss`,
@@ -360,12 +373,15 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
BAISHAN_CDN: `${ACCESS_PROVIDERS.BAISHAN}-cdn`,
BAOTAPANEL_CONSOLE: `${ACCESS_PROVIDERS.BAOTAPANEL}-console`,
BAOTAPANEL_SITE: `${ACCESS_PROVIDERS.BAOTAPANEL}-site`,
+ BAOTAWAF_CONSOLE: `${ACCESS_PROVIDERS.BAOTAWAF}-console`,
+ BAOTAWAF_SITE: `${ACCESS_PROVIDERS.BAOTAWAF}-site`,
BUNNY_CDN: `${ACCESS_PROVIDERS.BUNNY}-cdn`,
BYTEPLUS_CDN: `${ACCESS_PROVIDERS.BYTEPLUS}-cdn`,
CACHEFLY: `${ACCESS_PROVIDERS.CACHEFLY}`,
CDNFLY: `${ACCESS_PROVIDERS.CDNFLY}`,
DOGECLOUD_CDN: `${ACCESS_PROVIDERS.DOGECLOUD}-cdn`,
EDGIO_APPLICATIONS: `${ACCESS_PROVIDERS.EDGIO}-applications`,
+ FLEXCDN: `${ACCESS_PROVIDERS.FLEXCDN}`,
GCORE_CDN: `${ACCESS_PROVIDERS.GCORE}-cdn`,
GOEDGE: `${ACCESS_PROVIDERS.GOEDGE}`,
HUAWEICLOUD_CDN: `${ACCESS_PROVIDERS.HUAWEICLOUD}-cdn`,
@@ -377,6 +393,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
JDCLOUD_LIVE: `${ACCESS_PROVIDERS.JDCLOUD}-live`,
JDCLOUD_VOD: `${ACCESS_PROVIDERS.JDCLOUD}-vod`,
KUBERNETES_SECRET: `${ACCESS_PROVIDERS.KUBERNETES}-secret`,
+ LECDN: `${ACCESS_PROVIDERS.LECDN}`,
LOCAL: `${ACCESS_PROVIDERS.LOCAL}`,
NETLIFY_SITE: `${ACCESS_PROVIDERS.NETLIFY}-site`,
PROXMOXVE: `${ACCESS_PROVIDERS.PROXMOXVE}`,
@@ -384,6 +401,8 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
QINIU_KODO: `${ACCESS_PROVIDERS.QINIU}-kodo`,
QINIU_PILI: `${ACCESS_PROVIDERS.QINIU}-pili`,
RAINYUN_RCDN: `${ACCESS_PROVIDERS.RAINYUN}-rcdn`,
+ RATPANEL_CONSOLE: `${ACCESS_PROVIDERS.RATPANEL}-console`,
+ RATPANEL_SITE: `${ACCESS_PROVIDERS.RATPANEL}-site`,
SAFELINE: `${ACCESS_PROVIDERS.SAFELINE}`,
SSH: `${ACCESS_PROVIDERS.SSH}`,
TENCENTCLOUD_CDN: `${ACCESS_PROVIDERS.TENCENTCLOUD}-cdn`,
@@ -409,7 +428,9 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
VOLCENGINE_IMAGEX: `${ACCESS_PROVIDERS.VOLCENGINE}-imagex`,
VOLCENGINE_LIVE: `${ACCESS_PROVIDERS.VOLCENGINE}-live`,
VOLCENGINE_TOS: `${ACCESS_PROVIDERS.VOLCENGINE}-tos`,
+ WANGSU_CDN: `${ACCESS_PROVIDERS.WANGSU}-cdn`,
WANGSU_CDNPRO: `${ACCESS_PROVIDERS.WANGSU}-cdnpro`,
+ WANGSU_CERTIFICATE: `${ACCESS_PROVIDERS.WANGSU}-certificate`,
WEBHOOK: `${ACCESS_PROVIDERS.WEBHOOK}`,
} as const);
@@ -422,8 +443,10 @@ export const DEPLOYMENT_CATEGORIES = Object.freeze({
LOADBALANCE: "loadbalance",
FIREWALL: "firewall",
AV: "av",
+ APIGATEWAY: "apigw",
SERVERLESS: "serverless",
WEBSITE: "website",
+ SSL: "ssl",
NAS: "nas",
OTHER: "other",
} as const);
@@ -461,9 +484,10 @@ export const deploymentProvidersMap: Map [
@@ -547,7 +579,7 @@ export const NOTIFICATION_PROVIDERS = Object.freeze({
EMAIL: `${ACCESS_PROVIDERS.EMAIL}`,
LARKBOT: `${ACCESS_PROVIDERS.LARKBOT}`,
MATTERMOST: `${ACCESS_PROVIDERS.MATTERMOST}`,
- TELEGRAM: `${ACCESS_PROVIDERS.TELEGRAM}`,
+ TELEGRAMBOT: `${ACCESS_PROVIDERS.TELEGRAMBOT}`,
WEBHOOK: `${ACCESS_PROVIDERS.WEBHOOK}`,
WECOMBOT: `${ACCESS_PROVIDERS.WECOMBOT}`,
} as const);
@@ -573,7 +605,7 @@ export const notificationProvidersMap: Map [
type,
{
diff --git a/ui/src/domain/version.ts b/ui/src/domain/version.ts
index 312f45a1..442a7506 100644
--- a/ui/src/domain/version.ts
+++ b/ui/src/domain/version.ts
@@ -1 +1 @@
-export const version = "v0.3.12";
+export const version = "v0.3.13";
diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json
index cff44b7c..41b81636 100644
--- a/ui/src/i18n/locales/en/nls.access.json
+++ b/ui/src/i18n/locales/en/nls.access.json
@@ -36,12 +36,21 @@
"access.form.notification_channel.placeholder": "Please select a notification channel",
"access.form.1panel_api_url.label": "1Panel URL",
"access.form.1panel_api_url.placeholder": "Please enter 1Panel URL",
+ "access.form.1panel_api_version.label": "1Panel version",
+ "access.form.1panel_api_version.placeholder": "Please select 1Panel version",
"access.form.1panel_api_key.label": "1Panel API key",
"access.form.1panel_api_key.placeholder": "Please enter 1Panel API key",
"access.form.1panel_api_key.tooltip": "For more information, see https://docs.1panel.pro/dev_manual/api_manual/",
"access.form.1panel_allow_insecure_conns.label": "Insecure SSL/TLS connections",
"access.form.1panel_allow_insecure_conns.switch.on": "Allow",
"access.form.1panel_allow_insecure_conns.switch.off": "Disallow",
+ "access.form.acmeca_endpoint.label": "Endpoint",
+ "access.form.acmeca_endpoint.placeholder": "Please enter endpoint",
+ "access.form.acmeca_endpoint.tooltip": "For more information, see https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1",
+ "access.form.acmeca_eab_kid.label": "ACME EAB KID (Optional)",
+ "access.form.acmeca_eab_kid.placeholder": "Please enter ACME EAB KID",
+ "access.form.acmeca_eab_hmac_key.label": "ACME EAB HMAC key (Optional)",
+ "access.form.acmeca_eab_hmac_key.placeholder": "Please enter ACME EAB HMAC key",
"access.form.acmehttpreq_endpoint.label": "Endpoint",
"access.form.acmehttpreq_endpoint.placeholder": "Please enter endpoint",
"access.form.acmehttpreq_endpoint.tooltip": "For more information, see https://go-acme.github.io/lego/dns/httpreq/",
@@ -103,6 +112,14 @@
"access.form.baotapanel_allow_insecure_conns.label": "Insecure SSL/TLS connections",
"access.form.baotapanel_allow_insecure_conns.switch.on": "Allow",
"access.form.baotapanel_allow_insecure_conns.switch.off": "Disallow",
+ "access.form.baotawaf_api_url.label": "aaWAF URL",
+ "access.form.baotawaf_api_url.placeholder": "Please enter aaWAF URL",
+ "access.form.baotawaf_api_key.label": "aaWAF API key",
+ "access.form.baotawaf_api_key.placeholder": "Please enter aaWAF API key",
+ "access.form.baotawaf_api_key.tooltip": "For more information, see https://github.com/aaPanel/aaWAF/blob/main/API.md",
+ "access.form.baotawaf_allow_insecure_conns.label": "Insecure SSL/TLS connections",
+ "access.form.baotawaf_allow_insecure_conns.switch.on": "Allow",
+ "access.form.baotawaf_allow_insecure_conns.switch.off": "Disallow",
"access.form.byteplus_access_key.label": "BytePlus AccessKey",
"access.form.byteplus_access_key.placeholder": "Please enter BytePlus AccessKey",
"access.form.byteplus_access_key.tooltip": "For more information, see https://docs.byteplus.com/en/docs/byteplus-platform/docs-managing-keys",
@@ -112,8 +129,8 @@
"access.form.cachefly_api_token.label": "CacheFly API token",
"access.form.cachefly_api_token.placeholder": "Please enter CacheFly API token",
"access.form.cachefly_api_token.tooltip": "For more information, see https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228",
- "access.form.cdnfly_api_url.label": "Cdnfly API URL",
- "access.form.cdnfly_api_url.placeholder": "Please enter Cdnfly API URL",
+ "access.form.cdnfly_api_url.label": "Cdnfly URL",
+ "access.form.cdnfly_api_url.placeholder": "Please enter Cdnfly URL",
"access.form.cdnfly_api_key.label": "Cdnfly user API key",
"access.form.cdnfly_api_key.placeholder": "Please enter Cdnfly user API key",
"access.form.cdnfly_api_key.tooltip": "For more information, see https://doc.cdnfly.cn/shiyongjieshao.html",
@@ -184,6 +201,21 @@
"access.form.email_default_sender_address.placeholder": "Please enter default sender email address",
"access.form.email_default_receiver_address.label": "Default receiver email address (Optional)",
"access.form.email_default_receiver_address.placeholder": "Please enter default receiver email address",
+ "access.form.flexcdn_api_url.label": "FlexCDN URL",
+ "access.form.flexcdn_api_url.placeholder": "Please enter FlexCDN URL",
+ "access.form.flexcdn_api_role.label": "FlexCDN user role",
+ "access.form.flexcdn_api_role.placeholder": "Please select FlexCDN user role",
+ "access.form.flexcdn_api_role.option.user.label": "Platform user",
+ "access.form.flexcdn_api_role.option.admin.label": "Administrator user",
+ "access.form.flexcdn_access_key_id.label": "FlexCDN AccessKeyId",
+ "access.form.flexcdn_access_key_id.placeholder": "Please enter FlexCDN AccessKeyId",
+ "access.form.flexcdn_access_key_id.tooltip": "For more information, see https://flexcdn.cn/docs/api/auth",
+ "access.form.flexcdn_access_key.label": "FlexCDN AccessKey",
+ "access.form.flexcdn_access_key.placeholder": "Please enter FlexCDN AccessKey",
+ "access.form.flexcdn_access_key.tooltip": "For more information, see https://flexcdn.cn/docs/api/auth",
+ "access.form.flexcdn_allow_insecure_conns.label": "Insecure SSL/TLS connections",
+ "access.form.flexcdn_allow_insecure_conns.switch.on": "Allow",
+ "access.form.flexcdn_allow_insecure_conns.switch.off": "Disallow",
"access.form.gcore_api_token.label": "Gcore API token",
"access.form.gcore_api_token.placeholder": "Please enter Gcore API token",
"access.form.gcore_api_token.tooltip": "For more information, see https://api.gcore.com/docs/iam#section/Authentication",
@@ -199,8 +231,8 @@
"access.form.godaddy_api_secret.label": "GoDaddy API secret",
"access.form.godaddy_api_secret.placeholder": "Please enter GoDaddy API secret",
"access.form.godaddy_api_secret.tooltip": "For more information, see https://developer.godaddy.com/",
- "access.form.goedge_api_url.label": "GoEdge API URL",
- "access.form.goedge_api_url.placeholder": "Please enter GoEdge API URL",
+ "access.form.goedge_api_url.label": "GoEdge URL",
+ "access.form.goedge_api_url.placeholder": "Please enter GoEdge URL",
"access.form.goedge_api_role.label": "GoEdge user role",
"access.form.goedge_api_role.placeholder": "Please select GoEdge user role",
"access.form.goedge_api_role.option.user.label": "Platform user",
@@ -238,6 +270,21 @@
"access.form.larkbot_webhook_url.label": "Lark bot Webhook URL",
"access.form.larkbot_webhook_url.placeholder": "Please enter Lark bot Webhook URL",
"access.form.larkbot_webhook_url.tooltip": "For more information, see https://www.feishu.cn/hc/en-US/articles/807992406756",
+ "access.form.lecdn_api_url.label": "LeCDN URL",
+ "access.form.lecdn_api_url.placeholder": "Please enter LeCDN URL",
+ "access.form.lecdn_api_version.label": "LeCDN version",
+ "access.form.lecdn_api_version.placeholder": "Please select LeCDN version",
+ "access.form.lecdn_api_role.label": "LeCDN user role",
+ "access.form.lecdn_api_role.placeholder": "Please select LeCDN user role",
+ "access.form.lecdn_api_role.option.client.label": "Client",
+ "access.form.lecdn_api_role.option.master.label": "Master",
+ "access.form.lecdn_username.label": "LeCDN username",
+ "access.form.lecdn_username.placeholder": "Please enter LeCDN username",
+ "access.form.lecdn_password.label": "LeCDN password",
+ "access.form.lecdn_password.placeholder": "Please enter GoEdge password",
+ "access.form.lecdn_allow_insecure_conns.label": "Insecure SSL/TLS connections",
+ "access.form.lecdn_allow_insecure_conns.switch.on": "Allow",
+ "access.form.lecdn_allow_insecure_conns.switch.off": "Disallow",
"access.form.mattermost_server_url.label": "Mattermost server URL",
"access.form.mattermost_server_url.placeholder": "Please enter Mattermost server URL",
"access.form.mattermost_username.label": "Mattermost username",
@@ -262,8 +309,8 @@
"access.form.namesilo_api_key.label": "NameSilo API key",
"access.form.namesilo_api_key.placeholder": "Please enter NameSilo API key",
"access.form.namesilo_api_key.tooltip": "For more information, see https://www.namesilo.com/support/v2/articles/account-options/api-manager",
- "access.form.netlify_api_token.label": "netlify API token",
- "access.form.netlify_api_token.placeholder": "Please enter netlify API token",
+ "access.form.netlify_api_token.label": "Netlify API token",
+ "access.form.netlify_api_token.placeholder": "Please enter Netlify API token",
"access.form.netlify_api_token.tooltip": "For more information, see https://docs.netlify.com/api/get-started/#authentication",
"access.form.netcup_customer_number.label": "netcup customer number",
"access.form.netcup_customer_number.placeholder": "Please enter netcup customer number",
@@ -283,8 +330,8 @@
"access.form.porkbun_secret_api_key.label": "Porkbun secret API key",
"access.form.porkbun_secret_api_key.placeholder": "Please enter Porkbun secret API key",
"access.form.porkbun_secret_api_key.tooltip": "For more information, see https://porkbun.com/api/json/v3/documentation",
- "access.form.powerdns_api_url.label": "PowerDNS API URL",
- "access.form.powerdns_api_url.placeholder": "Please enter PowerDNS API URL",
+ "access.form.powerdns_api_url.label": "PowerDNS URL",
+ "access.form.powerdns_api_url.placeholder": "Please enter PowerDNS URL",
"access.form.powerdns_api_key.label": "PowerDNS API key",
"access.form.powerdns_api_key.placeholder": "Please enter PowerDNS API key",
"access.form.powerdns_api_key.tooltip": "For more information, see https://doc.powerdns.com/authoritative/http-api/index.html#enabling-the-api",
@@ -311,6 +358,17 @@
"access.form.rainyun_api_key.label": "Rain Yun API key",
"access.form.rainyun_api_key.placeholder": "Please enter Rain Yun API key",
"access.form.rainyun_api_key.tooltip": "For more information, see https://app.rainyun.com/account/settings/api-key",
+ "access.form.ratpanel_api_url.label": "RatPanel URL",
+ "access.form.ratpanel_api_url.placeholder": "Please enter RatPanel URL",
+ "access.form.ratpanel_access_token_id.label": "RatPanel access token ID",
+ "access.form.ratpanel_access_token_id.placeholder": "Please enter RatPanel access token ID",
+ "access.form.ratpanel_access_token_id.tooltip": "For more information, see https://ratpanel.github.io/advanced/api.html",
+ "access.form.ratpanel_access_token.label": "RatPanel access token",
+ "access.form.ratpanel_access_token.placeholder": "Please enter RatPanel access token",
+ "access.form.ratpanel_access_token.tooltip": "For more information, see https://ratpanel.github.io/advanced/api.html",
+ "access.form.ratpanel_allow_insecure_conns.label": "Insecure SSL/TLS connections",
+ "access.form.ratpanel_allow_insecure_conns.switch.on": "Allow",
+ "access.form.ratpanel_allow_insecure_conns.switch.off": "Disallow",
"access.form.safeline_api_url.label": "SafeLine URL",
"access.form.safeline_api_url.placeholder": "Please enter SafeLine URL",
"access.form.safeline_api_token.label": "SafeLine API token",
@@ -334,6 +392,9 @@
"access.form.ssh_key_passphrase.label": "SSH key passphrase (Optional)",
"access.form.ssh_key_passphrase.placeholder": "Please enter SSH key passphrase",
"access.form.ssh_key_passphrase.tooltip": "Optional when using key to connect to SSH.",
+ "access.form.ssh_jump_servers.label": "SSH jump server (Optional)",
+ "access.form.ssh_jump_servers.item.label": "Jump server",
+ "access.form.ssh_jump_servers.add": "Add jump server",
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
"access.form.sslcom_eab_kid.placeholder": "Please enter ACME EAB KID",
"access.form.sslcom_eab_kid.tooltip": "For more information, see https://www.ssl.com/how-to/generate-acme-credentials-for-reseller-customers/",
@@ -343,9 +404,9 @@
"access.form.telegram_bot_token.label": "Telegram bot token",
"access.form.telegram_bot_token.placeholder": "Please enter Telegram bot token",
"access.form.telegram_bot_token.tooltip": "How to get the bot token? Please refer to https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a",
- "access.form.telegram_default_chat_id.label": "Default Telegram chat ID (Optional)",
- "access.form.telegram_default_chat_id.placeholder": "Please enter default Telegram chat ID",
- "access.form.telegram_default_chat_id.tooltip": "How to get the chat ID? Please refer to https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a",
+ "access.form.telegram_bot_default_chat_id.label": "Default Telegram chat ID (Optional)",
+ "access.form.telegram_bot_default_chat_id.placeholder": "Please enter default Telegram chat ID",
+ "access.form.telegram_bot_default_chat_id.tooltip": "How to get the chat ID? Please refer to https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a",
"access.form.tencentcloud_secret_id.label": "Tencent Cloud SecretId",
"access.form.tencentcloud_secret_id.placeholder": "Please enter Tencent Cloud SecretId",
"access.form.tencentcloud_secret_id.tooltip": "For more information, see https://cloud.tencent.com/document/product/598/40488?lang=en",
diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json
index 6d67f55c..a3bbfecc 100644
--- a/ui/src/i18n/locales/en/nls.provider.json
+++ b/ui/src/i18n/locales/en/nls.provider.json
@@ -2,7 +2,8 @@
"provider.1panel": "1Panel",
"provider.1panel.console": "1Panel - Console",
"provider.1panel.site": "1Panel - Website",
- "provider.acmehttpreq": "Http Request (ACME Proxy)",
+ "provider.acmeca": "ACME Custom CA Endpoint",
+ "provider.acmehttpreq": "ACME Custom HTTP Endpoint",
"provider.aliyun": "Alibaba Cloud",
"provider.aliyun.alb": "Alibaba Cloud - ALB (Application Load Balancer)",
"provider.aliyun.apigw": "Alibaba Cloud - API Gateway",
@@ -15,6 +16,7 @@
"provider.aliyun.dns": "Alibaba Cloud - DNS (Domain Name Service)",
"provider.aliyun.esa": "Alibaba Cloud - ESA (Edge Security Acceleration)",
"provider.aliyun.fc": "Alibaba Cloud - FC (Function Compute)",
+ "provider.aliyun.ga": "Alibaba Cloud - GA (Global Accelerator)",
"provider.aliyun.live": "Alibaba Cloud - ApsaraVideo Live",
"provider.aliyun.nlb": "Alibaba Cloud - NLB (Network Load Balancer)",
"provider.aliyun.oss": "Alibaba Cloud - OSS (Object Storage Service)",
@@ -40,6 +42,9 @@
"provider.baotapanel": "aaPanel (aka BaoTaPanel)",
"provider.baotapanel.console": "aaPanel (aka BaoTaPanel) - Console",
"provider.baotapanel.site": "aaPanel (aka BaoTaPanel) - Website",
+ "provider.baotawaf": "aaWAF (aka BaotaWAF)",
+ "provider.baotawaf.console": "aaWAF (aka BaotaWAF) - Console",
+ "provider.baotawaf.site": "aaWAF (aka BaotaWAF) - Website",
"provider.bunny": "Bunny",
"provider.bunny.cdn": "Bunny - CDN (Content Delivery Network)",
"provider.byteplus": "BytePlus",
@@ -62,6 +67,7 @@
"provider.edgio.applications": "Edgio - Applications",
"provider.email": "Email",
"provider.fastly": "Fastly",
+ "provider.flexcdn": "FlexCDN",
"provider.gcore": "Gcore",
"provider.gcore.cdn": "Gcore - CDN (Content Delivery Network)",
"provider.gname": "GNAME",
@@ -83,6 +89,7 @@
"provider.kubernetes": "Kubernetes",
"provider.kubernetes.secret": "Kubernetes - Secret",
"provider.larkbot": "Lark Bot",
+ "provider.lecdn": "LeCDN",
"provider.letsencrypt": "Let's Encrypt",
"provider.letsencryptstaging": "Let's Encrypt Staging Environment",
"provider.local": "Local deployment",
@@ -91,8 +98,8 @@
"provider.namedotcom": "Name.com",
"provider.namesilo": "NameSilo",
"provider.netcup": "netcup",
- "provider.netlify": "netlify",
- "provider.netlify.site": "netlify - Site",
+ "provider.netlify": "Netlify",
+ "provider.netlify.site": "Netlify - Site",
"provider.ns1": "NS1 (IBM NS1 Connect)",
"provider.porkbun": "Porkbun",
"provider.powerdns": "PowerDNS",
@@ -103,10 +110,13 @@
"provider.qiniu.pili": "Qiniu - Pili",
"provider.rainyun": "Rain Yun",
"provider.rainyun.rcdn": "Rain Yun - RCDN (Rain Content Delivery Network)",
+ "provider.ratpanel": "RatPanel",
+ "provider.ratpanel.console": "RatPanel - Console",
+ "provider.ratpanel.site": "RatPanel - Website",
"provider.safeline": "SafeLine",
"provider.ssh": "SSH deployment",
"provider.sslcom": "SSL.com",
- "provider.telegram": "Telegram",
+ "provider.telegrambot": "Telegram Bot",
"provider.tencentcloud": "Tencent Cloud",
"provider.tencentcloud.cdn": "Tencent Cloud - CDN (Content Delivery Network)",
"provider.tencentcloud.clb": "Tencent Cloud - CLB (Cloud Load Balancer)",
@@ -138,7 +148,9 @@
"provider.volcengine.live": "Volcengine - Live",
"provider.volcengine.tos": "Volcengine - TOS (Tinder Object Storage)",
"provider.wangsu": "Wangsu Cloud",
- "provider.wangsu.cdnpro": "Wangsu Cloud - CDN Pro",
+ "provider.wangsu.cdn": "Wangsu Cloud - CDN (Content Delivery Network)",
+ "provider.wangsu.cdnpro": "Wangsu Cloud - CDN Pro (CDN 360)",
+ "provider.wangsu.certificate_upload": "Wangsu Cloud - Upload to Certificate Management",
"provider.webhook": "Webhook",
"provider.wecombot": "WeCom Bot",
"provider.westcn": "West.cn",
@@ -150,8 +162,10 @@
"provider.category.loadbalance": "Loadbalance",
"provider.category.firewall": "Firewall",
"provider.category.av": "Audio/Video",
+ "provider.category.apigw": "API Gateway",
"provider.category.serverless": "Serverless",
"provider.category.website": "Website",
+ "provider.category.ssl": "SSL",
"provider.category.nas": "NAS",
"provider.category.other": "Other",
diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json
index c9468b33..80237287 100644
--- a/ui/src/i18n/locales/en/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json
@@ -213,6 +213,19 @@
"workflow_node.deploy.form.aliyun_fc_domain.label": "Alibaba Cloud FC domain",
"workflow_node.deploy.form.aliyun_fc_domain.placeholder": "Please enter Alibaba Cloud FC domain name",
"workflow_node.deploy.form.aliyun_fc_domain.tooltip": "For more information, see https://fcnext.console.aliyun.com",
+ "workflow_node.deploy.form.aliyun_ga_resource_type.label": "Resource type",
+ "workflow_node.deploy.form.aliyun_ga_resource_type.placeholder": "Please select resource type",
+ "workflow_node.deploy.form.aliyun_ga_resource_type.option.accelerator.label": "GA accelerator",
+ "workflow_node.deploy.form.aliyun_ga_resource_type.option.listener.label": "GA listener",
+ "workflow_node.deploy.form.aliyun_ga_accelerator_id.label": "Alibaba Cloud GA accelerator ID",
+ "workflow_node.deploy.form.aliyun_ga_accelerator_id.placeholder": "Please enter Alibaba Cloud GA accelerator ID",
+ "workflow_node.deploy.form.aliyun_ga_accelerator_id.tooltip": "For more information, https://ga.console.aliyun.com",
+ "workflow_node.deploy.form.aliyun_ga_listener_id.label": "Alibaba Cloud GA listener ID",
+ "workflow_node.deploy.form.aliyun_ga_listener_id.placeholder": "Please enter Alibaba Cloud GA listener ID",
+ "workflow_node.deploy.form.aliyun_ga_listener_id.tooltip": "For more information, https://ga.console.aliyun.com",
+ "workflow_node.deploy.form.aliyun_ga_snidomain.label": "Alibaba Cloud GA SNI domain (Optional)",
+ "workflow_node.deploy.form.aliyun_ga_snidomain.placeholder": "Please enter Alibaba Cloud GA SNI domain name",
+ "workflow_node.deploy.form.aliyun_ga_snidomain.tooltip": "For more information, https://ga.console.aliyun.com",
"workflow_node.deploy.form.aliyun_live_region.label": "Alibaba Cloud Live region",
"workflow_node.deploy.form.aliyun_live_region.placeholder": "Please enter Alibaba Cloud Live region (e.g. cn-hangzhou)",
"workflow_node.deploy.form.aliyun_live_region.tooltip": "For more information, see https://www.alibabacloud.com/help/en/live/product-overview/supported-regions",
@@ -325,13 +338,18 @@
"workflow_node.deploy.form.baotapanel_site_type.option.other.label": "Other sites",
"workflow_node.deploy.form.baotapanel_site_name.label": "aaPanel site name",
"workflow_node.deploy.form.baotapanel_site_name.placeholder": "Please enter aaPanel site name",
- "workflow_node.deploy.form.baotapanel_site_name.tooltip": "Usually equal to the website domain name.",
+ "workflow_node.deploy.form.baotapanel_site_name.tooltip": "You can find it on aaPanel WebUI.",
"workflow_node.deploy.form.baotapanel_site_names.label": "aaPanel site names",
"workflow_node.deploy.form.baotapanel_site_names.placeholder": "Please enter aaPanel site names (separated by semicolons)",
"workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid": "Please enter a valid aaPanel site name",
- "workflow_node.deploy.form.baotapanel_site_names.tooltip": "Usually equal to the websites domain name.",
+ "workflow_node.deploy.form.baotapanel_site_names.tooltip": "You can find it on aaPanel WebUI.",
"workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title": "Change aaPanel site names",
"workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder": "Please enter aaPanel site name",
+ "workflow_node.deploy.form.baotawaf_site_name.label": "aaWAF site name",
+ "workflow_node.deploy.form.baotawaf_site_name.placeholder": "Please enter aaWAF site name",
+ "workflow_node.deploy.form.baotawaf_site_name.tooltip": "You can find it on aaWAF WebUI.",
+ "workflow_node.deploy.form.baotawaf_site_port.label": "aaWAF site SSL port",
+ "workflow_node.deploy.form.baotawaf_site_port.placeholder": "Please enter aaWAF SSL port",
"workflow_node.deploy.form.bunny_cdn_pull_zone_id.label": "Bunny CDN pull zone ID",
"workflow_node.deploy.form.bunny_cdn_pull_zone_id.placeholder": "Please enter Bunny CDN pull zone ID",
"workflow_node.deploy.form.bunny_cdn_pull_zone_id.tooltip": "What is this? See https://dash.bunny.net/cdn",
@@ -347,14 +365,22 @@
"workflow_node.deploy.form.cdnfly_resource_type.option.certificate.label": "Certificate",
"workflow_node.deploy.form.cdnfly_site_id.label": "Cdnfly site ID",
"workflow_node.deploy.form.cdnfly_site_id.placeholder": "Please enter Cdnfly site ID",
+ "workflow_node.deploy.form.cdnfly_site_id.tooltip": "You can find it on Cdnfly WebUI.",
"workflow_node.deploy.form.cdnfly_certificate_id.label": "Cdnfly certificate ID",
"workflow_node.deploy.form.cdnfly_certificate_id.placeholder": "Please enter Cdnfly certificate ID",
+ "workflow_node.deploy.form.cdnfly_certificate_id.tooltip": "You can find it on Cdnfly WebUI.",
"workflow_node.deploy.form.dogecloud_cdn_domain.label": "Doge Cloud CDN domain",
"workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "Please enter Doge Cloud CDN domain name",
"workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "For more information, see https://console.dogecloud.com/",
"workflow_node.deploy.form.edgio_applications_environment_id.label": "Edgio Applications environment ID",
"workflow_node.deploy.form.edgio_applications_environment_id.placeholder": "Please enter Edgio Applications environment ID",
"workflow_node.deploy.form.edgio_applications_environment_id.tooltip": "For more information, see https://edgio.app/",
+ "workflow_node.deploy.form.flexcdn_resource_type.label": "Resource type",
+ "workflow_node.deploy.form.flexcdn_resource_type.placeholder": "Please select resource type",
+ "workflow_node.deploy.form.flexcdn_resource_type.option.certificate.label": "Certificate",
+ "workflow_node.deploy.form.flexcdn_certificate_id.label": "FlexCDN certificate ID",
+ "workflow_node.deploy.form.flexcdn_certificate_id.placeholder": "Please enter FlexCDN certificate ID",
+ "workflow_node.deploy.form.flexcdn_certificate_id.tooltip": "You can find it on FlexCDN WebUI.",
"workflow_node.deploy.form.gcore_cdn_resource_id.label": "Gcore CDN resource ID",
"workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "Please enter Gcore CDN resource ID",
"workflow_node.deploy.form.gcore_cdn_resource_id.tooltip": "For more information, see https://cdn.gcore.com/resources/list",
@@ -366,6 +392,7 @@
"workflow_node.deploy.form.goedge_resource_type.option.certificate.label": "Certificate",
"workflow_node.deploy.form.goedge_certificate_id.label": "GoEdge certificate ID",
"workflow_node.deploy.form.goedge_certificate_id.placeholder": "Please enter GoEdge certificate ID",
+ "workflow_node.deploy.form.goedge_certificate_id.tooltip": "You can find it on GoEdge WebUI.",
"workflow_node.deploy.form.huaweicloud_cdn_region.label": "Huawei Cloud CDN region",
"workflow_node.deploy.form.huaweicloud_cdn_region.placeholder": "Please enter Huawei Cloud CDN region (e.g. cn-north-1)",
"workflow_node.deploy.form.huaweicloud_cdn_region.tooltip": "For more information, see https://console-intl.huaweicloud.com/apiexplorer/#/endpoint",
@@ -443,6 +470,15 @@
"workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret data key for private key",
"workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "Please enter Kubernetes Secret data key for private key",
"workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "For more information, see https://kubernetes.io/docs/concepts/configuration/secret/",
+ "workflow_node.deploy.form.lecdn_resource_type.label": "Resource type",
+ "workflow_node.deploy.form.lecdn_resource_type.placeholder": "Please select resource type",
+ "workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "Certificate",
+ "workflow_node.deploy.form.lecdn_certificate_id.label": "LeCDN certificate ID",
+ "workflow_node.deploy.form.lecdn_certificate_id.placeholder": "Please enter LeCDN certificate ID",
+ "workflow_node.deploy.form.lecdn_certificate_id.tooltip": "You can find it on LeCDN WebUI.",
+ "workflow_node.deploy.form.lecdn_client_id.label": "LeCDN user ID (Optional)",
+ "workflow_node.deploy.form.lecdn_client_id.placeholder": "Please enter LeCDN user ID",
+ "workflow_node.deploy.form.lecdn_client_id.tooltip": "You can find it on LeCDN WebUI.
Required when using administrator's authorization. It Must be the same as the user to which the certificate belongs.",
"workflow_node.deploy.form.local.guide": "Tips: If you are running Certimate in Docker, the \"Local\" refers to the container rather than the host.",
"workflow_node.deploy.form.local_format.label": "File format",
"workflow_node.deploy.form.local_format.placeholder": "Please select file format",
@@ -489,8 +525,8 @@
"workflow_node.deploy.form.local_preset_scripts.option.ps_binding_iis.label": "PowerShell - Binding IIS",
"workflow_node.deploy.form.local_preset_scripts.option.ps_binding_netsh.label": "PowerShell - Binding netsh",
"workflow_node.deploy.form.local_preset_scripts.option.ps_.label": "PowerShell - Binding RDP",
- "workflow_node.deploy.form.netlify_site_id.label": "netlify site ID",
- "workflow_node.deploy.form.netlify_site_id.placeholder": "Please enter netlify site ID",
+ "workflow_node.deploy.form.netlify_site_id.label": "Netlify site ID",
+ "workflow_node.deploy.form.netlify_site_id.placeholder": "Please enter Netlify site ID",
"workflow_node.deploy.form.netlify_site_id.tooltip": "For more information, see https://docs.netlify.com/api/get-started/#get-site",
"workflow_node.deploy.form.proxmoxve_node_name.label": "Proxmox VE cluster node name",
"workflow_node.deploy.form.proxmoxve_node_name.placeholder": "Please enter Proxmox VE cluster node name",
@@ -513,11 +549,15 @@
"workflow_node.deploy.form.rainyun_rcdn_domain.label": "Rain Yun RCDN domain",
"workflow_node.deploy.form.rainyun_rcdn_domain.placeholder": "Please enter Rain Yun RCDN domain name",
"workflow_node.deploy.form.rainyun_rcdn_domain.tooltip": "For more information, see https://app.rainyun.com/apps/rcdn/list",
+ "workflow_node.deploy.form.ratpanel_site_name.label": "RatPanel site name",
+ "workflow_node.deploy.form.ratpanel_site_name.placeholder": "Please enter RatPanel site name",
+ "workflow_node.deploy.form.ratpanel_site_name.tooltip": "You can find it on RatPanel WebUI.",
"workflow_node.deploy.form.safeline_resource_type.label": "Resource type",
"workflow_node.deploy.form.safeline_resource_type.placeholder": "Please select resource type",
"workflow_node.deploy.form.safeline_resource_type.option.certificate.label": "Certificate",
"workflow_node.deploy.form.safeline_certificate_id.label": "SafeLine certificate ID",
"workflow_node.deploy.form.safeline_certificate_id.placeholder": "Please enter SafeLine certificate ID",
+ "workflow_node.deploy.form.safeline_certificate_id.tooltip": "You can find it on SafeLine WebUI.",
"workflow_node.deploy.form.ssh_format.label": "File format",
"workflow_node.deploy.form.ssh_format.placeholder": "Please select file format",
"workflow_node.deploy.form.ssh_format.option.pem.label": "PEM (*.pem, *.crt, *.key)",
@@ -720,6 +760,11 @@
"workflow_node.deploy.form.volcengine_tos_domain.label": "VolcEngine TOS domain",
"workflow_node.deploy.form.volcengine_tos_domain.placeholder": "Please enter VolcEngine TOS domain name",
"workflow_node.deploy.form.volcengine_tos_domain.tooltip": "For more information, see https://console.volcengine.com/tos",
+ "workflow_node.deploy.form.wangsu_cdn_domains.label": "Wangsu Cloud CDN domains",
+ "workflow_node.deploy.form.wangsu_cdn_domains.placeholder": "Please enter Wangsu Cloud CDN domain names (separated by semicolons)",
+ "workflow_node.deploy.form.wangsu_cdn_domains.tooltip": "For more information, see https://cdn.console.wangsu.com/v2/index/#/property/list",
+ "workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.title": "Change Wangsu Cloud CDN domains",
+ "workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.placeholder": "Please enter Wangsu Cloud CDN domain",
"workflow_node.deploy.form.wangsu_cdnpro_environment.label": "Wangsu Cloud environment",
"workflow_node.deploy.form.wangsu_cdnpro_environment.placeholder": "Please select Wangsu Cloud environment",
"workflow_node.deploy.form.wangsu_cdnpro_environment.option.production.label": "Production environment",
@@ -733,6 +778,9 @@
"workflow_node.deploy.form.wangsu_cdnpro_webhook_id.label": "Wangsu Cloud CDN Webhook ID (Optional)",
"workflow_node.deploy.form.wangsu_cdnpro_webhook_id.placeholder": "Please enter Wangsu Cloud CDN Webhook ID",
"workflow_node.deploy.form.wangsu_cdnpro_webhook_id.tooltip": "For more information, see https://cdnpro.console.wangsu.com/v2/index/#/certificate",
+ "workflow_node.deploy.form.wangsu_certificate_id.label": "Wangsu Cloud certificate ID (Optional)",
+ "workflow_node.deploy.form.wangsu_certificate_id.placeholder": "Please enter Wangsu Cloud certificate ID",
+ "workflow_node.deploy.form.wangsu_certificate_id.tooltip": "For more information, see https://cdn.console.wangsu.com/v2/index#/certificate/list",
"workflow_node.deploy.form.webhook_data.label": "Webhook data (Optional)",
"workflow_node.deploy.form.webhook_data.placeholder": "Please enter Webhook data to override the default value",
"workflow_node.deploy.form.webhook_data.tooltip": "Leave it blank to use the default Webhook data provided by the authorization.",
@@ -776,9 +824,9 @@
"workflow_node.notify.form.mattermost_channel_id.label": "Mattermost channel ID (Optional)",
"workflow_node.notify.form.mattermost_channel_id.placeholder": "Please enter Mattermost channel ID to override the default value",
"workflow_node.notify.form.mattermost_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.",
- "workflow_node.notify.form.telegram_chat_id.label": "Telegram chat ID (Optional)",
- "workflow_node.notify.form.telegram_chat_id.placeholder": "Please enter Telegram chat ID to override the default value",
- "workflow_node.notify.form.telegram_chat_id.tooltip": "Leave it blank to use the default chat ID provided by the selected authorization.",
+ "workflow_node.notify.form.telegram_bot_chat_id.label": "Telegram chat ID (Optional)",
+ "workflow_node.notify.form.telegram_bot_chat_id.placeholder": "Please enter Telegram chat ID to override the default value",
+ "workflow_node.notify.form.telegram_bot_chat_id.tooltip": "Leave it blank to use the default chat ID provided by the selected authorization.",
"workflow_node.notify.form.webhook_data.label": "Webhook data (Optional)",
"workflow_node.notify.form.webhook_data.placeholder": "Please enter Webhook data to override the default value",
"workflow_node.notify.form.webhook_data.tooltip": "Leave it blank to use the default Webhook data provided by the authorization.",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index f73065ea..a5e89438 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -36,12 +36,21 @@
"access.form.notification_channel.placeholder": "请选择通知渠道",
"access.form.1panel_api_url.label": "1Panel URL",
"access.form.1panel_api_url.placeholder": "请输入 1Panel URL",
+ "access.form.1panel_api_version.label": "1Panel 版本",
+ "access.form.1panel_api_version.placeholder": "请选择 1Panel 版本",
"access.form.1panel_api_key.label": "1Panel 接口密钥",
"access.form.1panel_api_key.placeholder": "请输入 1Panel 接口密钥",
"access.form.1panel_api_key.tooltip": "这是什么?请参阅 https://1panel.cn/docs/dev_manual/api_manual/",
"access.form.1panel_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误",
"access.form.1panel_allow_insecure_conns.switch.on": "允许",
"access.form.1panel_allow_insecure_conns.switch.off": "不允许",
+ "access.form.acmeca_endpoint.label": "服务端点",
+ "access.form.acmeca_endpoint.placeholder": "请输入服务端点",
+ "access.form.acmeca_endpoint.tooltip": "这是什么?请参阅 https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1",
+ "access.form.acmeca_eab_kid.label": "ACME EAB KID(可选)",
+ "access.form.acmeca_eab_kid.placeholder": "请输入 ACME EAB KID",
+ "access.form.acmeca_eab_hmac_key.label": "ACME EAB HMAC Key(可选)",
+ "access.form.acmeca_eab_hmac_key.placeholder": "请输入 ACME EAB HMAC Key",
"access.form.acmehttpreq_endpoint.label": "服务端点",
"access.form.acmehttpreq_endpoint.placeholder": "请输入服务端点",
"access.form.acmehttpreq_endpoint.tooltip": "这是什么?请参阅 https://go-acme.github.io/lego/dns/httpreq/",
@@ -94,6 +103,14 @@
"access.form.baotapanel_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误",
"access.form.baotapanel_allow_insecure_conns.switch.on": "允许",
"access.form.baotapanel_allow_insecure_conns.switch.off": "不允许",
+ "access.form.baotawaf_api_url.label": "堡塔云 WAF URL",
+ "access.form.baotawaf_api_url.placeholder": "请输入堡塔云 WAF URL",
+ "access.form.baotawaf_api_key.label": "堡塔云 WAF 接口密钥",
+ "access.form.baotawaf_api_key.placeholder": "请输入 堡塔云 WAF 接口密钥",
+ "access.form.baotawaf_api_key.tooltip": "这是什么?请参阅 https://github.com/aaPanel/aaWAF/blob/main/API.md",
+ "access.form.baotawaf_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误",
+ "access.form.baotawaf_allow_insecure_conns.switch.on": "允许",
+ "access.form.baotawaf_allow_insecure_conns.switch.off": "不允许",
"access.form.bunny_api_key.label": "Bunny API Key",
"access.form.bunny_api_key.placeholder": "请输入 Bunny API Key",
"access.form.bunny_api_key.tooltip": "这是什么?请参阅 https://docs.bunny.net/reference/bunnynet-api-overview",
@@ -106,8 +123,8 @@
"access.form.cachefly_api_token.label": "CacheFly API Token",
"access.form.cachefly_api_token.placeholder": "请输入 CacheFly API Token",
"access.form.cachefly_api_token.tooltip": "这是什么?请参阅 https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228",
- "access.form.cdnfly_api_url.label": "Cdnfly API URL",
- "access.form.cdnfly_api_url.placeholder": "请输入 Cdnfly API URL",
+ "access.form.cdnfly_api_url.label": "Cdnfly URL",
+ "access.form.cdnfly_api_url.placeholder": "请输入 Cdnfly URL",
"access.form.cdnfly_api_key.label": "Cdnfly 用户端 API Key",
"access.form.cdnfly_api_key.placeholder": "请输入 Cdnfly 用户端 API Key",
"access.form.cdnfly_api_key.tooltip": "这是什么?请参阅 https://doc.cdnfly.cn/shiyongjieshao.html",
@@ -178,6 +195,21 @@
"access.form.email_default_sender_address.placeholder": "请输入默认的发送邮箱地址",
"access.form.email_default_receiver_address.label": "默认的接收邮箱地址(可选)",
"access.form.email_default_receiver_address.placeholder": "请输入默认的接收邮箱地址",
+ "access.form.flexcdn_api_url.label": "FlexCDN URL",
+ "access.form.flexcdn_api_url.placeholder": "请输入 FlexCDN URL",
+ "access.form.flexcdn_api_role.label": "FlexCDN 用户角色",
+ "access.form.flexcdn_api_role.placeholder": "请选择 FlexCDN 用户角色",
+ "access.form.flexcdn_api_role.option.user.label": "平台用户",
+ "access.form.flexcdn_api_role.option.admin.label": "系统管理员",
+ "access.form.flexcdn_access_key_id.label": "FlexCDN AccessKeyId",
+ "access.form.flexcdn_access_key_id.placeholder": "请输入 FlexCDN AccessKeyId",
+ "access.form.flexcdn_access_key_id.tooltip": "这是什么?请参阅 https://flexcdn.cn/docs/api/auth",
+ "access.form.flexcdn_access_key.label": "FlexCDN AccessKey",
+ "access.form.flexcdn_access_key.placeholder": "请输入 FlexCDN AccessKey",
+ "access.form.flexcdn_access_key.tooltip": "这是什么?请参阅 https://flexcdn.cn/docs/api/auth",
+ "access.form.flexcdn_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误",
+ "access.form.flexcdn_allow_insecure_conns.switch.on": "允许",
+ "access.form.flexcdn_allow_insecure_conns.switch.off": "不允许",
"access.form.gcore_api_token.label": "Gcore API Token",
"access.form.gcore_api_token.placeholder": "请输入 Gcore API Token",
"access.form.gcore_api_token.tooltip": "这是什么?请参阅 https://api.gcore.com/docs/iam#section/Authentication",
@@ -193,8 +225,8 @@
"access.form.godaddy_api_secret.label": "GoDaddy API Secret",
"access.form.godaddy_api_secret.placeholder": "请输入 GoDaddy API Secret",
"access.form.godaddy_api_secret.tooltip": "这是什么?请参阅 https://developer.godaddy.com/",
- "access.form.goedge_api_url.label": "GoEdge API URL",
- "access.form.goedge_api_url.placeholder": "请输入 GoEdge API URL",
+ "access.form.goedge_api_url.label": "GoEdge URL",
+ "access.form.goedge_api_url.placeholder": "请输入 GoEdge URL",
"access.form.goedge_api_role.label": "GoEdge 用户角色",
"access.form.goedge_api_role.placeholder": "请选择 GoEdge 用户角色",
"access.form.goedge_api_role.option.user.label": "平台用户",
@@ -232,6 +264,21 @@
"access.form.larkbot_webhook_url.label": "飞书群机器人 Webhook 地址",
"access.form.larkbot_webhook_url.placeholder": "请输入飞书群机器人 Webhook 地址",
"access.form.larkbot_webhook_url.tooltip": "这是什么?请参阅 https://www.feishu.cn/hc/zh-CN/articles/807992406756",
+ "access.form.lecdn_api_url.label": "LeCDN URL",
+ "access.form.lecdn_api_url.placeholder": "请输入 LeCDN URL",
+ "access.form.lecdn_api_version.label": "LeCDN 版本",
+ "access.form.lecdn_api_version.placeholder": "请选择 LeCDN 版本",
+ "access.form.lecdn_api_role.label": "LeCDN 用户角色",
+ "access.form.lecdn_api_role.placeholder": "请选择 LeCDN 用户角色",
+ "access.form.lecdn_api_role.option.client.label": "客户用户",
+ "access.form.lecdn_api_role.option.master.label": "主控管理员",
+ "access.form.lecdn_username.label": "LeCDN 用户名",
+ "access.form.lecdn_username.placeholder": "请输入 LeCDN 用户名",
+ "access.form.lecdn_password.label": "LeCDN 用户密码",
+ "access.form.lecdn_password.placeholder": "请输入 LeCDN 用户密码",
+ "access.form.lecdn_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误",
+ "access.form.lecdn_allow_insecure_conns.switch.on": "允许",
+ "access.form.lecdn_allow_insecure_conns.switch.off": "不允许",
"access.form.mattermost_server_url.label": "Mattermost 服务地址",
"access.form.mattermost_server_url.placeholder": "请输入 Mattermost 服务地址",
"access.form.mattermost_username.label": "Mattermost 用户名",
@@ -256,8 +303,8 @@
"access.form.namesilo_api_key.label": "NameSilo API Key",
"access.form.namesilo_api_key.placeholder": "请输入 NameSilo API Key",
"access.form.namesilo_api_key.tooltip": "这是什么?请参阅 https://www.namesilo.com/support/v2/articles/account-options/api-manager",
- "access.form.netlify_api_token.label": "netlify API Token",
- "access.form.netlify_api_token.placeholder": "请输入 netlify API Token",
+ "access.form.netlify_api_token.label": "Netlify API Token",
+ "access.form.netlify_api_token.placeholder": "请输入 Netlify API Token",
"access.form.netlify_api_token.tooltip": "这是什么?请参阅 https://docs.netlify.com/api/get-started/#authentication",
"access.form.netcup_customer_number.label": "netcup 客户编号",
"access.form.netcup_customer_number.placeholder": "请输入 netcup 客户编号",
@@ -277,8 +324,8 @@
"access.form.porkbun_secret_api_key.label": "Porkbun Secret API Key",
"access.form.porkbun_secret_api_key.placeholder": "请输入 Porkbun Secret API Key",
"access.form.porkbun_secret_api_key.tooltip": "这是什么?请参阅 https://porkbun.com/api/json/v3/documentation",
- "access.form.powerdns_api_url.label": "PowerDNS API URL",
- "access.form.powerdns_api_url.placeholder": "请输入 PowerDNS API URL",
+ "access.form.powerdns_api_url.label": "PowerDNS URL",
+ "access.form.powerdns_api_url.placeholder": "请输入 PowerDNS URL",
"access.form.powerdns_api_key.label": "PowerDNS API Key",
"access.form.powerdns_api_key.placeholder": "请输入 PowerDNS API Key",
"access.form.powerdns_api_key.tooltip": "这是什么?请参阅 https://doc.powerdns.com/authoritative/http-api/index.html#enabling-the-api",
@@ -305,6 +352,17 @@
"access.form.rainyun_api_key.label": "雨云 API 密钥",
"access.form.rainyun_api_key.placeholder": "请输入雨云 API 密钥",
"access.form.rainyun_api_key.tooltip": "这是什么?请参阅 https://app.rainyun.com/account/settings/api-key",
+ "access.form.ratpanel_api_url.label": "耗子面板 URL",
+ "access.form.ratpanel_api_url.placeholder": "请输入耗子面板 URL",
+ "access.form.ratpanel_access_token_id.label": "耗子面板 AccessToken ID",
+ "access.form.ratpanel_access_token_id.placeholder": "请输入耗子面板 AccessToken ID",
+ "access.form.ratpanel_access_token_id.tooltip": "这是什么?请参阅 https://ratpanel.github.io/advanced/api.html",
+ "access.form.ratpanel_access_token.label": "耗子面板 AccessToken",
+ "access.form.ratpanel_access_token.placeholder": "请输入耗子面板 AccessToken",
+ "access.form.ratpanel_access_token.tooltip": "这是什么?请参阅 https://ratpanel.github.io/advanced/api.html",
+ "access.form.ratpanel_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误",
+ "access.form.ratpanel_allow_insecure_conns.switch.on": "允许",
+ "access.form.ratpanel_allow_insecure_conns.switch.off": "不允许",
"access.form.safeline_api_url.label": "雷池 URL",
"access.form.safeline_api_url.placeholder": "请输入雷池 URL",
"access.form.safeline_api_token.label": "雷池 API Token",
@@ -328,18 +386,21 @@
"access.form.ssh_key_passphrase.label": "SSH 密钥口令(可选)",
"access.form.ssh_key_passphrase.placeholder": "请输入 SSH 密钥口令",
"access.form.ssh_key_passphrase.tooltip": "使用 SSH 密钥连接到 SSH 时选填。",
+ "access.form.ssh_jump_servers.label": "SSH 跳板机(可选)",
+ "access.form.ssh_jump_servers.item.label": "跳板机",
+ "access.form.ssh_jump_servers.add": "添加跳板机",
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
"access.form.sslcom_eab_kid.placeholder": "请输入 ACME EAB KID",
"access.form.sslcom_eab_kid.tooltip": "这是什么?请参阅 https://www.ssl.com/how-to/generate-acme-credentials-for-reseller-customers/",
"access.form.sslcom_eab_hmac_key.label": "ACME EAB HMAC key",
"access.form.sslcom_eab_hmac_key.placeholder": "请输入 ACME EAB HMAC key",
"access.form.sslcom_eab_hmac_key.tooltip": "这是什么?请参阅 https://www.ssl.com/how-to/generate-acme-credentials-for-reseller-customers/",
- "access.form.telegram_bot_token.label": "Telegram 机器人 API Token",
- "access.form.telegram_bot_token.placeholder": "请输入 Telegram 机器人 API Token",
+ "access.form.telegram_bot_token.label": "Telegram 群机器人 API Token",
+ "access.form.telegram_bot_token.placeholder": "请输入 Telegram 群机器人 API Token",
"access.form.telegram_bot_token.tooltip": "如何获取机器人 API Token?请参阅 https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a",
- "access.form.telegram_default_chat_id.label": "默认的 Telegram 会话 ID(可选)",
- "access.form.telegram_default_chat_id.placeholder": "请输入默认的 Telegram 会话 ID",
- "access.form.telegram_default_chat_id.tooltip": "如何获取会话 ID?请参阅 https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a",
+ "access.form.telegram_bot_default_chat_id.label": "默认的 Telegram 会话 ID(可选)",
+ "access.form.telegram_bot_default_chat_id.placeholder": "请输入默认的 Telegram 会话 ID",
+ "access.form.telegram_bot_default_chat_id.tooltip": "如何获取会话 ID?请参阅 https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a",
"access.form.tencentcloud_secret_id.label": "腾讯云 SecretId",
"access.form.tencentcloud_secret_id.placeholder": "请输入腾讯云 SecretId",
"access.form.tencentcloud_secret_id.tooltip": "这是什么?请参阅 https://cloud.tencent.com/document/product/598/40488",
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index 93b5137d..b9fb8777 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -1,8 +1,9 @@
{
"provider.1panel": "1Panel",
- "provider.1panel.console": "1Panel - 面板",
+ "provider.1panel.console": "1Panel - 控制台",
"provider.1panel.site": "1Panel - 网站",
- "provider.acmehttpreq": "Http Request (ACME Proxy)",
+ "provider.acmeca": "ACME 自定义 CA 端点",
+ "provider.acmehttpreq": "ACME 自定义 HTTP 端点",
"provider.aliyun": "阿里云",
"provider.aliyun.alb": "阿里云 - 应用型负载均衡 ALB",
"provider.aliyun.apigw": "阿里云 - API 网关",
@@ -15,6 +16,7 @@
"provider.aliyun.dns": "阿里云 - 云解析 DNS",
"provider.aliyun.esa": "阿里云 - 边缘安全加速 ESA",
"provider.aliyun.fc": "阿里云 - 函数计算 FC",
+ "provider.aliyun.ga": "阿里云 - 全球加速 GA",
"provider.aliyun.live": "阿里云 - 视频直播 Live",
"provider.aliyun.nlb": "阿里云 - 网络型负载均衡 NLB",
"provider.aliyun.oss": "阿里云 - 对象存储 OSS",
@@ -38,8 +40,11 @@
"provider.baishan": "白山云",
"provider.baishan.cdn": "白山云 - 内容分发网络 CDN",
"provider.baotapanel": "宝塔面板",
- "provider.baotapanel.console": "宝塔面板 - 面板",
+ "provider.baotapanel.console": "宝塔面板 - 控制台",
"provider.baotapanel.site": "宝塔面板 - 网站",
+ "provider.baotawaf": "堡塔云 WAF",
+ "provider.baotawaf.console": "堡塔云 WAF - 控制台",
+ "provider.baotawaf.site": "堡塔云 WAF - 网站",
"provider.bunny": "Bunny",
"provider.bunny.cdn": "Bunny - 内容分发网络 CDN",
"provider.byteplus": "BytePlus",
@@ -62,6 +67,7 @@
"provider.edgio.applications": "Edgio - Applications",
"provider.email": "邮件",
"provider.fastly": "Fastly",
+ "provider.flexcdn": "FlexCDN",
"provider.gcore": "Gcore",
"provider.gcore.cdn": "Gcore - 内容分发网络 CDN",
"provider.gname": "GNAME",
@@ -83,6 +89,7 @@
"provider.kubernetes": "Kubernetes",
"provider.kubernetes.secret": "Kubernetes - Secret",
"provider.larkbot": "飞书群机器人",
+ "provider.lecdn": "LeCDN",
"provider.letsencrypt": "Let's Encrypt",
"provider.letsencryptstaging": "Let's Encrypt 测试环境",
"provider.local": "本地部署",
@@ -91,8 +98,8 @@
"provider.namedotcom": "Name.com",
"provider.namesilo": "NameSilo",
"provider.netcup": "netcup",
- "provider.netlify": "netlify",
- "provider.netlify.site": "netlify - Site",
+ "provider.netlify": "Netlify",
+ "provider.netlify.site": "Netlify - Site",
"provider.ns1": "NS1 (IBM NS1 Connect)",
"provider.porkbun": "Porkbun",
"provider.powerdns": "PowerDNS",
@@ -103,10 +110,13 @@
"provider.qiniu.pili": "七牛云 - 视频直播 Pili",
"provider.rainyun": "雨云",
"provider.rainyun.rcdn": "雨云 - 雨盾 CDN",
+ "provider.ratpanel": "耗子面板",
+ "provider.ratpanel.console": "耗子面板 - 控制台",
+ "provider.ratpanel.site": "耗子面板 - 网站",
"provider.safeline": "雷池",
"provider.ssh": "SSH 部署",
"provider.sslcom": "SSL.com",
- "provider.telegram": "Telegram",
+ "provider.telegrambot": "Telegram 群机器人",
"provider.tencentcloud": "腾讯云",
"provider.tencentcloud.cdn": "腾讯云 - 内容分发网络 CDN",
"provider.tencentcloud.clb": "腾讯云 - 负载均衡 CLB",
@@ -138,7 +148,9 @@
"provider.volcengine.live": "火山引擎 - 视频直播 Live",
"provider.volcengine.tos": "火山引擎 - 对象存储 TOS",
"provider.wangsu": "网宿云",
- "provider.wangsu.cdnpro": "网宿云 - CDN Pro",
+ "provider.wangsu.cdn": "网宿云 - 内容分发网络 CDN",
+ "provider.wangsu.cdnpro": "网宿云 - CDN Pro (CDN 360)",
+ "provider.wangsu.certificate_upload": "网宿云 - 上传到证书管理",
"provider.webhook": "Webhook",
"provider.wecombot": "企业微信群机器人",
"provider.westcn": "西部数码",
@@ -150,8 +162,10 @@
"provider.category.loadbalance": "负载均衡",
"provider.category.firewall": "防火墙",
"provider.category.av": "音视频",
+ "provider.category.apigw": "API 网关",
"provider.category.serverless": "Serverless",
"provider.category.website": "网站托管",
+ "provider.category.ssl": "证书托管",
"provider.category.nas": "NAS",
"provider.category.other": "其他",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index b8c98418..faf40816 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -109,18 +109,18 @@
"workflow_node.deploy.form.certificate.tooltip": "待部署证书来自之前的申请或上传节点。如果选项为空请先确保前序节点配置正确。",
"workflow_node.deploy.form.params_config.label": "参数设置",
"workflow_node.deploy.form.1panel_console_auto_restart.label": "部署后自动重启宝塔面板服务",
- "workflow_node.deploy.form.1panel_site_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.1panel_site_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.1panel_site_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.1panel_site_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.1panel_site_resource_type.option.website.label": "替换指定网站的证书",
"workflow_node.deploy.form.1panel_site_resource_type.option.certificate.label": "替换指定证书",
"workflow_node.deploy.form.1panel_site_website_id.label": "1Panel 网站 ID",
"workflow_node.deploy.form.1panel_site_website_id.placeholder": "请输入 1Panel 网站 ID",
- "workflow_node.deploy.form.1panel_site_website_id.tooltip": "请在 1Panel 管理面板查看。",
+ "workflow_node.deploy.form.1panel_site_website_id.tooltip": "请登录 1Panel 面板查看。",
"workflow_node.deploy.form.1panel_site_certificate_id.label": "1Panel 证书 ID",
"workflow_node.deploy.form.1panel_site_certificate_id.placeholder": "请输入 1Panel 证书 ID",
- "workflow_node.deploy.form.1panel_site_certificate_id.tooltip": "请在 1Panel 管理面板查看。",
- "workflow_node.deploy.form.aliyun_alb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.aliyun_alb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.1panel_site_certificate_id.tooltip": "请登录 1Panel 面板查看。",
+ "workflow_node.deploy.form.aliyun_alb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.aliyun_alb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.aliyun_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/QUIC 监听的证书",
"workflow_node.deploy.form.aliyun_alb_resource_type.option.listener.label": "替换指定负载均衡监听器的证书",
"workflow_node.deploy.form.aliyun_alb_region.label": "阿里云 ALB 服务地域",
@@ -170,8 +170,8 @@
"workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.tooltip": "这是什么?请参阅 https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-listcontact
不填写时,将使用系统联系人列表中的第一个。",
"workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.title": "修改阿里云联系人 ID",
"workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.placeholder": "请输入阿里云联系人 ID",
- "workflow_node.deploy.form.aliyun_clb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.aliyun_clb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.aliyun_clb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.aliyun_clb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.aliyun_clb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听的证书",
"workflow_node.deploy.form.aliyun_clb_resource_type.option.listener.label": "替换指定负载均衡监听的证书",
"workflow_node.deploy.form.aliyun_clb_region.label": "阿里云 CLB 服务地域",
@@ -212,14 +212,27 @@
"workflow_node.deploy.form.aliyun_fc_domain.label": "阿里云 FC 自定义域名",
"workflow_node.deploy.form.aliyun_fc_domain.placeholder": "请输入阿里云 FC 自定义域名(支持泛域名)",
"workflow_node.deploy.form.aliyun_fc_domain.tooltip": "这是什么?请参阅 see https://fcnext.console.aliyun.com/",
+ "workflow_node.deploy.form.aliyun_ga_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.aliyun_ga_resource_type.placeholder": "请选择证书部署方式",
+ "workflow_node.deploy.form.aliyun_ga_resource_type.option.accelerator.label": "替换指定全球加速器下的全部 HTTPS 监听的证书",
+ "workflow_node.deploy.form.aliyun_ga_resource_type.option.listener.label": "替换指定全球加速器监听器的证书",
+ "workflow_node.deploy.form.aliyun_ga_accelerator_id.label": "阿里云全球加速实例 ID",
+ "workflow_node.deploy.form.aliyun_ga_accelerator_id.placeholder": "请输入阿里云全球加速实例 ID",
+ "workflow_node.deploy.form.aliyun_ga_accelerator_id.tooltip": "这是什么?请参阅 https://ga.console.aliyun.com",
+ "workflow_node.deploy.form.aliyun_ga_listener_id.label": "阿里云全球加速监听 ID",
+ "workflow_node.deploy.form.aliyun_ga_listener_id.placeholder": "请输入阿里云全球加速监听 ID",
+ "workflow_node.deploy.form.aliyun_ga_listener_id.tooltip": "这是什么?请参阅 https://ga.console.aliyun.com",
+ "workflow_node.deploy.form.aliyun_ga_snidomain.label": "阿里云全球加速扩展域名(可选)",
+ "workflow_node.deploy.form.aliyun_ga_snidomain.placeholder": "请输入阿里云全球加速扩展域名",
+ "workflow_node.deploy.form.aliyun_ga_snidomain.tooltip": "这是什么?请参阅 https://ga.console.aliyun.com
不填写时,将替换监听器的默认证书;否则,将替换扩展域名证书。",
"workflow_node.deploy.form.aliyun_live_region.label": "阿里云视频直播服务地域",
"workflow_node.deploy.form.aliyun_live_region.placeholder": "请输入阿里云视频直播服务地域(例如:cn-hangzhou)",
"workflow_node.deploy.form.aliyun_live_region.tooltip": "这是什么?请参阅 https://help.aliyun.com/zh/live/product-overview/supported-regions",
"workflow_node.deploy.form.aliyun_live_domain.label": "阿里云视频直播流域名",
"workflow_node.deploy.form.aliyun_live_domain.placeholder": "请输入阿里云视频直播流域名(支持泛域名)",
"workflow_node.deploy.form.aliyun_live_domain.tooltip": "这是什么?请参阅 https://live.console.aliyun.com",
- "workflow_node.deploy.form.aliyun_nlb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.aliyun_nlb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.aliyun_nlb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.aliyun_nlb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.aliyun_nlb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/QUIC 监听的证书",
"workflow_node.deploy.form.aliyun_nlb_resource_type.option.listener.label": "替换指定负载均衡监听器的证书",
"workflow_node.deploy.form.aliyun_nlb_region.label": "阿里云 NLB 服务地域",
@@ -276,8 +289,8 @@
"workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder": "请输入 Azure KeyVault 证书名称",
"workflow_node.deploy.form.azure_keyvault_certificate_name.tooltip": "不填写时,将由 Certimate 自动生成证书名称。",
"workflow_node.deploy.form.azure_keyvault_certificate_name.errmsg.invalid": "证书名称只能包含字母、数字和连字符(-),长度限制为 1 到 127 个字符",
- "workflow_node.deploy.form.baiducloud_appblb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.baiducloud_appblb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.baiducloud_appblb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.baiducloud_appblb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.baiducloud_appblb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/SSL 监听的证书",
"workflow_node.deploy.form.baiducloud_appblb_resource_type.option.listener.label": "替换指定负载均衡监听的证书",
"workflow_node.deploy.form.baiducloud_appblb_region.label": "百度智能云 BLB 服务地域",
@@ -292,8 +305,8 @@
"workflow_node.deploy.form.baiducloud_appblb_snidomain.label": "百度智能云 BLB 扩展域名(可选)",
"workflow_node.deploy.form.baiducloud_appblb_snidomain.placeholder": "请输入百度智能云 BLB 扩展域名(支持泛域名)",
"workflow_node.deploy.form.baiducloud_appblb_snidomain.tooltip": "这是什么?请参阅 https://console.bce.baidu.com/blb/#/appblb/list
不填写时,将替换监听器的默认证书;否则,将替换扩展域名证书。",
- "workflow_node.deploy.form.baiducloud_blb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.baiducloud_blb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.baiducloud_blb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.baiducloud_blb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.baiducloud_blb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/SSL 监听的证书",
"workflow_node.deploy.form.baiducloud_blb_resource_type.option.listener.label": "替换指定负载均衡监听的证书",
"workflow_node.deploy.form.baiducloud_blb_region.label": "百度智能云 BLB 服务地域",
@@ -324,13 +337,18 @@
"workflow_node.deploy.form.baotapanel_site_type.option.other.label": "其他",
"workflow_node.deploy.form.baotapanel_site_name.label": "宝塔面板网站名称",
"workflow_node.deploy.form.baotapanel_site_name.placeholder": "请输入宝塔面板网站名称",
- "workflow_node.deploy.form.baotapanel_site_name.tooltip": "通常为网站域名。",
+ "workflow_node.deploy.form.baotapanel_site_name.tooltip": "请登录宝塔面板查看。",
"workflow_node.deploy.form.baotapanel_site_names.label": "宝塔面板网站名称",
"workflow_node.deploy.form.baotapanel_site_names.placeholder": "请输入宝塔面板网站名称(多个值请用半角分号隔开)",
"workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid": "请输入正确的宝塔面板网站名称",
- "workflow_node.deploy.form.baotapanel_site_names.tooltip": "通常为网站域名。",
+ "workflow_node.deploy.form.baotapanel_site_names.tooltip": "请登录宝塔面板查看。",
"workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title": "修改宝塔面板网站名称",
"workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder": "请输入宝塔面板网站名称",
+ "workflow_node.deploy.form.baotawaf_site_name.label": "堡塔云 WAF 网站名称",
+ "workflow_node.deploy.form.baotawaf_site_name.placeholder": "请输入堡塔云 WAF 网站名称",
+ "workflow_node.deploy.form.baotawaf_site_name.tooltip": "请登录堡塔云 WAF 面板查看。",
+ "workflow_node.deploy.form.baotawaf_site_port.label": "堡塔云 WAF 网站 SSL 端口",
+ "workflow_node.deploy.form.baotawaf_site_port.placeholder": "请输入堡塔云 WAF 网站 SSL 端口",
"workflow_node.deploy.form.bunny_cdn_pull_zone_id.label": "Bunny CDN 拉取区域 ID",
"workflow_node.deploy.form.bunny_cdn_pull_zone_id.placeholder": "请输入 Bunny CDN 拉取区域 ID",
"workflow_node.deploy.form.bunny_cdn_pull_zone_id.tooltip": "这是什么?请参阅 https://dash.bunny.net/cdn",
@@ -340,39 +358,48 @@
"workflow_node.deploy.form.byteplus_cdn_domain.label": "BytePlus CDN 域名",
"workflow_node.deploy.form.byteplus_cdn_domain.placeholder": "请输入 BytePlus CDN 域名(支持泛域名)",
"workflow_node.deploy.form.byteplus_cdn_domain.tooltip": "这是什么?请参阅 https://console.byteplus.com/cdn",
- "workflow_node.deploy.form.cdnfly_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.cdnfly_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.cdnfly_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.cdnfly_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.cdnfly_resource_type.option.site.label": "替换指定网站的证书",
"workflow_node.deploy.form.cdnfly_resource_type.option.certificate.label": "替换指定证书",
"workflow_node.deploy.form.cdnfly_site_id.label": "Cdnfly 网站 ID",
"workflow_node.deploy.form.cdnfly_site_id.placeholder": "请输入 Cdnfly 网站 ID",
+ "workflow_node.deploy.form.cdnfly_site_id.tooltip": "请登录 Cdnfly 控制台查看。",
"workflow_node.deploy.form.cdnfly_certificate_id.label": "Cdnfly 证书 ID",
"workflow_node.deploy.form.cdnfly_certificate_id.placeholder": "请输入 Cdnfly 证书 ID",
+ "workflow_node.deploy.form.cdnfly_certificate_id.tooltip": "请登录 Cdnfly 控制台查看。",
"workflow_node.deploy.form.dogecloud_cdn_domain.label": "多吉云 CDN 加速域名",
"workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "请输入多吉云 CDN 加速域名",
"workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.dogecloud.com",
"workflow_node.deploy.form.edgio_applications_environment_id.label": "Edgio Applications 环境 ID",
"workflow_node.deploy.form.edgio_applications_environment_id.placeholder": "请输入 Edgio Applications 环境 ID",
"workflow_node.deploy.form.edgio_applications_environment_id.tooltip": "这是什么?请参阅 https://edgio.app/",
+ "workflow_node.deploy.form.flexcdn_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.flexcdn_resource_type.placeholder": "请选择证书部署方式",
+ "workflow_node.deploy.form.flexcdn_resource_type.option.certificate.label": "替换指定证书",
+ "workflow_node.deploy.form.flexcdn_certificate_id.label": "FlexCDN 证书 ID",
+ "workflow_node.deploy.form.flexcdn_certificate_id.placeholder": "请输入 FlexCDN 证书 ID",
+ "workflow_node.deploy.form.flexcdn_certificate_id.tooltip": "请登录 FlexCDN 控制台查看。",
"workflow_node.deploy.form.gcore_cdn_resource_id.label": "Gcore CDN 资源 ID",
"workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "请输入 Gcore CDN 资源 ID",
"workflow_node.deploy.form.gcore_cdn_resource_id.tooltip": "这是什么?请参阅 https://cdn.gcore.com/resources/list",
"workflow_node.deploy.form.gcore_cdn_certificate_id.label": "Gcore CDN 原证书 ID(可选)",
"workflow_node.deploy.form.gcore_cdn_certificate_id.placeholder": "请输入 Gcore CDN 原证书 ID",
"workflow_node.deploy.form.gcore_cdn_certificate_id.tooltip": "这是什么?请参阅 https://cdn.gcore.com/ssl
不填写时,将上传新证书;否则,将替换原证书。",
- "workflow_node.deploy.form.goedge_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.goedge_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.goedge_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.goedge_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.goedge_resource_type.option.certificate.label": "替换指定证书",
"workflow_node.deploy.form.goedge_certificate_id.label": "GoEdge 证书 ID",
"workflow_node.deploy.form.goedge_certificate_id.placeholder": "请输入 GoEdge 证书 ID",
+ "workflow_node.deploy.form.goedge_certificate_id.tooltip": "请登录 GoEdge 控制台查看。",
"workflow_node.deploy.form.huaweicloud_cdn_region.label": "华为云 CDN 服务区域",
"workflow_node.deploy.form.huaweicloud_cdn_region.placeholder": "请输入华为云 CDN 服务区域(例如:cn-north-1)",
"workflow_node.deploy.form.huaweicloud_cdn_region.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/apiexplorer/#/endpoint",
"workflow_node.deploy.form.huaweicloud_cdn_domain.label": "华为云 CDN 加速域名",
"workflow_node.deploy.form.huaweicloud_cdn_domain.placeholder": "请输入华为云 CDN 加速域名",
"workflow_node.deploy.form.huaweicloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/cdn",
- "workflow_node.deploy.form.huaweicloud_elb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.huaweicloud_elb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.huaweicloud_elb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.huaweicloud_elb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.huaweicloud_elb_resource_type.option.certificate.label": "替换指定证书",
"workflow_node.deploy.form.huaweicloud_elb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听器的证书",
"workflow_node.deploy.form.huaweicloud_elb_resource_type.option.listener.label": "替换指定监听器的证书",
@@ -388,8 +415,8 @@
"workflow_node.deploy.form.huaweicloud_elb_listener_id.label": "华为云 ELB 监听器 ID",
"workflow_node.deploy.form.huaweicloud_elb_listener_id.placeholder": "请输入华为云 ELB 监听器 ID",
"workflow_node.deploy.form.huaweicloud_elb_listener_id.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/vpc/#/elb/list/grid",
- "workflow_node.deploy.form.huaweicloud_waf_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.huaweicloud_waf_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.huaweicloud_waf_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.huaweicloud_waf_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.huaweicloud_waf_resource_type.option.certificate.label": "替换指定证书",
"workflow_node.deploy.form.huaweicloud_waf_resource_type.option.cloudserver.label": "替换指定云模式防护网站的证书",
"workflow_node.deploy.form.huaweicloud_waf_resource_type.option.premiumhost.label": "替换指定独享模式防护网站的证书",
@@ -402,8 +429,8 @@
"workflow_node.deploy.form.huaweicloud_waf_domain.label": "华为云 WAF 防护域名",
"workflow_node.deploy.form.huaweicloud_waf_domain.placeholder": "请输入华为云 WAF 防护域名(支持泛域名)",
"workflow_node.deploy.form.huaweicloud_waf_domain.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/console/#/waf/domain/list",
- "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.jdcloud_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/TLS 监听的证书",
"workflow_node.deploy.form.jdcloud_alb_resource_type.option.listener.label": "替换指定负载均衡监听器的证书",
"workflow_node.deploy.form.jdcloud_alb_region_id.label": "京东云 ALB 服务地域 ID",
@@ -442,6 +469,15 @@
"workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret 数据键(用于存放私钥的字段)",
"workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "请输入 Kubernetes Secret 中用于存放私钥的数据键",
"workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "这是什么?请参阅 https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/",
+ "workflow_node.deploy.form.lecdn_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.lecdn_resource_type.placeholder": "请选择证书部署方式",
+ "workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "替换指定证书",
+ "workflow_node.deploy.form.lecdn_certificate_id.label": "LeCDN 证书 ID",
+ "workflow_node.deploy.form.lecdn_certificate_id.placeholder": "请输入 LeCDN 证书 ID",
+ "workflow_node.deploy.form.lecdn_certificate_id.tooltip": "请登录 LeCDN 控制台查看。",
+ "workflow_node.deploy.form.lecdn_client_id.label": "LeCDN 客户 ID(可选)",
+ "workflow_node.deploy.form.lecdn_client_id.placeholder": "请输入 LeCDN 客户 ID",
+ "workflow_node.deploy.form.lecdn_client_id.tooltip": "请登录 LeCDN 控制台查看。
使用的是系统管理员的授权信息时必填,需与证书所属客户相同。",
"workflow_node.deploy.form.local.guide": "小贴士:如果你正在使用 Docker 运行 Certimate,“本地”指的是容器内而非宿主机。",
"workflow_node.deploy.form.local_format.label": "文件格式",
"workflow_node.deploy.form.local_format.placeholder": "请选择文件格式",
@@ -488,7 +524,7 @@
"workflow_node.deploy.form.local_preset_scripts.option.ps_binding_iis.label": "PowerShell - 导入并绑定到 IIS",
"workflow_node.deploy.form.local_preset_scripts.option.ps_binding_netsh.label": "PowerShell - 导入并绑定到 netsh",
"workflow_node.deploy.form.local_preset_scripts.option.ps_binding_rdp.label": "PowerShell - 导入并绑定到 RDP",
- "workflow_node.deploy.form.netlify_site_id.label": "netlify 网站 ID",
+ "workflow_node.deploy.form.netlify_site_id.label": "Netlify 网站 ID",
"workflow_node.deploy.form.netlify_site_id.placeholder": "请输入 netlify 网站 ID",
"workflow_node.deploy.form.netlify_site_id.tooltip": "这是什么?请参阅 https://docs.netlify.com/api/get-started/#get-site",
"workflow_node.deploy.form.proxmoxve_node_name.label": "Proxmox VE 集群节点名称",
@@ -512,11 +548,15 @@
"workflow_node.deploy.form.rainyun_rcdn_domain.label": "雨云 RCDN 加速域名",
"workflow_node.deploy.form.rainyun_rcdn_domain.placeholder": "请输入雨云 RCDN 加速域名(支持泛域名)",
"workflow_node.deploy.form.rainyun_rcdn_domain.tooltip": "这是什么?请参阅 https://app.rainyun.com/apps/rcdn/list",
- "workflow_node.deploy.form.safeline_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.safeline_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.ratpanel_site_name.label": "耗子面板网站名称",
+ "workflow_node.deploy.form.ratpanel_site_name.placeholder": "请输入耗子面板网站名称",
+ "workflow_node.deploy.form.ratpanel_site_name.tooltip": "请登录耗子面板查看。",
+ "workflow_node.deploy.form.safeline_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.safeline_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.safeline_resource_type.option.certificate.label": "替换指定证书",
"workflow_node.deploy.form.safeline_certificate_id.label": "雷池证书 ID",
"workflow_node.deploy.form.safeline_certificate_id.placeholder": "请输入雷池证书 ID",
+ "workflow_node.deploy.form.safeline_certificate_id.tooltip": "请登录雷池控制台查看。",
"workflow_node.deploy.form.ssh_format.label": "文件格式",
"workflow_node.deploy.form.ssh_format.placeholder": "请选择文件格式",
"workflow_node.deploy.form.ssh_format.option.pem.label": "PEM 格式(*.pem, *.crt, *.key)",
@@ -564,8 +604,8 @@
"workflow_node.deploy.form.tencentcloud_cdn_domain.label": "腾讯云 CDN 加速域名",
"workflow_node.deploy.form.tencentcloud_cdn_domain.placeholder": "请输入腾讯云 CDN 加速域名(支持泛域名)",
"workflow_node.deploy.form.tencentcloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.cloud.tencent.com/cdn",
- "workflow_node.deploy.form.tencentcloud_clb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.tencentcloud_clb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.tencentcloud_clb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.tencentcloud_clb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.tencentcloud_clb_resource_type.option.ssl_deploy.label": "通过 SSL 服务部署到云资源实例",
"workflow_node.deploy.form.tencentcloud_clb_resource_type.option.loadbalancer.label": "替换指定实例下的全部 HTTPS/TCPSSL/QUIC 监听器的证书",
"workflow_node.deploy.form.tencentcloud_clb_resource_type.option.listener.label": "替换指定监听器的证书",
@@ -661,8 +701,8 @@
"workflow_node.deploy.form.upyun_file_domain.label": "又拍云云存储加速域名",
"workflow_node.deploy.form.upyun_file_domain.placeholder": "请输入又拍云云存储加速域名",
"workflow_node.deploy.form.upyun_file_domain.tooltip": "这是什么?请参阅 https://console.upyun.com/services/file/",
- "workflow_node.deploy.form.volcengine_alb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.volcengine_alb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.volcengine_alb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.volcengine_alb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.volcengine_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听的证书",
"workflow_node.deploy.form.volcengine_alb_resource_type.option.listener.label": "替换指定监听器的证书",
"workflow_node.deploy.form.volcengine_alb_region.label": "火山引擎 ALB 服务地域",
@@ -682,8 +722,8 @@
"workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "这是什么?请参阅 https://console.volcengine.com/cdn/homepage",
"workflow_node.deploy.form.volcengine_certcenter_region.label": "火山引擎证书中心服务地域",
"workflow_node.deploy.form.volcengine_certcenter_region.placeholder": "请输入火山引擎证书中心服务地域(例如:cn-beijing)",
- "workflow_node.deploy.form.volcengine_clb_resource_type.label": "证书替换方式",
- "workflow_node.deploy.form.volcengine_clb_resource_type.placeholder": "请选择证书替换方式",
+ "workflow_node.deploy.form.volcengine_clb_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.volcengine_clb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.volcengine_clb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听的证书",
"workflow_node.deploy.form.volcengine_clb_resource_type.option.listener.label": "替换指定监听器的证书",
"workflow_node.deploy.form.volcengine_clb_region.label": "火山引擎 CLB 服务地域",
@@ -719,6 +759,11 @@
"workflow_node.deploy.form.volcengine_tos_domain.label": "火山引擎 TOS 自定义域名",
"workflow_node.deploy.form.volcengine_tos_domain.placeholder": "请输入火山引擎 TOS 自定义域名",
"workflow_node.deploy.form.volcengine_tos_domain.tooltip": "这是什么?请参阅 see https://console.volcengine.com/tos",
+ "workflow_node.deploy.form.wangsu_cdn_domains.label": "网宿云 CDN 加速域名",
+ "workflow_node.deploy.form.wangsu_cdn_domains.placeholder": "请输入网宿云 CDN 加速域名(多个值请用半角分号隔开)",
+ "workflow_node.deploy.form.wangsu_cdn_domains.tooltip": "这是什么?请参阅 https://cdn.console.wangsu.com/v2/index/#/property/list",
+ "workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.title": "修改网宿云 CDN 加速域名",
+ "workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.placeholder": "请输入网宿云 CDN 加速域名",
"workflow_node.deploy.form.wangsu_cdnpro_environment.label": "网宿云环境",
"workflow_node.deploy.form.wangsu_cdnpro_environment.placeholder": "请选择网宿云环境",
"workflow_node.deploy.form.wangsu_cdnpro_environment.option.production.label": "生产环境",
@@ -732,6 +777,9 @@
"workflow_node.deploy.form.wangsu_cdnpro_webhook_id.label": "网宿云 CDN Pro 部署任务 Webhook ID(可选)",
"workflow_node.deploy.form.wangsu_cdnpro_webhook_id.placeholder": "请输入网宿云 CDN Pro 部署任务 Webhook ID",
"workflow_node.deploy.form.wangsu_cdnpro_webhook_id.tooltip": "这是什么?请参阅 https://cdnpro.console.wangsu.com/v2/index/#/certificate",
+ "workflow_node.deploy.form.wangsu_certificate_id.label": "网宿云证书 ID(可选)",
+ "workflow_node.deploy.form.wangsu_certificate_id.placeholder": "请输入网宿云证书 ID",
+ "workflow_node.deploy.form.wangsu_certificate_id.tooltip": "这是什么?请参阅 https://cdn.console.wangsu.com/v2/index#/certificate/list
不填写时,将上传新证书;否则,将替换原证书。",
"workflow_node.deploy.form.webhook_data.label": "Webhook 回调数据(可选)",
"workflow_node.deploy.form.webhook_data.placeholder": "请输入 Webhook 回调数据以覆盖默认值",
"workflow_node.deploy.form.webhook_data.tooltip": "不填写时,将使用所选部署目标授权的默认 Webhook 回调数据。",
@@ -775,9 +823,9 @@
"workflow_node.notify.form.mattermost_channel_id.label": "Mattermost 频道 ID(可选)",
"workflow_node.notify.form.mattermost_channel_id.placeholder": "请输入 Mattermost 频道 ID 以覆盖默认值",
"workflow_node.notify.form.mattermost_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。",
- "workflow_node.notify.form.telegram_chat_id.label": "Telegram 会话 ID(可选)",
- "workflow_node.notify.form.telegram_chat_id.placeholder": "请输入 Telegram 会话 ID 以覆盖默认值",
- "workflow_node.notify.form.telegram_chat_id.tooltip": "不填写时,将使用所选通知渠道授权的默认会话 ID。",
+ "workflow_node.notify.form.telegram_bot_chat_id.label": "Telegram 会话 ID(可选)",
+ "workflow_node.notify.form.telegram_bot_chat_id.placeholder": "请输入 Telegram 会话 ID 以覆盖默认值",
+ "workflow_node.notify.form.telegram_bot_chat_id.tooltip": "不填写时,将使用所选通知渠道授权的默认会话 ID。",
"workflow_node.notify.form.webhook_data.label": "Webhook 回调数据(可选)",
"workflow_node.notify.form.webhook_data.placeholder": "请输入 Webhook 回调数据以覆盖默认值",
"workflow_node.notify.form.webhook_data.tooltip": "不填写时,将使用所选部署目标授权的默认 Webhook 回调数据。",
diff --git a/ui/src/pages/certificates/CertificateList.tsx b/ui/src/pages/certificates/CertificateList.tsx
index 46bc1745..97eab0ef 100644
--- a/ui/src/pages/certificates/CertificateList.tsx
+++ b/ui/src/pages/certificates/CertificateList.tsx
@@ -126,11 +126,11 @@ const CertificateList = () => {
},
},
{
- key: "issuer",
+ key: "brand",
title: t("certificate.props.brand"),
render: (_, record) => (
- {record.issuer}
+ {record.issuerOrg}
{record.keyAlgorithm}
),