diff --git a/.goreleaser.yml b/.goreleaser.yml index 9315c6a7..d65550fd 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -31,7 +31,7 @@ builds: goarch: arm upx: - enable: true + - enabled: true release: draft: true diff --git a/README.md b/README.md index 988089d9..1e1091dc 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-hosting-providers)); +- 支持 90+ 部署目标(如 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 f94020a2..67bab154 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-hosting-providers)); +- Supports more than 90+ 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 37686d8c..0f12bd45 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.24.3 require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1 github.com/Edgio/edgio-api v0.0.0-workspace github.com/G-Core/gcorelabscdn-go v1.0.31 @@ -14,10 +14,10 @@ require ( github.com/alibabacloud-go/apig-20240327/v3 v3.2.2 github.com/alibabacloud-go/cas-20200407/v3 v3.0.4 github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2 - github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3 + github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.4 github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7 github.com/alibabacloud-go/ddoscoo-20200101/v4 v4.0.0 - github.com/alibabacloud-go/esa-20240910/v2 v2.32.0 + github.com/alibabacloud-go/esa-20240910/v2 v2.33.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 @@ -26,18 +26,18 @@ require ( github.com/alibabacloud-go/slb-20140515/v4 v4.0.10 github.com/alibabacloud-go/tea v1.3.9 github.com/alibabacloud-go/vod-20170321/v4 v4.8.4 - github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2 + github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.3 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible 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/baidubce/bce-sdk-go v0.9.228 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 + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150 github.com/jdcloud-api/jdcloud-sdk-go v1.64.0 github.com/libdns/dynv6 v1.0.0 github.com/libdns/libdns v0.2.3 @@ -45,29 +45,29 @@ require ( 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 - github.com/pocketbase/pocketbase v0.28.0 + github.com/pocketbase/pocketbase v0.28.2 github.com/povsister/scp v0.0.0-20250504051308-e467f71ea63c github.com/qiniu/go-sdk/v7 v7.25.3 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1166 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1173 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162 - github.com/ucloud/ucloud-sdk-go v0.22.35 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1169 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1166 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1164 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1170 + github.com/ucloud/ucloud-sdk-go v0.22.41 github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12 - github.com/volcengine/volc-sdk-golang v1.0.207 - github.com/volcengine/volcengine-go-sdk v1.1.7 + github.com/volcengine/volc-sdk-golang v1.0.208 + github.com/volcengine/volcengine-go-sdk v1.1.8 gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 golang.org/x/crypto v0.38.0 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 - k8s.io/api v0.33.0 - k8s.io/apimachinery v0.33.0 - k8s.io/client-go v0.33.0 + k8s.io/api v0.33.1 + k8s.io/apimachinery v0.33.1 + k8s.io/client-go v0.33.1 software.sslmate.com/src/go-pkcs12 v0.5.0 ) @@ -211,10 +211,10 @@ require ( google.golang.org/protobuf v1.36.5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/libc v1.62.1 // indirect + modernc.org/libc v1.65.7 // indirect modernc.org/mathutil v1.7.1 // indirect - modernc.org/memory v1.9.1 // indirect - modernc.org/sqlite v1.37.0 // indirect + modernc.org/memory v1.11.0 // indirect + modernc.org/sqlite v1.37.1 // indirect ) replace github.com/Edgio/edgio-api v0.0.0-workspace => ./internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace diff --git a/go.sum b/go.sum index 5a64f77a..7bbf5848 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,8 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4 github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= @@ -101,8 +101,8 @@ github.com/alibabacloud-go/cas-20200407/v3 v3.0.4 h1:ngRlctbt135zoujwX0lXSv9m4h1 github.com/alibabacloud-go/cas-20200407/v3 v3.0.4/go.mod h1:6n9MZ9SH3HlSzfe2oKwjOqhJx3dxvW2gMDO+lq8t9U4= github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2 h1:+KJOPukTM+xMyiLOW5qBwYKG2df3Ar7coRsqc1juKO8= github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2/go.mod h1:GnPiPL3HlzCi8SGiLiVgKrAFkP1vTtcF4yGtjsl4wfo= -github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3 h1:OTLn0ShbE0jJj+5Z+P76zeHsZYxZjO7YVThQoeaBM9M= -github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3/go.mod h1:eUmD1G4BjEBOAHIeJrHJL7pyLGgXSRTPLjBmYY7uPEg= +github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.4 h1:SsyAoXM1R4J3I4xQdPW/rRW8cTo2KN440/4h/pGiwRQ= +github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.4/go.mod h1:eUmD1G4BjEBOAHIeJrHJL7pyLGgXSRTPLjBmYY7uPEg= github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY= github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI= github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE= @@ -132,8 +132,8 @@ github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/ql github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= github.com/alibabacloud-go/endpoint-util v1.1.1 h1:ZkBv2/jnghxtU0p+upSU0GGzW1VL9GQdZO3mcSUTUy8= github.com/alibabacloud-go/endpoint-util v1.1.1/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= -github.com/alibabacloud-go/esa-20240910/v2 v2.32.0 h1:eudSgNIkCg6huIu3HuF16BJG6+CA6bIuIddxpuPydpg= -github.com/alibabacloud-go/esa-20240910/v2 v2.32.0/go.mod h1:HZS5PmYJvcmH4vrJYuCvK3AnYzD9hLlO8CT0hgRyDXo= +github.com/alibabacloud-go/esa-20240910/v2 v2.33.0 h1:10IWxrMcF1W6/7BUJIJifrofduSG0wRFqDbRfIsR3Zw= +github.com/alibabacloud-go/esa-20240910/v2 v2.33.0/go.mod h1:HZS5PmYJvcmH4vrJYuCvK3AnYzD9hLlO8CT0hgRyDXo= github.com/alibabacloud-go/fc-20230330/v4 v4.3.5 h1:nDNjVzGwkQPbQnAuxAmxvS9x8QGLph8j0ptEdZDPGBA= 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= @@ -190,8 +190,8 @@ github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzY github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alibabacloud-go/vod-20170321/v4 v4.8.4 h1:MYP2xfrcud8vlWljQ4lhemNgAgi9/AUAa450n8TUXZo= github.com/alibabacloud-go/vod-20170321/v4 v4.8.4/go.mod h1:5ocQ6hIc9tpGixD2iy099aOGwIgpzjT2le4Krd4aLn8= -github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2 h1:CmhJzCZ5RiSiWU6BV2XJUtIMD2LDo9FFfqlYGtx1aAw= -github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2/go.mod h1:9itYSTzipL3NlvhvNYfTjFaapoZzG68nlu/KUdh9SpA= +github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.3 h1:25tmcJxIitrk55crBGssPlqRzmFcpGVW5TEFxdUvfg0= +github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.3/go.mod h1:9itYSTzipL3NlvhvNYfTjFaapoZzG68nlu/KUdh9SpA= github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 h1:yUkCbrSM1cWtgBfRVKMQtdt22KhDvKY7g4V+92eG9wA= github.com/aliyun/alibaba-cloud-sdk-go v1.63.100/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= @@ -250,8 +250,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjK github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= -github.com/baidubce/bce-sdk-go v0.9.226 h1:VKEKcJC9P33yIfYJZr12Q/4Bvj18RFbgO8w8XOfU8AI= -github.com/baidubce/bce-sdk-go v0.9.226/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= +github.com/baidubce/bce-sdk-go v0.9.228 h1:XEY3/oAxXcsi7+3atib9fMI6YNE9sL5qo+WMZ+iqmNE= +github.com/baidubce/bce-sdk-go v0.9.228/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -545,8 +545,8 @@ github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148 h1:PdWSbniKnPhKe1B19KUHW/9ahYbFH2EY6Iq6sxOnomo= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150 h1:Ih+z79Ko1ClH4dlv7O1lyHRiVCjkb2NZYYk+1cSZbU8= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -737,8 +737,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pocketbase/dbx v1.11.0 h1:LpZezioMfT3K4tLrqA55wWFw1EtH1pM4tzSVa7kgszU= github.com/pocketbase/dbx v1.11.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs= -github.com/pocketbase/pocketbase v0.28.0 h1:dnMHSO0wuYpKs6oP3X5buw1lY9ptd8zy1fTjN+Ae+mA= -github.com/pocketbase/pocketbase v0.28.0/go.mod h1:WE6xMM4+pxKIVNl4B1mcOEZXlDvPGl7cZ64TW2iXHdI= +github.com/pocketbase/pocketbase v0.28.2 h1:b6cfUfr5d4whvUFGFhI8oHRzx/eB76GCUQGftqgv9lM= +github.com/pocketbase/pocketbase v0.28.2/go.mod h1:ElwIYS1b5xS9w0U7AK7tsm6FuC0lzw57H8p/118Cu7g= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/povsister/scp v0.0.0-20250504051308-e467f71ea63c h1:1+j5JHz9mUzYSp0scuF6hzvJP28EDBFe5eBJb0xnGk4= @@ -771,8 +771,8 @@ github.com/qiniu/x v1.10.5 h1:7V/CYWEmo9axJULvrJN6sMYh2FdY+esN5h8jwDkA4b0= github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -829,31 +829,32 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/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= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161/go.mod h1:9MzQSEULYm5wHAKz8R3oQ8ovg4vWeLFzn0DmRWTc6zg= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1120/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1124/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1166 h1:cEoDsBt7vGh7YtfVHVmgXKQURZANBE8UKK/So84QUdU= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1166/go.mod h1:0o5Cfgdh+bAx7kpQ5a5wce/ZUiDvy4Md8NcbrLtk6i8= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1150/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1155/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1160/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1161/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162 h1:bscCBygP9JRl6iNabF+vmBOhY+xayFFGYV5Wa0NzH0A= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1164/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1166/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1169/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1170/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1173 h1:W5bzEWiJwiwRZR0/P1l78OYWUXYsXLjhBaQ64c+9+fk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1173/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 h1:mrJ5Fbkd7sZIJ5F6oRfh5zebPQaudPH9Y0+GUmFytYU= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128/go.mod h1:zbsYIBT+VTX4z4ocjTAdLBIWyNYj3z0BRqd0iPdnjsk= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150 h1:RQQYfZOFYlkxKR2+xp8el3+8xs9DhxBy+ajlHtapqtQ= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150/go.mod h1:zpfr6EBWy7ClASTGUgIy01Gn4R79UXf+2QGQeyR124A= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120 h1:z0t0lb5h1mZirXftO8MRg25COYZHx0ubQjSPhZT/LY0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120/go.mod h1:IFZL44Keyl+MHrhpFwUaQmJvMDwGr+t+cUfFAC+74lU= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124 h1:LQKAlxFb0sYiE8ojK5h9+seuFzogoJtYnXmiRF+4F4Q= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124/go.mod h1:tYbK0FbHVG+78od7eZpzczE8qk0JWKO/osTQWuiJ3Fo= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162 h1:z/JF+JGi6bGf8vnK9ZeVXz+1Q3ih8nF6KjThxhtIrNc= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162/go.mod h1:rsO8JCP+WQeLQ32wAB4oRRjsEz0O+kvCGDqc7Ze1jc4= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160 h1:aNVEDS1yQ7sLfXOOQ/bF3eljFjyvHoJ/J8qSC9mC9gw= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160/go.mod h1:kf6NQmKK6sh1ACwh8iliBy7I/burd+AWusNz6zbDvLM= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162 h1:gnmuUaoFAShc9FKj3Omswu3n08bHM/sGsl8xjFAkFNs= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162/go.mod h1:bu3KAFeoJ1xDGQp72h9Le3FqbOcCcdomOUig3OqgcE4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172 h1:6SUO0hTie3zxnUEMxmhnS1iRIXpAukSZV27Nrx4NwIk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172/go.mod h1:tmN4zfu70SD0iee3qfpc09NRLel30zGoAuzIs4X0Kfs= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1169 h1:oBymtJEmKDnS2NIR0PDLd+xCGQ+7uMoEt7zEB5Q3x8U= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1169/go.mod h1:K27PNEgRJ602ESXUNnlRnCkf1+XYHI6RVP/ylIe/Aro= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1166 h1:+kIsoG2If/0y15PpZsXfT0QqTuwec9nMgo1JP8KQMkw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1166/go.mod h1:lGmBMXqe3vBg6Bi9h4mVpWLKd7jQIMRbndr8KNHBqSw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1164 h1:caaIGWOs/JtWOK5ptVMEiiGgzU7Jf6UpMv+9IbIa4vs= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1164/go.mod h1:SJI2mc77gDC7Tw1QoF/4d5SwLvz8HQFUecWtIXb+r/Q= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1170 h1:kcQCWuI9zOkZgL5CK66HNAJmSWCSJxRrDxXT+j02CeE= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1170/go.mod h1:vTukVfThbBIc4lOf4eq/q51eEk78oZUJd2lAoJBOJwI= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= @@ -862,18 +863,18 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ucloud/ucloud-sdk-go v0.22.35 h1:Q4CY3Ae5813jmNUrGdCjc8tlyleL5Lyl0APnpK5L4sk= -github.com/ucloud/ucloud-sdk-go v0.22.35/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw= +github.com/ucloud/ucloud-sdk-go v0.22.41 h1:JndTJhCx7A1wggZfVb4KMm7D0Wfvd/HkmQVfSNjClQA= +github.com/ucloud/ucloud-sdk-go v0.22.41/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12 h1:u9+32DXQIOFPG8oQ3xrjSAUSyAcaq5bqO4cEBom/6lA= github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12/go.mod h1:IrjK84IJJTuOZOTMv/P18Ydjy/x+ow7fF7q11jAxXLM= github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU= -github.com/volcengine/volc-sdk-golang v1.0.207 h1:1OJ/nC92dF1URRoyO1AHSghCob12NT1PAA/GoK8uU18= -github.com/volcengine/volc-sdk-golang v1.0.207/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= -github.com/volcengine/volcengine-go-sdk v1.1.7 h1:5ElF1inqX1QUKX8/XGk+HGpG+F01W+m73cLQH+0x50s= -github.com/volcengine/volcengine-go-sdk v1.1.7/go.mod h1:EyKoi6t6eZxoPNGr2GdFCZti2Skd7MO3eUzx7TtSvNo= +github.com/volcengine/volc-sdk-golang v1.0.208 h1:DyGUPjEKhWS08BkptfqenXTuUq+LKb7+gX/RBAtNl8w= +github.com/volcengine/volc-sdk-golang v1.0.208/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= +github.com/volcengine/volcengine-go-sdk v1.1.8 h1:/T2p7qeeLWWhGrhtB00b8VNlE32S266LcO+jqFUYwzY= +github.com/volcengine/volcengine-go-sdk v1.1.8/go.mod h1:EyKoi6t6eZxoPNGr2GdFCZti2Skd7MO3eUzx7TtSvNo= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1394,39 +1395,39 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= -k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= -k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= -k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= -k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= +k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= +k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= +k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic= -modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU= -modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw= +modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s= +modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= +modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE= modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= -modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= -modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8= +modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s= -modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo= +modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00= +modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g= -modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= -modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= +modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs= +modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/internal/applicant/applicant.go b/internal/applicant/applicant.go index e6a04bcd..f1200094 100644 --- a/internal/applicant/applicant.go +++ b/internal/applicant/applicant.go @@ -26,13 +26,14 @@ import ( ) type ApplyResult struct { + CSR string FullChainCertificate string IssuerCertificate string PrivateKey string ACMEAccountUrl string ACMECertUrl string ACMECertStableUrl string - CSR string + ARIReplaced bool } type Applicant interface { @@ -109,7 +110,7 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err certRepo := repository.NewCertificateRepository() lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), config.Node.Id) - if lastCertificate != nil { + if lastCertificate != nil && !lastCertificate.ACMERenewed { newCertSan := slices.Clone(options.Domains) oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";") slices.Sort(newCertSan) @@ -119,8 +120,8 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err lastCertX509, _ := certcrypto.ParsePEMCertificate([]byte(lastCertificate.Certificate)) if lastCertX509 != nil { replacedARICertId, _ := certificate.MakeARICertID(lastCertX509) - options.ReplacedARIAcct = lastCertificate.ACMEAccountUrl - options.ReplacedARICert = replacedARICertId + options.ARIReplaceAcct = lastCertificate.ACMEAccountUrl + options.ARIReplaceCert = replacedARICertId } } } @@ -235,22 +236,24 @@ func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOpt Domains: options.Domains, Bundle: true, } - if options.ReplacedARIAcct == user.Registration.URI { - certRequest.ReplacesCertID = options.ReplacedARICert + if options.ARIReplaceAcct == user.Registration.URI { + certRequest.ReplacesCertID = options.ARIReplaceCert } + certResource, err := client.Certificate.Obtain(certRequest) if err != nil { return nil, err } return &ApplyResult{ + CSR: strings.TrimSpace(string(certResource.CSR)), FullChainCertificate: strings.TrimSpace(string(certResource.Certificate)), IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)), PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)), ACMEAccountUrl: user.Registration.URI, ACMECertUrl: certResource.CertURL, ACMECertStableUrl: certResource.CertStableURL, - CSR: strings.TrimSpace(string(certResource.CSR)), + ARIReplaced: certRequest.ReplacesCertID != "", }, nil } diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index de47ae18..98561daf 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -17,11 +17,14 @@ import ( pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns" pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud" pDeSEC "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec" + pDigitalOcean "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean" pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla" + pDuckDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/duckdns" pDynv6 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6" pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore" pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname" pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy" + pHetzner "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/hetzner" pHuaweiCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud" pJDCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud" pNamecheap "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namecheap" @@ -57,8 +60,8 @@ type applicantProviderOptions struct { DnsPropagationTimeout int32 DnsTTL int32 DisableFollowCNAME bool - ReplacedARIAcct string - ReplacedARICert string + ARIReplaceAcct string + ARIReplaceCert string } func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) { @@ -246,6 +249,21 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi return applicant, err } + case domain.ACMEDns01ProviderTypeDigitalOcean: + { + access := domain.AccessConfigForDigitalOcean{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pDigitalOcean.NewChallengeProvider(&pDigitalOcean.ChallengeProviderConfig{ + AccessToken: access.AccessToken, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ACMEDns01ProviderTypeDNSLA: { access := domain.AccessConfigForDNSLA{} @@ -262,6 +280,20 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi return applicant, err } + case domain.ACMEDns01ProviderTypeDuckDNS: + { + access := domain.AccessConfigForDuckDNS{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pDuckDNS.NewChallengeProvider(&pDuckDNS.ChallengeProviderConfig{ + Token: access.Token, + DnsPropagationTimeout: options.DnsPropagationTimeout, + }) + return applicant, err + } + case domain.ACMEDns01ProviderTypeDynv6: { access := domain.AccessConfigForDynv6{} @@ -324,6 +356,21 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi return applicant, err } + case domain.ACMEDns01ProviderTypeHetzner: + { + access := domain.AccessConfigForHetzner{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pHetzner.NewChallengeProvider(&pHetzner.ChallengeProviderConfig{ + ApiToken: access.ApiToken, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ACMEDns01ProviderTypeHuaweiCloud, domain.ACMEDns01ProviderTypeHuaweiCloudDNS: { access := domain.AccessConfigForHuaweiCloud{} @@ -476,7 +523,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi } applicant, err := pPowerDNS.NewChallengeProvider(&pPowerDNS.ChallengeProviderConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, DnsPropagationTimeout: options.DnsPropagationTimeout, diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index d1d72408..7f9bae91 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -79,6 +79,7 @@ import ( pTencentCloudWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-waf" pUCloudUCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-ucdn" pUCloudUS3 "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-us3" + pUniCloudWebHost "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/unicloud-webhost" pUpyunCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/upyun-cdn" pVolcEngineALB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-alb" pVolcEngineCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-cdn" @@ -119,7 +120,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer switch options.Provider { case domain.DeploymentProviderType1PanelConsole: deployer, err := p1PanelConsole.NewDeployer(&p1PanelConsole.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiVersion: access.ApiVersion, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, @@ -129,7 +130,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderType1PanelSite: deployer, err := p1PanelSite.NewDeployer(&p1PanelSite.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiVersion: access.ApiVersion, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, @@ -454,7 +455,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer switch options.Provider { case domain.DeploymentProviderTypeBaotaPanelConsole: deployer, err := pBaotaPanelConsole.NewDeployer(&pBaotaPanelConsole.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, AutoRestart: maputil.GetBool(options.ProviderServiceConfig, "autoRestart"), @@ -463,7 +464,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderTypeBaotaPanelSite: deployer, err := pBaotaPanelSite.NewDeployer(&pBaotaPanelSite.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, SiteType: maputil.GetOrDefaultString(options.ProviderServiceConfig, "siteType", "other"), @@ -487,7 +488,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer switch options.Provider { case domain.DeploymentProviderTypeBaotaWAFConsole: deployer, err := pBaotaWAFConsole.NewDeployer(&pBaotaWAFConsole.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, }) @@ -495,7 +496,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderTypeBaotaWAFSite: deployer, err := pBaotaWAFSite.NewDeployer(&pBaotaWAFSite.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, SiteName: maputil.GetString(options.ProviderServiceConfig, "siteName"), @@ -565,7 +566,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pCdnfly.NewDeployer(&pCdnfly.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, ApiSecret: access.ApiSecret, AllowInsecureConnections: access.AllowInsecureConnections, @@ -614,7 +615,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pFlexCDN.NewDeployer(&pFlexCDN.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiRole: access.ApiRole, AccessKeyId: access.AccessKeyId, AccessKey: access.AccessKey, @@ -654,7 +655,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pGoEdge.NewDeployer(&pGoEdge.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiRole: access.ApiRole, AccessKeyId: access.AccessKeyId, AccessKey: access.AccessKey, @@ -773,7 +774,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pLeCDN.NewDeployer(&pLeCDN.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiVersion: access.ApiVersion, ApiRole: access.ApiRole, Username: access.Username, @@ -845,7 +846,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pProxmoxVE.NewDeployer(&pProxmoxVE.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiToken: access.ApiToken, ApiTokenSecret: access.ApiTokenSecret, AllowInsecureConnections: access.AllowInsecureConnections, @@ -916,7 +917,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer switch options.Provider { case domain.DeploymentProviderTypeRatPanelConsole: deployer, err := pRatPanelConsole.NewDeployer(&pRatPanelConsole.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, AccessTokenId: access.AccessTokenId, AccessToken: access.AccessToken, AllowInsecureConnections: access.AllowInsecureConnections, @@ -925,7 +926,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderTypeRatPanelSite: deployer, err := pRatPanelSite.NewDeployer(&pRatPanelSite.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, AccessTokenId: access.AccessTokenId, AccessToken: access.AccessToken, AllowInsecureConnections: access.AllowInsecureConnections, @@ -946,7 +947,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pSafeLine.NewDeployer(&pSafeLine.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiToken: access.ApiToken, AllowInsecureConnections: access.AllowInsecureConnections, ResourceType: pSafeLine.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), @@ -1144,6 +1145,23 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } } + case domain.DeploymentProviderTypeUniCloudWebHost: + { + access := domain.AccessConfigForUniCloud{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pUniCloudWebHost.NewDeployer(&pUniCloudWebHost.DeployerConfig{ + Username: access.Username, + Password: access.Password, + SpaceProvider: maputil.GetString(options.ProviderServiceConfig, "spaceProvider"), + SpaceId: maputil.GetString(options.ProviderServiceConfig, "spaceId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), + }) + return deployer, err + } + case domain.DeploymentProviderTypeUpyunCDN, domain.DeploymentProviderTypeUpyunFile: { access := domain.AccessConfigForUpyun{} diff --git a/internal/domain/access.go b/internal/domain/access.go index 13975392..e31bb1a0 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -16,7 +16,7 @@ type Access struct { } type AccessConfigFor1Panel struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiVersion string `json:"apiVersion"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` @@ -62,13 +62,13 @@ type AccessConfigForBaishan struct { } type AccessConfigForBaotaPanel struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type AccessConfigForBaotaWAF struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } @@ -87,7 +87,7 @@ type AccessConfigForCacheFly struct { } type AccessConfigForCdnfly struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` ApiSecret string `json:"apiSecret"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` @@ -112,11 +112,20 @@ type AccessConfigForDeSEC struct { Token string `json:"token"` } +type AccessConfigForDigitalOcean struct { + AccessToken string `json:"accessToken"` +} + type AccessConfigForDingTalkBot struct { WebhookUrl string `json:"webhookUrl"` Secret string `json:"secret"` } +type AccessConfigForDiscordBot struct { + BotToken string `json:"botToken"` + DefaultChannelId string `json:"defaultChannelId,omitempty"` +} + type AccessConfigForDNSLA struct { ApiId string `json:"apiId"` ApiSecret string `json:"apiSecret"` @@ -127,6 +136,10 @@ type AccessConfigForDogeCloud struct { SecretKey string `json:"secretKey"` } +type AccessConfigForDuckDNS struct { + Token string `json:"token"` +} + type AccessConfigForDynv6 struct { HttpToken string `json:"httpToken"` } @@ -147,7 +160,7 @@ type AccessConfigForEmail struct { } type AccessConfigForFlexCDN struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiRole string `json:"apiRole"` AccessKeyId string `json:"accessKeyId"` AccessKey string `json:"accessKey"` @@ -169,7 +182,7 @@ type AccessConfigForGoDaddy struct { } type AccessConfigForGoEdge struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiRole string `json:"apiRole"` AccessKeyId string `json:"accessKeyId"` AccessKey string `json:"accessKey"` @@ -181,6 +194,10 @@ type AccessConfigForGoogleTrustServices struct { EabHmacKey string `json:"eabHmacKey"` } +type AccessConfigForHetzner struct { + ApiToken string `json:"apiToken"` +} + type AccessConfigForHuaweiCloud struct { AccessKeyId string `json:"accessKeyId"` SecretAccessKey string `json:"secretAccessKey"` @@ -200,7 +217,7 @@ type AccessConfigForLarkBot struct { } type AccessConfigForLeCDN struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiVersion string `json:"apiVersion"` ApiRole string `json:"apiRole"` Username string `json:"username"` @@ -249,13 +266,13 @@ type AccessConfigForPorkbun struct { } type AccessConfigForPowerDNS struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type AccessConfigForProxmoxVE struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiToken string `json:"apiToken"` ApiTokenSecret string `json:"apiTokenSecret,omitempty"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` @@ -271,18 +288,23 @@ type AccessConfigForRainYun struct { } type AccessConfigForRatPanel struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` AccessTokenId int32 `json:"accessTokenId"` AccessToken string `json:"accessToken"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type AccessConfigForSafeLine struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiToken string `json:"apiToken"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } +type AccessConfigForSlackBot struct { + BotToken string `json:"botToken"` + DefaultChannelId string `json:"defaultChannelId,omitempty"` +} + type AccessConfigForSSH struct { Host string `json:"host"` Port int32 `json:"port"` @@ -321,6 +343,11 @@ type AccessConfigForUCloud struct { ProjectId string `json:"projectId,omitempty"` } +type AccessConfigForUniCloud struct { + Username string `json:"username"` + Password string `json:"password"` +} + type AccessConfigForUpyun struct { Username string `json:"username"` Password string `json:"password"` diff --git a/internal/domain/certificate.go b/internal/domain/certificate.go index 710ce268..b2b48fcd 100644 --- a/internal/domain/certificate.go +++ b/internal/domain/certificate.go @@ -28,6 +28,7 @@ type Certificate struct { ACMEAccountUrl string `json:"acmeAccountUrl" db:"acmeAccountUrl"` ACMECertUrl string `json:"acmeCertUrl" db:"acmeCertUrl"` ACMECertStableUrl string `json:"acmeCertStableUrl" db:"acmeCertStableUrl"` + ACMERenewed bool `json:"acmeRenewed" db:"acmeRenewed"` WorkflowId string `json:"workflowId" db:"workflowId"` WorkflowNodeId string `json:"workflowNodeId" db:"workflowNodeId"` WorkflowRunId string `json:"workflowRunId" db:"workflowRunId"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index db0de59d..55f8b2af 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -31,9 +31,12 @@ const ( AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 天翼云(预留) AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 联通云(预留) AccessProviderTypeDeSEC = AccessProviderType("desec") + AccessProviderTypeDigitalOcean = AccessProviderType("digitalocean") AccessProviderTypeDingTalkBot = AccessProviderType("dingtalkbot") + AccessProviderTypeDiscordBot = AccessProviderType("discordbot") AccessProviderTypeDNSLA = AccessProviderType("dnsla") AccessProviderTypeDogeCloud = AccessProviderType("dogecloud") + AccessProviderTypeDuckDNS = AccessProviderType("duckdns") AccessProviderTypeDynv6 = AccessProviderType("dynv6") AccessProviderTypeEdgio = AccessProviderType("edgio") AccessProviderTypeEmail = AccessProviderType("email") @@ -44,6 +47,7 @@ const ( AccessProviderTypeGoDaddy = AccessProviderType("godaddy") AccessProviderTypeGoEdge = AccessProviderType("goedge") AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices") + AccessProviderTypeHetzner = AccessProviderType("hetzner") AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") AccessProviderTypeJDCloud = AccessProviderType("jdcloud") AccessProviderTypeKubernetes = AccessProviderType("k8s") @@ -67,11 +71,13 @@ const ( AccessProviderTypeRainYun = AccessProviderType("rainyun") AccessProviderTypeRatPanel = AccessProviderType("ratpanel") AccessProviderTypeSafeLine = AccessProviderType("safeline") + AccessProviderTypeSlackBot = AccessProviderType("slackbot") AccessProviderTypeSSH = AccessProviderType("ssh") AccessProviderTypeSSLCOM = AccessProviderType("sslcom") AccessProviderTypeTelegramBot = AccessProviderType("telegrambot") AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud") AccessProviderTypeUCloud = AccessProviderType("ucloud") + AccessProviderTypeUniCloud = AccessProviderType("unicloud") AccessProviderTypeUpyun = AccessProviderType("upyun") AccessProviderTypeVercel = AccessProviderType("vercel") AccessProviderTypeVolcEngine = AccessProviderType("volcengine") @@ -126,11 +132,14 @@ const ( ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS) ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud) ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC) + ACMEDns01ProviderTypeDigitalOcean = ACMEDns01ProviderType(AccessProviderTypeDigitalOcean) ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA) + ACMEDns01ProviderTypeDuckDNS = ACMEDns01ProviderType(AccessProviderTypeDuckDNS) ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6) ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore) ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname) ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy) + ACMEDns01ProviderTypeHetzner = ACMEDns01ProviderType(AccessProviderTypeHetzner) ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS] ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns") ACMEDns01ProviderTypeJDCloud = ACMEDns01ProviderType(AccessProviderTypeJDCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeJDCloudDNS] @@ -236,6 +245,7 @@ const ( DeploymentProviderTypeTencentCloudWAF = DeploymentProviderType(AccessProviderTypeTencentCloud + "-waf") DeploymentProviderTypeUCloudUCDN = DeploymentProviderType(AccessProviderTypeUCloud + "-ucdn") DeploymentProviderTypeUCloudUS3 = DeploymentProviderType(AccessProviderTypeUCloud + "-us3") + DeploymentProviderTypeUniCloudWebHost = DeploymentProviderType(AccessProviderTypeUniCloud + "-webhost") DeploymentProviderTypeUpyunCDN = DeploymentProviderType(AccessProviderTypeUpyun + "-cdn") DeploymentProviderTypeUpyunFile = DeploymentProviderType(AccessProviderTypeUpyun + "-file") DeploymentProviderTypeVolcEngineALB = DeploymentProviderType(AccessProviderTypeVolcEngine + "-alb") @@ -263,9 +273,11 @@ type NotificationProviderType string */ const ( NotificationProviderTypeDingTalkBot = NotificationProviderType(AccessProviderTypeDingTalkBot) + NotificationProviderTypeDiscordBot = NotificationProviderType(AccessProviderTypeDiscordBot) NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail) NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot) NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost) + NotificationProviderTypeSlackBot = NotificationProviderType(AccessProviderTypeSlackBot) NotificationProviderTypeTelegramBot = NotificationProviderType(AccessProviderTypeTelegramBot) NotificationProviderTypeWebhook = NotificationProviderType(AccessProviderTypeWebhook) NotificationProviderTypeWeComBot = NotificationProviderType(AccessProviderTypeWeComBot) diff --git a/internal/notify/providers.go b/internal/notify/providers.go index 3a8c575d..7dc63465 100644 --- a/internal/notify/providers.go +++ b/internal/notify/providers.go @@ -7,9 +7,11 @@ import ( "github.com/usual2970/certimate/internal/domain" "github.com/usual2970/certimate/internal/pkg/core/notifier" pDingTalkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot" + pDiscordBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/discordbot" pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email" pLarkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot" pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost" + pSlackBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/slackbot" pTelegramBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot" pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook" pWeComBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot" @@ -42,6 +44,19 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier }) } + case domain.NotificationProviderTypeDiscordBot: + { + access := domain.AccessConfigForDiscordBot{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + return pDiscordBot.NewNotifier(&pDiscordBot.NotifierConfig{ + BotToken: access.BotToken, + ChannelId: maputil.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.DefaultChannelId), + }) + } + case domain.NotificationProviderTypeEmail: { access := domain.AccessConfigForEmail{} @@ -87,6 +102,19 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier }) } + case domain.NotificationProviderTypeSlackBot: + { + access := domain.AccessConfigForSlackBot{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + return pSlackBot.NewNotifier(&pSlackBot.NotifierConfig{ + BotToken: access.BotToken, + ChannelId: maputil.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.DefaultChannelId), + }) + } + case domain.NotificationProviderTypeTelegramBot: { access := domain.AccessConfigForTelegramBot{} 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 51d4e7c4..5a576af1 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 @@ -102,9 +102,10 @@ 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(strings.TrimRight(authZone, ".")) + siteName := strings.TrimRight(authZone, ".") + siteId, err := d.getSiteId(siteName) if err != nil { - return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err) + return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err) } if err := d.addOrUpdateDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, "."), info.Value); err != nil { @@ -122,9 +123,10 @@ 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(strings.TrimRight(authZone, ".")) + siteName := strings.TrimRight(authZone, ".") + siteId, err := d.getSiteId(siteName) if err != nil { - return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err) + return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err) } if err := d.removeDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, ".")); err != nil { diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean/digitalocean.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean/digitalocean.go new file mode 100644 index 00000000..0e3cb358 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean/digitalocean.go @@ -0,0 +1,36 @@ +package namedotcom + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/digitalocean" +) + +type ChallengeProviderConfig struct { + AccessToken string `json:"accessToken"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := digitalocean.NewDefaultConfig() + providerConfig.AuthToken = config.AccessToken + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := digitalocean.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/duckdns/duckdns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/duckdns/duckdns.go new file mode 100644 index 00000000..6cc823d0 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/duckdns/duckdns.go @@ -0,0 +1,32 @@ +package namedotcom + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/duckdns" +) + +type ChallengeProviderConfig struct { + Token string `json:"token"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := duckdns.NewDefaultConfig() + providerConfig.Token = config.Token + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + + provider, err := duckdns.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/hetzner/hetzner.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/hetzner/hetzner.go new file mode 100644 index 00000000..c202cc78 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/hetzner/hetzner.go @@ -0,0 +1,36 @@ +package namedotcom + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/hetzner" +) + +type ChallengeProviderConfig struct { + ApiToken string `json:"apiToken"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := hetzner.NewDefaultConfig() + providerConfig.APIKey = config.ApiToken + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := hetzner.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go index 7630633c..b34516d4 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go @@ -11,7 +11,7 @@ import ( ) type ChallengeProviderConfig struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` @@ -23,9 +23,9 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, panic("config is nil") } - host, _ := url.Parse(config.ApiUrl) + serverUrl, _ := url.Parse(config.ServerUrl) providerConfig := pdns.NewDefaultConfig() - providerConfig.Host = host + providerConfig.Host = serverUrl providerConfig.APIKey = config.ApiKey if config.AllowInsecureConnections { providerConfig.HTTPClient.Transport = &http.Transport{ 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 e81b264f..cdeb8af5 100644 --- a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go +++ b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 1Panel 地址。 - ApiUrl string `json:"apiUrl"` + // 1Panel 服务地址。 + ServerUrl string `json:"serverUrl"` // 1Panel 版本。 // 可取值 "v1"、"v2"。 ApiVersion string `json:"apiVersion"` @@ -39,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -82,9 +82,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -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") +func createSdkClient(serverUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid 1panel server url") } if apiVersion == "" { @@ -95,7 +95,7 @@ func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*on return nil, errors.New("invalid 1panel api key") } - client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) + client := onepanelsdk.NewClient(serverUrl, 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 88bf961a..0feae021 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 @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiVersion string fApiKey string ) @@ -24,7 +24,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -35,7 +35,7 @@ Shell command to run this test: go test -v ./1panel_console_test.go -args \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIURL="http://127.0.0.1:20410" \ + --CERTIMATE_DEPLOYER_1PANELCONSOLE_SERVERURL="http://127.0.0.1:20410" \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIVERSION="v1" \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key" */ @@ -47,13 +47,13 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiVersion: fApiVersion, ApiKey: fApiKey, AllowInsecureConnections: 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 690e5242..07d124a3 100644 --- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go +++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go @@ -16,8 +16,8 @@ import ( ) type DeployerConfig struct { - // 1Panel 地址。 - ApiUrl string `json:"apiUrl"` + // 1Panel 服务地址。 + ServerUrl string `json:"serverUrl"` // 1Panel 版本。 // 可取值 "v1"、"v2"。 ApiVersion string `json:"apiVersion"` @@ -49,15 +49,16 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, 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, - ApiVersion: config.ApiVersion, - ApiKey: config.ApiKey, + ServerUrl: config.ServerUrl, + ApiVersion: config.ApiVersion, + ApiKey: config.ApiKey, + AllowInsecureConnections: config.AllowInsecureConnections, }) if err != nil { return nil, fmt.Errorf("failed to create ssl uploader: %w", err) @@ -177,9 +178,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -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") +func createSdkClient(serverUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid 1panel server url") } if apiVersion == "" { @@ -190,7 +191,7 @@ func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*on return nil, errors.New("invalid 1panel api key") } - client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) + client := onepanelsdk.NewClient(serverUrl, 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 1d5bafef..91b1ebb0 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 @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiVersion string fApiKey string fWebsiteId int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./1panel_site_test.go -args \ --CERTIMATE_DEPLOYER_1PANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_1PANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_1PANELSITE_APIURL="http://127.0.0.1:20410" \ + --CERTIMATE_DEPLOYER_1PANELSITE_SERVERURL="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" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("APIKEY: %v", fApiKey), fmt.Sprintf("WEBSITEID: %v", fWebsiteId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiVersion: fApiVersion, ApiKey: fApiKey, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go index b1a57a81..5709f82d 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go +++ b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 宝塔面板地址。 - ApiUrl string `json:"apiUrl"` + // 宝塔面板服务地址。 + ServerUrl string `json:"serverUrl"` // 宝塔面板接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -36,7 +36,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -82,16 +82,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE 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") +func createSdkClient(serverUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid baota server url") } if apiKey == "" { return nil, errors.New("invalid baota api key") } - client := btsdk.NewClient(apiUrl, apiKey) + client := btsdk.NewClient(serverUrl, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go index 5f3845e4..2fd4cc5b 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go +++ b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string ) @@ -23,7 +23,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -33,7 +33,7 @@ Shell command to run this test: go test -v ./baotapanel_console_test.go -args \ --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_APIKEY="your-api-key" */ func TestDeploy(t *testing.T) { @@ -44,12 +44,12 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, AllowInsecureConnections: true, AutoRestart: true, diff --git a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go index 4d481f7f..d6ee1533 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go @@ -14,8 +14,8 @@ import ( ) type DeployerConfig struct { - // 宝塔面板地址。 - ApiUrl string `json:"apiUrl"` + // 宝塔面板服务地址。 + ServerUrl string `json:"serverUrl"` // 宝塔面板接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -41,7 +41,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -124,16 +124,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE 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") +func createSdkClient(serverUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid baota server url") } if apiKey == "" { return nil, errors.New("invalid baota api key") } - client := btsdk.NewClient(apiUrl, apiKey) + client := btsdk.NewClient(serverUrl, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go index 5fece978..9e4659ea 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string fSiteType string fSiteName string @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") flag.StringVar(&fSiteType, argsPrefix+"SITETYPE", "", "") flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./baotapanel_site_test.go -args \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_BAOTAPANELSITE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_BAOTAPANELSITE_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_APIKEY="your-api-key" \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_SITETYPE="php" \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_SITENAME="your-site-name" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), fmt.Sprintf("SITETYPE: %v", fSiteType), fmt.Sprintf("SITENAME: %v", fSiteName), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, AllowInsecureConnections: true, SiteType: fSiteType, diff --git a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go index 811b350b..482ca8e4 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go +++ b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 堡塔云 WAF 地址。 - ApiUrl string `json:"apiUrl"` + // 堡塔云 WAF 服务地址。 + ServerUrl string `json:"serverUrl"` // 堡塔云 WAF 接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -34,7 +34,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -70,16 +70,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE 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") +func createSdkClient(serverUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid baota server url") } if apiKey == "" { return nil, errors.New("invalid baota api key") } - client := btsdk.NewClient(apiUrl, apiKey) + client := btsdk.NewClient(serverUrl, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } 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 index ba6ddd26..b3804fb5 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go +++ b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string fSiteName string fSitePort int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -35,7 +35,7 @@ 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_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_APIKEY="your-api-key" */ func TestDeploy(t *testing.T) { @@ -46,12 +46,12 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, AllowInsecureConnections: true, }) diff --git a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go index ed05937a..435f7a69 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go +++ b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go @@ -14,8 +14,8 @@ import ( ) type DeployerConfig struct { - // 堡塔云 WAF 地址。 - ApiUrl string `json:"apiUrl"` + // 堡塔云 WAF 服务地址。 + ServerUrl string `json:"serverUrl"` // 堡塔云 WAF 接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -40,7 +40,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -133,16 +133,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE 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") +func createSdkClient(serverUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid baota server url") } if apiKey == "" { return nil, errors.New("invalid baota api key") } - client := btsdk.NewClient(apiUrl, apiKey) + client := btsdk.NewClient(serverUrl, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } 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 index 4e1ffe34..6bead4b5 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go +++ b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string fSiteName string fSitePort int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") flag.Int64Var(&fSitePort, argsPrefix+"SITEPORT", 0, "") @@ -37,7 +37,7 @@ 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_SERVERURL="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 @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), fmt.Sprintf("SITENAME: %v", fSiteName), fmt.Sprintf("SITEPORT: %v", fSitePort), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, AllowInsecureConnections: true, SiteName: fSiteName, diff --git a/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go index 909caf3e..1ced8caf 100644 --- a/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go +++ b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go @@ -15,8 +15,8 @@ import ( ) type DeployerConfig struct { - // Cdnfly 地址。 - ApiUrl string `json:"apiUrl"` + // Cdnfly 服务地址。 + ServerUrl string `json:"serverUrl"` // Cdnfly 用户端 API Key。 ApiKey string `json:"apiKey"` // Cdnfly 用户端 API Secret。 @@ -46,7 +46,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.ApiSecret, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.ApiSecret, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -160,9 +160,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiKey, apiSecret string, skipTlsVerify bool) (*cfsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid cachefly api url") +func createSdkClient(serverUrl, apiKey, apiSecret string, skipTlsVerify bool) (*cfsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid cachefly server url") } if apiKey == "" { @@ -173,7 +173,7 @@ func createSdkClient(apiUrl, apiKey, apiSecret string, skipTlsVerify bool) (*cfs return nil, errors.New("invalid cachefly api secret") } - client := cfsdk.NewClient(apiUrl, apiKey, apiSecret) + client := cfsdk.NewClient(serverUrl, apiKey, apiSecret) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go b/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go index 26486721..73128183 100644 --- a/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go +++ b/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string fApiSecret string fCertificateId string @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") flag.StringVar(&fApiSecret, argsPrefix+"APISECRET", "", "") flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./cdnfly_test.go -args \ --CERTIMATE_DEPLOYER_CDNFLY_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_CDNFLY_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_CDNFLY_APIURL="http://127.0.0.1:88" \ + --CERTIMATE_DEPLOYER_CDNFLY_SERVERURL="http://127.0.0.1:88" \ --CERTIMATE_DEPLOYER_CDNFLY_APIKEY="your-api-key" \ --CERTIMATE_DEPLOYER_CDNFLY_APISECRET="your-api-secret" \ --CERTIMATE_DEPLOYER_CDNFLY_CERTIFICATEID="your-cert-id" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), fmt.Sprintf("APISECRET: %v", fApiSecret), fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, ApiSecret: fApiSecret, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go index a12ed164..8b692e90 100644 --- a/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go +++ b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go @@ -16,8 +16,8 @@ import ( ) type DeployerConfig struct { - // FlexCDN URL。 - ApiUrl string `json:"apiUrl"` + // FlexCDN 服务地址。 + ServerUrl string `json:"serverUrl"` // FlexCDN 用户角色。 // 可取值 "user"、"admin"。 ApiRole string `json:"apiRole"` @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -119,9 +119,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri 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") +func createSdkClient(serverUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*flexcdnsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid flexcdn server url") } if apiRole != "user" && apiRole != "admin" { @@ -136,7 +136,7 @@ func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVeri return nil, errors.New("invalid flexcdn access key") } - client := flexcdnsdk.NewClient(apiUrl, apiRole, accessKeyId, accessKey) + client := flexcdnsdk.NewClient(serverUrl, apiRole, accessKeyId, accessKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go b/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go index b9b8de07..6725140a 100644 --- a/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go +++ b/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fAccessKeyId string fAccessKey string fCertificateId int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "") flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") @@ -37,7 +37,7 @@ 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_SERVERURL="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" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), fmt.Sprintf("ACCESSKEY: %v", fAccessKey), fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiRole: "user", AccessKeyId: fAccessKeyId, AccessKey: fAccessKey, diff --git a/internal/pkg/core/deployer/providers/goedge/goedge.go b/internal/pkg/core/deployer/providers/goedge/goedge.go index ecae774e..25caeb01 100644 --- a/internal/pkg/core/deployer/providers/goedge/goedge.go +++ b/internal/pkg/core/deployer/providers/goedge/goedge.go @@ -16,8 +16,8 @@ import ( ) type DeployerConfig struct { - // GoEdge URL。 - ApiUrl string `json:"apiUrl"` + // GoEdge 服务地址。 + ServerUrl string `json:"serverUrl"` // GoEdge 用户角色。 // 可取值 "user"、"admin"。 ApiRole string `json:"apiRole"` @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -119,9 +119,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*goedgesdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid goedge api url") +func createSdkClient(serverUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*goedgesdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid goedge server url") } if apiRole != "user" && apiRole != "admin" { @@ -136,7 +136,7 @@ func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVeri return nil, errors.New("invalid goedge access key") } - client := goedgesdk.NewClient(apiUrl, apiRole, accessKeyId, accessKey) + client := goedgesdk.NewClient(serverUrl, apiRole, accessKeyId, accessKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/goedge/goedge_test.go b/internal/pkg/core/deployer/providers/goedge/goedge_test.go index d10f931c..ae03db1d 100644 --- a/internal/pkg/core/deployer/providers/goedge/goedge_test.go +++ b/internal/pkg/core/deployer/providers/goedge/goedge_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fAccessKeyId string fAccessKey string fCertificateId int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "") flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./goedge_test.go -args \ --CERTIMATE_DEPLOYER_GOEDGE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_GOEDGE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_GOEDGE_APIURL="http://127.0.0.1:7788" \ + --CERTIMATE_DEPLOYER_GOEDGE_SERVERURL="http://127.0.0.1:7788" \ --CERTIMATE_DEPLOYER_GOEDGE_ACCESSKEYID="your-access-key-id" \ --CERTIMATE_DEPLOYER_GOEDGE_ACCESSKEY="your-access-key" \ --CERTIMATE_DEPLOYER_GOEDGE_CERTIFICATEID="your-cerficiate-id" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), fmt.Sprintf("ACCESSKEY: %v", fAccessKey), fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiRole: "user", AccessKeyId: fAccessKeyId, AccessKey: fAccessKey, diff --git a/internal/pkg/core/deployer/providers/lecdn/lecdn.go b/internal/pkg/core/deployer/providers/lecdn/lecdn.go index 1ad88dcf..c85f6558 100644 --- a/internal/pkg/core/deployer/providers/lecdn/lecdn.go +++ b/internal/pkg/core/deployer/providers/lecdn/lecdn.go @@ -15,8 +15,8 @@ import ( ) type DeployerConfig struct { - // LeCDN URL。 - ApiUrl string `json:"apiUrl"` + // LeCDN 服务地址。 + ServerUrl string `json:"serverUrl"` // LeCDN 版本。 // 可取值 "v3"。 ApiVersion string `json:"apiVersion"` @@ -59,7 +59,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiRole, config.Username, config.Password, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiVersion, config.ApiRole, config.Username, config.Password, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -141,9 +141,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri 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") +func createSdkClient(serverUrl, apiVersion, apiRole, username, password string, skipTlsVerify bool) (interface{}, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid lecdn server url") } if username == "" { @@ -156,7 +156,7 @@ func createSdkClient(apiUrl, apiVersion, apiRole, username, password string, ski if apiVersion == apiVersionV3 && apiRole == apiRoleClient { // v3 版客户端 - client := leclientsdkv3.NewClient(apiUrl, username, password) + client := leclientsdkv3.NewClient(serverUrl, username, password) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } @@ -164,7 +164,7 @@ func createSdkClient(apiUrl, apiVersion, apiRole, username, password string, ski return client, nil } else if apiVersion == apiVersionV3 && apiRole == apiRoleMaster { // v3 版主控端 - client := lemastersdkv3.NewClient(apiUrl, username, password) + client := lemastersdkv3.NewClient(serverUrl, username, password) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go b/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go index cbaa4523..fda880c4 100644 --- a/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go +++ b/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiVersion string fUsername string fPassword string @@ -26,7 +26,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v3", "") flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "") flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "") @@ -39,7 +39,7 @@ 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_SERVERURL="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" @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("USERNAME: %v", fUsername), fmt.Sprintf("PASSWORD: %v", fPassword), @@ -60,7 +60,7 @@ func TestDeploy(t *testing.T) { }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiVersion: fApiVersion, ApiRole: "user", Username: fUsername, diff --git a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go index d2e92460..349c3a16 100644 --- a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go +++ b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go @@ -16,8 +16,8 @@ import ( ) type DeployerConfig struct { - // Proxmox VE 地址。 - ApiUrl string `json:"apiUrl"` + // Proxmox VE 服务地址。 + ServerUrl string `json:"serverUrl"` // Proxmox VE API Token。 ApiToken string `json:"apiToken"` // Proxmox VE API Token Secret。 @@ -43,7 +43,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiToken, config.ApiTokenSecret, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiToken, config.ApiTokenSecret, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -91,9 +91,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiToken, apiTokenSecret string, skipTlsVerify bool) (*proxmox.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid pve api url") +func createSdkClient(serverUrl, apiToken, apiTokenSecret string, skipTlsVerify bool) (*proxmox.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid pve server url") } if apiToken == "" { @@ -112,7 +112,7 @@ func createSdkClient(apiUrl, apiToken, apiTokenSecret string, skipTlsVerify bool } } client := proxmox.NewClient( - strings.TrimRight(apiUrl, "/")+"/api2/json", + strings.TrimRight(serverUrl, "/")+"/api2/json", proxmox.WithHTTPClient(httpClient), proxmox.WithAPIToken(apiToken, apiTokenSecret), ) diff --git a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go index 6251bd75..8ae02f3b 100644 --- a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go +++ b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiToken string fApiTokenSecret string fNodeName string @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") flag.StringVar(&fApiTokenSecret, argsPrefix+"APITOKENSECRET", "", "") flag.StringVar(&fNodeName, argsPrefix+"NODENAME", "", "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./proxmoxve_test.go -args \ --CERTIMATE_DEPLOYER_PROXMOXVE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_PROXMOXVE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_PROXMOXVE_APIURL="http://127.0.0.1:8006" \ + --CERTIMATE_DEPLOYER_PROXMOXVE_SERVERURL="http://127.0.0.1:8006" \ --CERTIMATE_DEPLOYER_PROXMOXVE_APITOKEN="your-api-token" \ --CERTIMATE_DEPLOYER_PROXMOXVE_APITOKENSECRET="your-api-token-secret" \ --CERTIMATE_DEPLOYER_PROXMOXVE_NODENAME="your-cluster-node-name" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APITOKEN: %v", fApiToken), fmt.Sprintf("APITOKENSECRET: %v", fApiTokenSecret), fmt.Sprintf("NODENAME: %v", fNodeName), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiToken: fApiToken, ApiTokenSecret: fApiTokenSecret, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go index 51faf4f2..651ae0ac 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go +++ b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 耗子面板地址。 - ApiUrl string `json:"apiUrl"` + // 耗子面板服务地址。 + ServerUrl string `json:"serverUrl"` // 耗子面板访问令牌 ID。 AccessTokenId int32 `json:"accessTokenId"` // 耗子面板访问令牌。 @@ -36,7 +36,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.AccessTokenId, config.AccessToken, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.AccessTokenId, config.AccessToken, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -72,9 +72,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE 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") +func createSdkClient(serverUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid ratpanel server url") } if accessTokenId == 0 { @@ -85,7 +85,7 @@ func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, ski return nil, errors.New("invalid ratpanel access token") } - client := rpsdk.NewClient(apiUrl, accessTokenId, accessToken) + client := rpsdk.NewClient(serverUrl, accessTokenId, accessToken) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } 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 index 3f3193b3..3366b06c 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go +++ b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fAccessTokenId int64 fAccessToken string ) @@ -24,7 +24,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "") flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "") } @@ -35,7 +35,7 @@ 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_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKENID="your-access-token-id" \ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKEN="your-access-token" */ @@ -47,13 +47,13 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("ACCESSTOKENID: %v", fAccessTokenId), fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, AccessTokenId: int32(fAccessTokenId), AccessToken: fAccessToken, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go index b4e283be..8d605b3d 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go +++ b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 耗子面板地址。 - ApiUrl string `json:"apiUrl"` + // 耗子面板服务地址。 + ServerUrl string `json:"serverUrl"` // 耗子面板访问令牌 ID。 AccessTokenId int32 `json:"accessTokenId"` // 耗子面板访问令牌。 @@ -38,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.AccessTokenId, config.AccessToken, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.AccessTokenId, config.AccessToken, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -79,9 +79,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE 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") +func createSdkClient(serverUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid ratpanel server url") } if accessTokenId == 0 { @@ -92,7 +92,7 @@ func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, ski return nil, errors.New("invalid ratpanel access token") } - client := rpsdk.NewClient(apiUrl, accessTokenId, accessToken) + client := rpsdk.NewClient(serverUrl, accessTokenId, accessToken) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } 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 index 658175fb..cd84b3ea 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go +++ b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fAccessTokenId int64 fAccessToken string fSiteName string @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "") flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "") flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") @@ -37,7 +37,7 @@ 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_SERVERURL="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" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("ACCESSTOKENID: %v", fAccessTokenId), fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken), fmt.Sprintf("SITENAME: %v", fSiteName), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, AccessTokenId: int32(fAccessTokenId), AccessToken: fAccessToken, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/safeline/safeline.go b/internal/pkg/core/deployer/providers/safeline/safeline.go index f737fda9..f1b6b039 100644 --- a/internal/pkg/core/deployer/providers/safeline/safeline.go +++ b/internal/pkg/core/deployer/providers/safeline/safeline.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 雷池 URL。 - ApiUrl string `json:"apiUrl"` + // 雷池服务地址。 + ServerUrl string `json:"serverUrl"` // 雷池 API Token。 ApiToken string `json:"apiToken"` // 是否允许不安全的连接。 @@ -39,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiToken, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiToken, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -98,16 +98,16 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiToken string, skipTlsVerify bool) (*safelinesdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid safeline api url") +func createSdkClient(serverUrl, apiToken string, skipTlsVerify bool) (*safelinesdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid safeline server url") } if apiToken == "" { return nil, errors.New("invalid safeline api token") } - client := safelinesdk.NewClient(apiUrl, apiToken) + client := safelinesdk.NewClient(serverUrl, apiToken) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/safeline/safeline_test.go b/internal/pkg/core/deployer/providers/safeline/safeline_test.go index 67fe6755..9730473c 100644 --- a/internal/pkg/core/deployer/providers/safeline/safeline_test.go +++ b/internal/pkg/core/deployer/providers/safeline/safeline_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiToken string fCertificateId int64 ) @@ -24,7 +24,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") } @@ -35,7 +35,7 @@ Shell command to run this test: go test -v ./safeline_test.go -args \ --CERTIMATE_DEPLOYER_SAFELINE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_SAFELINE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_SAFELINE_APIURL="http://127.0.0.1:9443" \ + --CERTIMATE_DEPLOYER_SAFELINE_SERVERURL="http://127.0.0.1:9443" \ --CERTIMATE_DEPLOYER_SAFELINE_APITOKEN="your-api-token" \ --CERTIMATE_DEPLOYER_SAFELINE_CERTIFICATEID="your-cerficiate-id" */ @@ -47,13 +47,13 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APITOKEN: %v", fApiToken), fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiToken: fApiToken, AllowInsecureConnections: true, ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, diff --git a/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost.go b/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost.go new file mode 100644 index 00000000..e24708bd --- /dev/null +++ b/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost.go @@ -0,0 +1,101 @@ +package unicloudwebhost + +import ( + "context" + "errors" + "fmt" + "log/slog" + "net/url" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + unisdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/dcloud/unicloud" +) + +type DeployerConfig struct { + // uniCloud 控制台账号。 + Username string `json:"username"` + // uniCloud 控制台密码。 + Password string `json:"password"` + // 服务空间提供商。 + // 可取值 "aliyun"、"tencent"。 + SpaceProvider string `json:"spaceProvider"` + // 服务空间 ID。 + SpaceId string `json:"spaceId"` + // 托管网站域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *unisdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.Username, config.Password) + 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.SpaceProvider == "" { + return nil, errors.New("config `spaceProvider` is required") + } + if d.config.SpaceId == "" { + return nil, errors.New("config `spaceId` is required") + } + if d.config.Domain == "" { + return nil, errors.New("config `domain` is required") + } + + // 变更网站证书 + createDomainWithCertReq := &unisdk.CreateDomainWithCertRequest{ + Provider: d.config.SpaceProvider, + SpaceId: d.config.SpaceId, + Domain: d.config.Domain, + Cert: url.QueryEscape(certPEM), + Key: url.QueryEscape(privkeyPEM), + } + createDomainWithCertResp, err := d.sdkClient.CreateDomainWithCert(createDomainWithCertReq) + d.logger.Debug("sdk request 'unicloud.host.CreateDomainWithCert'", slog.Any("request", createDomainWithCertReq), slog.Any("response", createDomainWithCertResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'unicloud.host.CreateDomainWithCert': %w", err) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(username, password string) (*unisdk.Client, error) { + if username == "" { + return nil, errors.New("invalid unicloud username") + } + + if password == "" { + return nil, errors.New("invalid unicloud password") + } + + client := unisdk.NewClient(username, password) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost_test.go b/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost_test.go new file mode 100644 index 00000000..1e47ba24 --- /dev/null +++ b/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost_test.go @@ -0,0 +1,85 @@ +package unicloudwebhost_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/unicloud-webhost" +) + +var ( + fInputCertPath string + fInputKeyPath string + fUsername string + fPassword string + fSpaceProvider string + fSpaceId string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "") + flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "") + flag.StringVar(&fSpaceProvider, argsPrefix+"SPACEPROVIDER", "", "") + flag.StringVar(&fSpaceId, argsPrefix+"SPACEID", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./unicloud_webhost_test.go -args \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_USERNAME="your-username" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_PASSWORD="your-password" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_SPACEPROVIDER="aliyun/tencent" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_SPACEID="your-space-id" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_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("USERNAME: %v", fUsername), + fmt.Sprintf("PASSWORD: %v", fPassword), + fmt.Sprintf("SPACEPROVIDER: %v", fSpaceProvider), + fmt.Sprintf("SPACEID: %v", fSpaceId), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + Username: fUsername, + Password: fPassword, + SpaceProvider: fSpaceProvider, + SpaceId: fSpaceId, + 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/notifier/providers/bark/bark.go b/internal/pkg/core/notifier/providers/bark/bark.go index 97ece0be..ec0d44f3 100644 --- a/internal/pkg/core/notifier/providers/bark/bark.go +++ b/internal/pkg/core/notifier/providers/bark/bark.go @@ -58,6 +58,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // REF: https://bark.day.app/#/tutorial req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "title": subject, diff --git a/internal/pkg/core/notifier/providers/discordbot/discordbot.go b/internal/pkg/core/notifier/providers/discordbot/discordbot.go new file mode 100644 index 00000000..3ed0cab7 --- /dev/null +++ b/internal/pkg/core/notifier/providers/discordbot/discordbot.go @@ -0,0 +1,68 @@ +package discordbot + +import ( + "context" + "fmt" + "log/slog" + + "github.com/go-resty/resty/v2" + + "github.com/usual2970/certimate/internal/pkg/core/notifier" +) + +type NotifierConfig struct { + // Discord Bot API Token。 + BotToken string `json:"botToken"` + // Discord Channel ID。 + ChannelId string `json:"channelId"` +} + +type NotifierProvider struct { + config *NotifierConfig + logger *slog.Logger + httpClient *resty.Client +} + +var _ notifier.Notifier = (*NotifierProvider)(nil) + +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { + if config == nil { + panic("config is nil") + } + + client := resty.New() + + return &NotifierProvider{ + config: config, + logger: slog.Default(), + httpClient: client, + }, nil +} + +func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { + if logger == nil { + n.logger = slog.Default() + } else { + n.logger = logger + } + return n +} + +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { + // REF: https://discord.com/developers/docs/resources/message#create-message + req := n.httpClient.R(). + SetContext(ctx). + SetHeader("Content-Type", "application/json"). + SetHeader("Authorization", "Bot "+n.config.BotToken). + SetBody(map[string]any{ + "content": subject + "\n" + message, + }) + resp, err := req.Post(fmt.Sprintf("https://discord.com/api/v9/channels/%s/messages", n.config.ChannelId)) + if err != nil { + return nil, fmt.Errorf("discord api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("discord api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return ¬ifier.NotifyResult{}, nil +} diff --git a/internal/pkg/core/notifier/providers/discordbot/discordbot_test.go b/internal/pkg/core/notifier/providers/discordbot/discordbot_test.go new file mode 100644 index 00000000..42edf95e --- /dev/null +++ b/internal/pkg/core/notifier/providers/discordbot/discordbot_test.go @@ -0,0 +1,64 @@ +package discordbot_test + +import ( + "context" + "flag" + "fmt" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/discordbot" +) + +const ( + mockSubject = "test_subject" + mockMessage = "test_message" +) + +var ( + fApiToken string + fChannelId string +) + +func init() { + argsPrefix := "CERTIMATE_NOTIFIER_DISCORDBOT_" + + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") + flag.StringVar(&fChannelId, argsPrefix+"CHANNELID", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./discordbot_test.go -args \ + --CERTIMATE_NOTIFIER_DISCORDBOT_APITOKEN="your-bot-token" \ + --CERTIMATE_NOTIFIER_DISCORDBOT_CHANNELID="your-channel-id" +*/ +func TestNotify(t *testing.T) { + flag.Parse() + + t.Run("Notify", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("APITOKEN: %v", fApiToken), + fmt.Sprintf("CHANNELID: %v", fChannelId), + }, "\n")) + + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ + BotToken: fApiToken, + ChannelId: fChannelId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + res, err := notifier.Notify(context.Background(), mockSubject, mockMessage) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/notifier/providers/gotify/gotify.go b/internal/pkg/core/notifier/providers/gotify/gotify.go index 81dcb8ad..c82cd5a5 100644 --- a/internal/pkg/core/notifier/providers/gotify/gotify.go +++ b/internal/pkg/core/notifier/providers/gotify/gotify.go @@ -56,6 +56,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // REF: https://gotify.net/api-docs#/message/createMessage req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetHeader("Authorization", "Bearer "+n.config.Token). SetBody(map[string]any{ diff --git a/internal/pkg/core/notifier/providers/mattermost/mattermost.go b/internal/pkg/core/notifier/providers/mattermost/mattermost.go index a9b2f4d6..81283f7c 100644 --- a/internal/pkg/core/notifier/providers/mattermost/mattermost.go +++ b/internal/pkg/core/notifier/providers/mattermost/mattermost.go @@ -12,13 +12,13 @@ import ( ) type NotifierConfig struct { - // 服务地址。 + // Mattermost 服务地址。 ServerUrl string `json:"serverUrl"` - // 用户名。 + // Mattermost 用户名。 Username string `json:"username"` - // 密码。 + // Mattermost 密码。 Password string `json:"password"` - // 频道 ID。 + // Mattermost 频道 ID。 ChannelId string `json:"channelId"` } @@ -58,6 +58,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // REF: https://developers.mattermost.com/api-documentation/#/operations/Login loginReq := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "login_id": n.config.Username, @@ -74,6 +75,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // REF: https://developers.mattermost.com/api-documentation/#/operations/CreatePost postReq := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetHeader("Authorization", "Bearer "+loginResp.Header().Get("Token")). SetBody(map[string]any{ diff --git a/internal/pkg/core/notifier/providers/pushover/pushover.go b/internal/pkg/core/notifier/providers/pushover/pushover.go index b7f74bba..827a45d6 100644 --- a/internal/pkg/core/notifier/providers/pushover/pushover.go +++ b/internal/pkg/core/notifier/providers/pushover/pushover.go @@ -51,6 +51,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://pushover.net/api req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "title": subject, diff --git a/internal/pkg/core/notifier/providers/pushplus/pushplus.go b/internal/pkg/core/notifier/providers/pushplus/pushplus.go index 834f9683..79a27d49 100644 --- a/internal/pkg/core/notifier/providers/pushplus/pushplus.go +++ b/internal/pkg/core/notifier/providers/pushplus/pushplus.go @@ -50,6 +50,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // 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(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "title": subject, diff --git a/internal/pkg/core/notifier/providers/serverchan/serverchan.go b/internal/pkg/core/notifier/providers/serverchan/serverchan.go index d74b2fcc..d1897ab4 100644 --- a/internal/pkg/core/notifier/providers/serverchan/serverchan.go +++ b/internal/pkg/core/notifier/providers/serverchan/serverchan.go @@ -49,6 +49,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://sct.ftqq.com/ req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "text": subject, diff --git a/internal/pkg/core/notifier/providers/slackbot/slackbot.go b/internal/pkg/core/notifier/providers/slackbot/slackbot.go new file mode 100644 index 00000000..7b16ad25 --- /dev/null +++ b/internal/pkg/core/notifier/providers/slackbot/slackbot.go @@ -0,0 +1,70 @@ +package discordbot + +import ( + "context" + "fmt" + "log/slog" + + "github.com/go-resty/resty/v2" + + "github.com/usual2970/certimate/internal/pkg/core/notifier" +) + +type NotifierConfig struct { + // Slack Bot API Token。 + BotToken string `json:"botToken"` + // Slack Channel ID。 + ChannelId string `json:"channelId"` +} + +type NotifierProvider struct { + config *NotifierConfig + logger *slog.Logger + httpClient *resty.Client +} + +var _ notifier.Notifier = (*NotifierProvider)(nil) + +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { + if config == nil { + panic("config is nil") + } + + client := resty.New() + + return &NotifierProvider{ + config: config, + logger: slog.Default(), + httpClient: client, + }, nil +} + +func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { + if logger == nil { + n.logger = slog.Default() + } else { + n.logger = logger + } + return n +} + +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { + // REF: https://docs.slack.dev/messaging/sending-and-scheduling-messages#publishing + req := n.httpClient.R(). + SetContext(ctx). + SetHeader("Content-Type", "application/json"). + SetHeader("Authorization", "Bearer "+n.config.BotToken). + SetBody(map[string]any{ + "token": n.config.BotToken, + "channel": n.config.ChannelId, + "text": subject + "\n" + message, + }) + resp, err := req.Post("https://slack.com/api/chat.postMessage") + if err != nil { + return nil, fmt.Errorf("slack api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("slack api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return ¬ifier.NotifyResult{}, nil +} diff --git a/internal/pkg/core/notifier/providers/slackbot/slackbot_test.go b/internal/pkg/core/notifier/providers/slackbot/slackbot_test.go new file mode 100644 index 00000000..356ef71f --- /dev/null +++ b/internal/pkg/core/notifier/providers/slackbot/slackbot_test.go @@ -0,0 +1,64 @@ +package discordbot_test + +import ( + "context" + "flag" + "fmt" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/slackbot" +) + +const ( + mockSubject = "test_subject" + mockMessage = "test_message" +) + +var ( + fApiToken string + fChannelId string +) + +func init() { + argsPrefix := "CERTIMATE_NOTIFIER_SLACKBOT_" + + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") + flag.StringVar(&fChannelId, argsPrefix+"CHANNELID", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./slackbot_test.go -args \ + --CERTIMATE_NOTIFIER_SLACKBOT_APITOKEN="your-bot-token" \ + --CERTIMATE_NOTIFIER_SLACKBOT_CHANNELID="your-channel-id" +*/ +func TestNotify(t *testing.T) { + flag.Parse() + + t.Run("Notify", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("APITOKEN: %v", fApiToken), + fmt.Sprintf("CHANNELID: %v", fChannelId), + }, "\n")) + + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ + BotToken: fApiToken, + ChannelId: fChannelId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + res, err := notifier.Notify(context.Background(), mockSubject, mockMessage) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go b/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go index 99b86a38..39e1f705 100644 --- a/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go +++ b/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go @@ -51,6 +51,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://core.telegram.org/bots/api#sendmessage req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "chat_id": n.config.ChatId, diff --git a/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go b/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go index 3a207384..8dc18b95 100644 --- a/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go +++ b/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go @@ -17,14 +17,14 @@ const ( var ( fApiToken string - fChartId int64 + fChatId int64 ) func init() { argsPrefix := "CERTIMATE_NOTIFIER_TELEGRAMBOT_" flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") - flag.Int64Var(&fChartId, argsPrefix+"CHATID", 0, "") + flag.Int64Var(&fChatId, argsPrefix+"CHATID", 0, "") } /* @@ -41,12 +41,12 @@ func TestNotify(t *testing.T) { t.Log(strings.Join([]string{ "args:", fmt.Sprintf("APITOKEN: %v", fApiToken), - fmt.Sprintf("CHATID: %v", fChartId), + fmt.Sprintf("CHATID: %v", fChatId), }, "\n")) notifier, err := provider.NewNotifier(&provider.NotifierConfig{ BotToken: fApiToken, - ChatId: fChartId, + ChatId: fChatId, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/internal/pkg/core/notifier/providers/webhook/webhook.go b/internal/pkg/core/notifier/providers/webhook/webhook.go index 5f62f170..8850ea73 100644 --- a/internal/pkg/core/notifier/providers/webhook/webhook.go +++ b/internal/pkg/core/notifier/providers/webhook/webhook.go @@ -139,7 +139,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // 生成请求 // 其中 GET 请求需转换为查询参数 - req := n.httpClient.R().SetHeaderMultiValues(webhookHeaders) + req := n.httpClient.R().SetContext(ctx).SetHeaderMultiValues(webhookHeaders) req.URL = webhookUrl.String() req.Method = webhookMethod if webhookMethod == http.MethodGet { diff --git a/internal/pkg/core/notifier/providers/wecombot/wecombot.go b/internal/pkg/core/notifier/providers/wecombot/wecombot.go index 36c179d4..8f51a70a 100644 --- a/internal/pkg/core/notifier/providers/wecombot/wecombot.go +++ b/internal/pkg/core/notifier/providers/wecombot/wecombot.go @@ -49,6 +49,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://developer.work.weixin.qq.com/document/path/91770 req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "msgtype": "text", 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 63900125..ca3c7303 100644 --- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go +++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go @@ -2,6 +2,7 @@ package onepanelssl import ( "context" + "crypto/tls" "errors" "fmt" "log/slog" @@ -14,12 +15,14 @@ import ( ) type UploaderConfig struct { - // 1Panel 地址。 - ApiUrl string `json:"apiUrl"` + // 1Panel 服务地址。 + ServerUrl string `json:"serverUrl"` // 1Panel 版本。 ApiVersion string `json:"apiVersion"` // 1Panel 接口密钥。 ApiKey string `json:"apiKey"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type UploaderProvider struct { @@ -35,7 +38,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey) + client, err := createSdkClient(config.ServerUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -132,9 +135,9 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string, return nil, nil } -func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid 1panel api url") +func createSdkClient(serverUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid 1panel server url") } if apiVersion == "" { @@ -145,6 +148,10 @@ func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, er return nil, errors.New("invalid 1panel api key") } - client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) + client := onepanelsdk.NewClient(serverUrl, apiVersion, apiKey) + if skipTlsVerify { + client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + 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 cfb250be..d0af7c12 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 @@ -15,7 +15,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiVersion string fApiKey string ) @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -36,7 +36,7 @@ Shell command to run this test: go test -v ./1panel_ssl_test.go -args \ --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_SERVERURL="http://127.0.0.1:20410" \ --CERTIMATE_UPLOADER_1PANELSSL_APIVERSION="v1" \ --CERTIMATE_UPLOADER_1PANELSSL_APIKEY="your-api-key" */ @@ -48,13 +48,13 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) uploader, err := provider.NewUploader(&provider.UploaderConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiVersion: fApiVersion, ApiKey: fApiKey, }) diff --git a/internal/pkg/sdk3rd/1panel/client.go b/internal/pkg/sdk3rd/1panel/client.go index 003203d3..3fe549a0 100644 --- a/internal/pkg/sdk3rd/1panel/client.go +++ b/internal/pkg/sdk3rd/1panel/client.go @@ -19,13 +19,13 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiVersion, apiKey string) *Client { +func NewClient(serverUrl, apiVersion, apiKey string) *Client { if apiVersion == "" { apiVersion = "v1" } client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/") + "/api/" + apiVersion). + SetBaseURL(strings.TrimRight(serverUrl, "/") + "/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)) diff --git a/internal/pkg/sdk3rd/btpanel/client.go b/internal/pkg/sdk3rd/btpanel/client.go index 1da625da..aafee04f 100644 --- a/internal/pkg/sdk3rd/btpanel/client.go +++ b/internal/pkg/sdk3rd/btpanel/client.go @@ -19,9 +19,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiKey string) *Client { +func NewClient(serverUrl, apiKey string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")) + SetBaseURL(strings.TrimRight(serverUrl, "/")) return &Client{ apiKey: apiKey, diff --git a/internal/pkg/sdk3rd/btwaf/client.go b/internal/pkg/sdk3rd/btwaf/client.go index 5ae545cc..083db0c1 100644 --- a/internal/pkg/sdk3rd/btwaf/client.go +++ b/internal/pkg/sdk3rd/btwaf/client.go @@ -17,9 +17,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiKey string) *Client { +func NewClient(serverUrl, apiKey string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/") + "/api"). + SetBaseURL(strings.TrimRight(serverUrl, "/") + "/api"). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { timestamp := fmt.Sprintf("%d", time.Now().Unix()) keyMd5 := md5.Sum([]byte(apiKey)) diff --git a/internal/pkg/sdk3rd/cdnfly/client.go b/internal/pkg/sdk3rd/cdnfly/client.go index c8753ed5..2dabf6fd 100644 --- a/internal/pkg/sdk3rd/cdnfly/client.go +++ b/internal/pkg/sdk3rd/cdnfly/client.go @@ -15,9 +15,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiKey, apiSecret string) *Client { +func NewClient(serverUrl, apiKey, apiSecret string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")). + SetBaseURL(strings.TrimRight(serverUrl, "/")). SetHeader("api-key", apiKey). SetHeader("api-secret", apiSecret) diff --git a/internal/pkg/sdk3rd/dcloud/unicloud/api.go b/internal/pkg/sdk3rd/dcloud/unicloud/api.go new file mode 100644 index 00000000..1cd90b15 --- /dev/null +++ b/internal/pkg/sdk3rd/dcloud/unicloud/api.go @@ -0,0 +1,78 @@ +package unicloud + +import ( + "fmt" + "net/http" + "regexp" + "time" +) + +func (c *Client) ensureServerlessJwtTokenExists() error { + c.serverlessJwtTokenMtx.Lock() + defer c.serverlessJwtTokenMtx.Unlock() + if c.serverlessJwtToken != "" && c.serverlessJwtTokenExp.After(time.Now()) { + return nil + } + + params := &loginParams{ + Password: c.password, + } + if regexp.MustCompile("^1\\d{10}$").MatchString(c.username) { + params.Mobile = c.username + } else if regexp.MustCompile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$").MatchString(c.username) { + params.Email = c.username + } else { + params.Username = c.username + } + + resp := &loginResponse{} + if err := c.invokeServerlessWithResult( + uniIdentityEndpoint, uniIdentityClientSecret, uniIdentityAppId, uniIdentitySpaceId, + "uni-id-co", "login", "", params, nil, + resp); err != nil { + return err + } else if resp.Data == nil || resp.Data.NewToken == nil || resp.Data.NewToken.Token == "" { + return fmt.Errorf("unicloud api error: received empty token") + } + + c.serverlessJwtToken = resp.Data.NewToken.Token + c.serverlessJwtTokenExp = time.UnixMilli(resp.Data.NewToken.TokenExpired) + + return nil +} + +func (c *Client) ensureApiUserTokenExists() error { + if err := c.ensureServerlessJwtTokenExists(); err != nil { + return err + } + + c.apiUserTokenMtx.Lock() + defer c.apiUserTokenMtx.Unlock() + if c.apiUserToken != "" { + return nil + } + + resp := &getUserTokenResponse{} + if err := c.invokeServerlessWithResult( + uniConsoleEndpoint, uniConsoleClientSecret, uniConsoleAppId, uniConsoleSpaceId, + "uni-cloud-kernel", "", "user/getUserToken", nil, map[string]any{"isLogin": true}, + resp); err != nil { + return err + } else if resp.Data == nil || resp.Data.Data == nil || resp.Data.Data.Data == nil || resp.Data.Data.Data.Token == "" { + return fmt.Errorf("unicloud api error: received empty user token") + } + + c.apiUserToken = resp.Data.Data.Data.Token + + return nil +} + +func (c *Client) CreateDomainWithCert(req *CreateDomainWithCertRequest) (*CreateDomainWithCertResponse, error) { + if err := c.ensureApiUserTokenExists(); err != nil { + return nil, err + } + + resp := &CreateDomainWithCertResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/host/create-domain-with-cert", req, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/dcloud/unicloud/client.go b/internal/pkg/sdk3rd/dcloud/unicloud/client.go new file mode 100644 index 00000000..1e0f3728 --- /dev/null +++ b/internal/pkg/sdk3rd/dcloud/unicloud/client.go @@ -0,0 +1,257 @@ +package unicloud + +import ( + "crypto/hmac" + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "runtime" + "sort" + "strings" + "sync" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + username string + password string + + serverlessJwtToken string + serverlessJwtTokenExp time.Time + serverlessJwtTokenMtx sync.Mutex + + serverlessClient *resty.Client + + apiUserToken string + apiUserTokenMtx sync.Mutex + + apiClient *resty.Client +} + +const ( + uniIdentityEndpoint = "https://account.dcloud.net.cn/client" + uniIdentityClientSecret = "ba461799-fde8-429f-8cc4-4b6d306e2339" + uniIdentityAppId = "__UNI__uniid_server" + uniIdentitySpaceId = "uni-id-server" + uniConsoleEndpoint = "https://unicloud.dcloud.net.cn/client" + uniConsoleClientSecret = "4c1f7fbf-c732-42b0-ab10-4634a8bbe834" + uniConsoleAppId = "__UNI__unicloud_console" + uniConsoleSpaceId = "dc-6nfabcn6ada8d3dd" +) + +func NewClient(username, password string) *Client { + client := &Client{ + username: username, + password: password, + } + client.serverlessClient = resty.New() + client.apiClient = resty.New(). + SetBaseURL("https://unicloud-api.dcloud.net.cn/unicloud/api"). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + if client.apiUserToken != "" { + req.Header.Set("Token", client.apiUserToken) + } + + return nil + }) + + return client +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.serverlessClient.SetTimeout(timeout) + return c +} + +func (c *Client) generateSignature(params map[string]any, secret string) string { + keys := make([]string, 0, len(params)) + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) + + canonicalStr := "" + for i, k := range keys { + if i > 0 { + canonicalStr += "&" + } + canonicalStr += k + "=" + fmt.Sprintf("%v", params[k]) + } + + mac := hmac.New(md5.New, []byte(secret)) + mac.Write([]byte(canonicalStr)) + sign := mac.Sum(nil) + signHex := hex.EncodeToString(sign) + + return signHex +} + +func (c *Client) buildServerlessClientInfo(appId string) (_clientInfo map[string]any, _err error) { + return map[string]any{ + "PLATFORM": "web", + "OS": strings.ToUpper(runtime.GOOS), + "APPID": appId, + "DEVICEID": "certimate", + "LOCALE": "zh-Hans", + "osName": runtime.GOOS, + "appId": appId, + "appName": "uniCloud", + "deviceId": "certimate", + "deviceType": "pc", + "uniPlatform": "web", + "uniCompilerVersion": "4.45", + "uniRuntimeVersion": "4.45", + }, nil +} + +func (c *Client) buildServerlessPayloadInfo(appId, spaceId, target, method, action string, params, data interface{}) (map[string]any, error) { + clientInfo, err := c.buildServerlessClientInfo(appId) + if err != nil { + return nil, err + } + + functionArgsParams := make([]any, 0) + if params != nil { + functionArgsParams = append(functionArgsParams, params) + } + + functionArgs := map[string]any{ + "clientInfo": clientInfo, + "uniIdToken": c.serverlessJwtToken, + } + if method != "" { + functionArgs["method"] = method + functionArgs["params"] = make([]any, 0) + } + if action != "" { + type _obj struct{} + functionArgs["action"] = action + functionArgs["data"] = &_obj{} + } + if params != nil { + functionArgs["params"] = []any{params} + } + if data != nil { + functionArgs["data"] = data + } + + jsonb, err := json.Marshal(map[string]any{ + "functionTarget": target, + "functionArgs": functionArgs, + }) + if err != nil { + return nil, err + } + + payload := map[string]any{ + "method": "serverless.function.runtime.invoke", + "params": string(jsonb), + "spaceId": spaceId, + "timestamp": time.Now().UnixMilli(), + } + + return payload, nil +} + +func (c *Client) invokeServerless(endpoint, clientSecret, appId, spaceId, target, method, action string, params, data interface{}) (*resty.Response, error) { + if endpoint == "" { + return nil, fmt.Errorf("unicloud api error: endpoint cannot be empty") + } + + payload, err := c.buildServerlessPayloadInfo(appId, spaceId, target, method, action, params, data) + if err != nil { + return nil, fmt.Errorf("unicloud api error: failed to build request: %w", err) + } + + clientInfo, _ := c.buildServerlessClientInfo(appId) + clientInfoJsonb, _ := json.Marshal(clientInfo) + + sign := c.generateSignature(payload, clientSecret) + + req := c.serverlessClient.R(). + SetHeader("Origin", "https://unicloud.dcloud.net.cn"). + SetHeader("Referer", "https://unicloud.dcloud.net.cn"). + SetHeader("Content-Type", "application/json"). + SetHeader("X-Client-Info", string(clientInfoJsonb)). + SetHeader("X-Client-Token", c.serverlessJwtToken). + SetHeader("X-Serverless-Sign", sign). + SetBody(payload) + resp, err := req.Post(endpoint) + if err != nil { + return resp, fmt.Errorf("unicloud api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("unicloud api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return resp, nil +} + +func (c *Client) invokeServerlessWithResult(endpoint, clientSecret, appId, spaceId, target, method, action string, params, data interface{}, result BaseResponse) error { + resp, err := c.invokeServerless(endpoint, clientSecret, appId, spaceId, target, method, action, params, data) + 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("unicloud api error: failed to unmarshal response: %w", err) + } else if success := result.GetSuccess(); !success { + return fmt.Errorf("unicloud api error: code='%s', message='%s'", result.GetErrorCode(), result.GetErrorMessage()) + } + + return nil +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.apiClient.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("unicloud api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("unicloud 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("unicloud api error: failed to unmarshal response: %w", err) + } else if retcode := result.GetReturnCode(); retcode != 0 { + return fmt.Errorf("unicloud api error: ret='%d', desc='%s'", retcode, result.GetReturnDesc()) + } + + return nil +} diff --git a/internal/pkg/sdk3rd/dcloud/unicloud/models.go b/internal/pkg/sdk3rd/dcloud/unicloud/models.go new file mode 100644 index 00000000..05b02db6 --- /dev/null +++ b/internal/pkg/sdk3rd/dcloud/unicloud/models.go @@ -0,0 +1,103 @@ +package unicloud + +type BaseResponse interface { + GetSuccess() bool + GetErrorCode() string + GetErrorMessage() string + + GetReturnCode() int32 + GetReturnDesc() string +} + +type baseResponse struct { + Success *bool `json:"success,omitempty"` + Header *map[string]string `json:"header,omitempty"` + Error *struct { + Code string `json:"code"` + Message string `json:"message"` + } `json:"error,omitempty"` + + ReturnCode *int32 `json:"ret,omitempty"` + ReturnDesc *string `json:"desc,omitempty"` +} + +func (r *baseResponse) GetReturnCode() int32 { + if r.ReturnCode != nil { + return *r.ReturnCode + } + return 0 +} + +func (r *baseResponse) GetReturnDesc() string { + if r.ReturnDesc != nil { + return *r.ReturnDesc + } + return "" +} + +func (r *baseResponse) GetSuccess() bool { + if r.Success != nil { + return *r.Success + } + return false +} + +func (r *baseResponse) GetErrorCode() string { + if r.Error != nil { + return r.Error.Code + } + return "" +} + +func (r *baseResponse) GetErrorMessage() string { + if r.Error != nil { + return r.Error.Message + } + return "" +} + +type loginParams struct { + Email string `json:"email,omitempty"` + Mobile string `json:"mobile,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password"` +} + +type loginResponse struct { + baseResponse + Data *struct { + Code int32 `json:"errCode"` + UID string `json:"uid"` + NewToken *struct { + Token string `json:"token"` + TokenExpired int64 `json:"tokenExpired"` + } `json:"newToken,omitempty"` + } `json:"data,omitempty"` +} + +type getUserTokenResponse struct { + baseResponse + Data *struct { + Code int32 `json:"code"` + Data *struct { + Result int32 `json:"ret"` + Description string `json:"desc"` + Data *struct { + Email string `json:"email"` + Token string `json:"token"` + } `json:"data,omitempty"` + } `json:"data,omitempty"` + } `json:"data,omitempty"` +} + +type CreateDomainWithCertRequest struct { + Provider string `json:"provider"` + SpaceId string `json:"spaceId"` + Domain string `json:"domain"` + Cert string `json:"cert"` + Key string `json:"key"` +} + +type CreateDomainWithCertResponse struct { + baseResponse +} diff --git a/internal/pkg/sdk3rd/flexcdn/client.go b/internal/pkg/sdk3rd/flexcdn/client.go index beae469a..b478ffac 100644 --- a/internal/pkg/sdk3rd/flexcdn/client.go +++ b/internal/pkg/sdk3rd/flexcdn/client.go @@ -24,14 +24,14 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiRole, accessKeyId, accessKey string) *Client { +func NewClient(serverUrl, apiRole, accessKeyId, accessKey string) *Client { client := &Client{ apiRole: apiRole, accessKeyId: accessKeyId, accessKey: accessKey, } client.client = resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")). + SetBaseURL(strings.TrimRight(serverUrl, "/")). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { if client.accessToken != "" { req.Header.Set("X-Cloud-Access-Token", client.accessToken) diff --git a/internal/pkg/sdk3rd/goedge/client.go b/internal/pkg/sdk3rd/goedge/client.go index 3dc961e3..3cd4900a 100644 --- a/internal/pkg/sdk3rd/goedge/client.go +++ b/internal/pkg/sdk3rd/goedge/client.go @@ -24,14 +24,14 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiRole, accessKeyId, accessKey string) *Client { +func NewClient(serverUrl, apiRole, accessKeyId, accessKey string) *Client { client := &Client{ apiRole: apiRole, accessKeyId: accessKeyId, accessKey: accessKey, } client.client = resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")). + SetBaseURL(strings.TrimRight(serverUrl, "/")). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { if client.accessToken != "" { req.Header.Set("X-Edge-Access-Token", client.accessToken) diff --git a/internal/pkg/sdk3rd/lecdn/v3/client/client.go b/internal/pkg/sdk3rd/lecdn/v3/client/client.go index ad3a752e..3fa822eb 100644 --- a/internal/pkg/sdk3rd/lecdn/v3/client/client.go +++ b/internal/pkg/sdk3rd/lecdn/v3/client/client.go @@ -22,13 +22,13 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, username, password string) *Client { +func NewClient(serverUrl, username, password string) *Client { client := &Client{ username: username, password: password, } client.client = resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/") + "/prod-api"). + SetBaseURL(strings.TrimRight(serverUrl, "/") + "/prod-api"). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { if client.accessToken != "" { req.Header.Set("Authorization", "Bearer "+client.accessToken) diff --git a/internal/pkg/sdk3rd/lecdn/v3/master/client.go b/internal/pkg/sdk3rd/lecdn/v3/master/client.go index 2da0c0c4..ee1abaca 100644 --- a/internal/pkg/sdk3rd/lecdn/v3/master/client.go +++ b/internal/pkg/sdk3rd/lecdn/v3/master/client.go @@ -22,13 +22,13 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, username, password string) *Client { +func NewClient(serverUrl, username, password string) *Client { client := &Client{ username: username, password: password, } client.client = resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/") + "/prod-api"). + SetBaseURL(strings.TrimRight(serverUrl, "/") + "/prod-api"). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { if client.accessToken != "" { req.Header.Set("Authorization", "Bearer "+client.accessToken) diff --git a/internal/pkg/sdk3rd/netlify/models.go b/internal/pkg/sdk3rd/netlify/models.go index 196ee6cc..3ff2d216 100644 --- a/internal/pkg/sdk3rd/netlify/models.go +++ b/internal/pkg/sdk3rd/netlify/models.go @@ -26,8 +26,8 @@ func (r *baseResponse) GetMessage() string { type ProvisionSiteTLSCertificateParams struct { Certificate string `json:"certificate"` - CACertificates string `json:"key"` - Key string `json:"ca_certificates"` + CACertificates string `json:"ca_certificates"` + Key string `json:"key"` } type ProvisionSiteTLSCertificateResponse struct { diff --git a/internal/pkg/sdk3rd/ratpanel/client.go b/internal/pkg/sdk3rd/ratpanel/client.go index 47202a04..f1d20359 100644 --- a/internal/pkg/sdk3rd/ratpanel/client.go +++ b/internal/pkg/sdk3rd/ratpanel/client.go @@ -20,9 +20,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost string, accessTokenId int32, accessToken string) *Client { +func NewClient(serverUrl string, accessTokenId int32, accessToken string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")+"/api"). + SetBaseURL(strings.TrimRight(serverUrl, "/")+"/api"). SetHeader("Accept", "application/json"). SetHeader("Content-Type", "application/json"). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { diff --git a/internal/pkg/sdk3rd/safeline/client.go b/internal/pkg/sdk3rd/safeline/client.go index efcd3bd6..93d884a3 100644 --- a/internal/pkg/sdk3rd/safeline/client.go +++ b/internal/pkg/sdk3rd/safeline/client.go @@ -14,9 +14,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiToken string) *Client { +func NewClient(serverUrl, apiToken string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")). + SetBaseURL(strings.TrimRight(serverUrl, "/")). SetHeader("X-SLCE-API-TOKEN", apiToken) return &Client{ diff --git a/internal/repository/certificate.go b/internal/repository/certificate.go index 95bfd713..290d5f9f 100644 --- a/internal/repository/certificate.go +++ b/internal/repository/certificate.go @@ -77,6 +77,25 @@ func (r *CertificateRepository) GetByWorkflowNodeId(ctx context.Context, workflo return r.castRecordToModel(records[0]) } +func (r *CertificateRepository) GetByWorkflowRunId(ctx context.Context, workflowRunId string) (*domain.Certificate, error) { + records, err := app.GetApp().FindRecordsByFilter( + domain.CollectionNameCertificate, + "workflowRunId={:workflowRunId} && deleted=null", + "-created", + 1, 0, + dbx.Params{"workflowRunId": workflowRunId}, + ) + if err != nil { + return nil, err + } + + if len(records) == 0 { + return nil, domain.ErrRecordNotFound + } + + return r.castRecordToModel(records[0]) +} + func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Certificate) (*domain.Certificate, error) { collection, err := app.GetApp().FindCollectionByNameOrId(domain.CollectionNameCertificate) if err != nil { @@ -109,6 +128,7 @@ func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Ce record.Set("acmeAccountUrl", certificate.ACMEAccountUrl) record.Set("acmeCertUrl", certificate.ACMECertUrl) record.Set("acmeCertStableUrl", certificate.ACMECertStableUrl) + record.Set("acmeRenewed", certificate.ACMERenewed) record.Set("workflowId", certificate.WorkflowId) record.Set("workflowRunId", certificate.WorkflowRunId) record.Set("workflowNodeId", certificate.WorkflowNodeId) @@ -170,6 +190,7 @@ func (r *CertificateRepository) castRecordToModel(record *core.Record) (*domain. ACMEAccountUrl: record.GetString("acmeAccountUrl"), ACMECertUrl: record.GetString("acmeCertUrl"), ACMECertStableUrl: record.GetString("acmeCertStableUrl"), + ACMERenewed: record.GetBool("acmeRenewed"), WorkflowId: record.GetString("workflowId"), WorkflowRunId: record.GetString("workflowRunId"), WorkflowNodeId: record.GetString("workflowNodeId"), diff --git a/internal/workflow/node-processor/apply_node.go b/internal/workflow/node-processor/apply_node.go index ff8c573d..d38ece89 100644 --- a/internal/workflow/node-processor/apply_node.go +++ b/internal/workflow/node-processor/apply_node.go @@ -96,6 +96,15 @@ func (n *applyNode) Process(ctx context.Context) error { return err } + // 保存 ARI 记录 + if applyResult.ARIReplaced { + lastCertificate, _ := n.certRepo.GetByWorkflowRunId(ctx, lastOutput.RunId) + if lastCertificate != nil { + lastCertificate.ACMERenewed = true + n.certRepo.Save(ctx, lastCertificate) + } + } + n.logger.Info("apply completed") return nil @@ -134,7 +143,7 @@ func (n *applyNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workflo return false, "the configuration item 'KeyAlgorithm' changed" } - lastCertificate, _ := n.certRepo.GetByWorkflowNodeId(ctx, n.node.Id) + lastCertificate, _ := n.certRepo.GetByWorkflowRunId(ctx, lastOutput.RunId) if lastCertificate != nil { renewalInterval := time.Duration(currentNodeConfig.SkipBeforeExpiryDays) * time.Hour * 24 expirationTime := time.Until(lastCertificate.ExpireAt) diff --git a/internal/workflow/node-processor/processor.go b/internal/workflow/node-processor/processor.go index 4523b13a..f98aebae 100644 --- a/internal/workflow/node-processor/processor.go +++ b/internal/workflow/node-processor/processor.go @@ -34,6 +34,8 @@ func (n *nodeProcessor) SetLogger(logger *slog.Logger) { type certificateRepository interface { GetByWorkflowNodeId(ctx context.Context, workflowNodeId string) (*domain.Certificate, error) + GetByWorkflowRunId(ctx context.Context, workflowRunId string) (*domain.Certificate, error) + Save(ctx context.Context, certificate *domain.Certificate) (*domain.Certificate, error) } type workflowOutputRepository interface { diff --git a/internal/workflow/node-processor/upload_node.go b/internal/workflow/node-processor/upload_node.go index 2da19eed..a1878a41 100644 --- a/internal/workflow/node-processor/upload_node.go +++ b/internal/workflow/node-processor/upload_node.go @@ -83,7 +83,7 @@ func (n *uploadNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workfl return false, "the configuration item 'PrivateKey' changed" } - lastCertificate, _ := n.certRepo.GetByWorkflowNodeId(ctx, n.node.Id) + lastCertificate, _ := n.certRepo.GetByWorkflowRunId(ctx, lastOutput.RunId) if lastCertificate != nil { return true, "the certificate has already been uploaded" } diff --git a/migrations/1739462400_collections_snapshot.go b/migrations/1739462400_collections_snapshot.go index 25564526..2f78e7b9 100644 --- a/migrations/1739462400_collections_snapshot.go +++ b/migrations/1739462400_collections_snapshot.go @@ -2,7 +2,6 @@ package migrations import ( x509 "crypto/x509" - "log/slog" "strings" "github.com/pocketbase/pocketbase/core" @@ -12,7 +11,8 @@ import ( func init() { m.Register(func(app core.App) error { - slog.Info("[CERTIMATE] migration: ready ...") + tracer := NewTracer("to v0.3") + tracer.Printf("go ...") // backup collection records collectionRecords := make([]*core.Record, 0) @@ -30,7 +30,7 @@ func init() { } collectionRecords = append(collectionRecords, records...) - slog.Info("[CERTIMATE] migration: collection '" + collection.Name + "' backed up") + tracer.Printf("collection '%s' backed up", collection.Name) if collection.Name == "access" { collection.Fields.RemoveByName("usage") @@ -107,7 +107,7 @@ func init() { { app.Delete(collection) - slog.Info("[CERTIMATE] migration: collection '" + collection.Name + "' truncated") + tracer.Printf("collection '%s' truncated", collection.Name) } } } @@ -1729,7 +1729,7 @@ func init() { return err } - slog.Info("[CERTIMATE] migration: collections imported") + tracer.Printf("collections imported") // restore records for _, record := range collectionRecords { @@ -1795,12 +1795,11 @@ func init() { return err } - slog.Info("[CERTIMATE] migration: collection '" + record.Collection().Name + "' record #" + record.Id + " updated") + tracer.Printf("record #%s in collection '%s' updated", record.Id, record.Collection().Name) } } - slog.Info("[CERTIMATE] migration: done") - + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1740050400_upgrade.go b/migrations/1740050400_upgrade.go index 93bc3f33..261169f8 100644 --- a/migrations/1740050400_upgrade.go +++ b/migrations/1740050400_upgrade.go @@ -7,14 +7,17 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1740050400") + tracer.Printf("go ...") + // update collection `certificate` { - certimateCollection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") + collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") if err != nil { return err } - if err := certimateCollection.Fields.AddMarshaledJSONAt(4, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(4, []byte(`{ "autogeneratePattern": "", "hidden": false, "id": "plmambpz", @@ -31,7 +34,7 @@ func init() { return err } - if err := certimateCollection.Fields.AddMarshaledJSONAt(5, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(5, []byte(`{ "autogeneratePattern": "", "hidden": false, "id": "49qvwxcg", @@ -48,7 +51,7 @@ func init() { return err } - if err := certimateCollection.Fields.AddMarshaledJSONAt(7, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(7, []byte(`{ "autogeneratePattern": "", "hidden": false, "id": "agt7n5bb", @@ -65,19 +68,21 @@ func init() { return err } - if err := app.Save(certimateCollection); err != nil { + if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `workflow` { - workflowCollection, err := app.FindCollectionByNameOrId("tovyif5ax6j62ur") + collection, err := app.FindCollectionByNameOrId("tovyif5ax6j62ur") if err != nil { return err } - if err := workflowCollection.Fields.AddMarshaledJSONAt(6, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(6, []byte(`{ "hidden": false, "id": "awlphkfe", "maxSize": 5000000, @@ -90,7 +95,7 @@ func init() { return err } - if err := workflowCollection.Fields.AddMarshaledJSONAt(7, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(7, []byte(`{ "hidden": false, "id": "g9ohkk5o", "maxSize": 5000000, @@ -103,19 +108,21 @@ func init() { return err } - if err := app.Save(workflowCollection); err != nil { + if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `workflow_output` { - workflowOutputCollection, err := app.FindCollectionByNameOrId("bqnxb95f2cooowp") + collection, err := app.FindCollectionByNameOrId("bqnxb95f2cooowp") if err != nil { return err } - if err := workflowOutputCollection.Fields.AddMarshaledJSONAt(4, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(4, []byte(`{ "hidden": false, "id": "c2rm9omj", "maxSize": 5000000, @@ -128,11 +135,14 @@ func init() { return err } - if err := app.Save(workflowOutputCollection); err != nil { + if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1742209200_upgrade.go b/migrations/1742209200_upgrade.go index 5cda0c35..1bc0d482 100644 --- a/migrations/1742209200_upgrade.go +++ b/migrations/1742209200_upgrade.go @@ -11,6 +11,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1742209200") + tracer.Printf("go ...") + // create collection `workflow_logs` { jsonData := `{ @@ -167,6 +170,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' created", collection.Name) } // migrate data @@ -215,6 +220,8 @@ func init() { if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } } @@ -243,6 +250,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // migrate data @@ -321,6 +330,8 @@ func init() { if err := app.Save(workflowRun); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", workflowRun.Id, workflowRun.Collection().Name) } } @@ -336,8 +347,11 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1742392800_upgrade.go b/migrations/1742392800_upgrade.go index a06bdc75..4416936a 100644 --- a/migrations/1742392800_upgrade.go +++ b/migrations/1742392800_upgrade.go @@ -7,74 +7,86 @@ import ( func init() { m.Register(func(app core.App) error { - collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") - if err != nil { - return err + tracer := NewTracer("(v0.3)1742392800") + tracer.Printf("go ...") + + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "hwy7m03o", + "maxSelect": 1, + "name": "provider", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "1panel", + "acmehttpreq", + "akamai", + "aliyun", + "aws", + "azure", + "baiducloud", + "baishan", + "baotapanel", + "byteplus", + "cachefly", + "cdnfly", + "cloudflare", + "cloudns", + "cmcccloud", + "ctcccloud", + "cucccloud", + "dnsla", + "dogecloud", + "dynv6", + "edgio", + "fastly", + "gname", + "gcore", + "godaddy", + "goedge", + "huaweicloud", + "jdcloud", + "k8s", + "local", + "namecheap", + "namedotcom", + "namesilo", + "ns1", + "powerdns", + "qiniu", + "qingcloud", + "rainyun", + "safeline", + "ssh", + "tencentcloud", + "ucloud", + "upyun", + "volcengine", + "webhook", + "westcn" + ] + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + + tracer.Printf("collection '%s' updated", collection.Name) } - // update field - if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ - "hidden": false, - "id": "hwy7m03o", - "maxSelect": 1, - "name": "provider", - "presentable": false, - "required": false, - "system": false, - "type": "select", - "values": [ - "1panel", - "acmehttpreq", - "akamai", - "aliyun", - "aws", - "azure", - "baiducloud", - "baishan", - "baotapanel", - "byteplus", - "cachefly", - "cdnfly", - "cloudflare", - "cloudns", - "cmcccloud", - "ctcccloud", - "cucccloud", - "dnsla", - "dogecloud", - "dynv6", - "edgio", - "fastly", - "gname", - "gcore", - "godaddy", - "goedge", - "huaweicloud", - "jdcloud", - "k8s", - "local", - "namecheap", - "namedotcom", - "namesilo", - "ns1", - "powerdns", - "qiniu", - "qingcloud", - "rainyun", - "safeline", - "ssh", - "tencentcloud", - "ucloud", - "upyun", - "volcengine", - "webhook", - "westcn" - ] - }`)); err != nil { - return err - } - - return app.Save(collection) + tracer.Printf("done") + return nil }, func(app core.App) error { return nil }) diff --git a/migrations/1742644800_upgrade.go b/migrations/1742644800_upgrade.go index b28634a1..228a70fc 100644 --- a/migrations/1742644800_upgrade.go +++ b/migrations/1742644800_upgrade.go @@ -7,6 +7,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1742644800") + tracer.Printf("go ...") + // update collection `workflow_run` { collection, err := app.FindCollectionByNameOrId("qjp8lygssgwyqyz") @@ -35,6 +38,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `workflow_output` @@ -61,6 +66,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `workflow_logs` @@ -105,6 +112,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `access` @@ -182,8 +191,11 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1743264000_upgrade.go b/migrations/1743264000_upgrade.go index 75f98b30..22738be2 100644 --- a/migrations/1743264000_upgrade.go +++ b/migrations/1743264000_upgrade.go @@ -7,6 +7,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1743264000") + tracer.Printf("go ...") + // update collection `settings` { collection, err := app.FindCollectionByNameOrId("dy6ccjb60spfy6p") @@ -52,6 +55,8 @@ func init() { if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } @@ -62,7 +67,6 @@ func init() { return err } - // update field if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ "hidden": false, "id": "hwy7m03o", @@ -136,6 +140,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `acme_accounts` @@ -163,9 +169,12 @@ func init() { if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1744192800_upgrade.go b/migrations/1744192800_upgrade.go index 83e83ee6..eb4c6d06 100644 --- a/migrations/1744192800_upgrade.go +++ b/migrations/1744192800_upgrade.go @@ -7,84 +7,96 @@ import ( func init() { m.Register(func(app core.App) error { - collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") - if err != nil { - return err + tracer := NewTracer("(v0.3)1744192800") + tracer.Printf("go ...") + + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "hwy7m03o", + "maxSelect": 1, + "name": "provider", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "1panel", + "acmehttpreq", + "akamai", + "aliyun", + "aws", + "azure", + "baiducloud", + "baishan", + "baotapanel", + "byteplus", + "buypass", + "cachefly", + "cdnfly", + "cloudflare", + "cloudns", + "cmcccloud", + "ctcccloud", + "cucccloud", + "desec", + "dnsla", + "dogecloud", + "dynv6", + "edgio", + "fastly", + "gname", + "gcore", + "godaddy", + "goedge", + "googletrustservices", + "huaweicloud", + "jdcloud", + "k8s", + "letsencrypt", + "letsencryptstaging", + "local", + "namecheap", + "namedotcom", + "namesilo", + "ns1", + "porkbun", + "powerdns", + "qiniu", + "qingcloud", + "rainyun", + "safeline", + "ssh", + "sslcom", + "tencentcloud", + "ucloud", + "upyun", + "vercel", + "volcengine", + "wangsu", + "webhook", + "westcn", + "zerossl" + ] + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return nil + } + + tracer.Printf("collection '%s' updated", collection.Name) } - // update field - if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ - "hidden": false, - "id": "hwy7m03o", - "maxSelect": 1, - "name": "provider", - "presentable": false, - "required": false, - "system": false, - "type": "select", - "values": [ - "1panel", - "acmehttpreq", - "akamai", - "aliyun", - "aws", - "azure", - "baiducloud", - "baishan", - "baotapanel", - "byteplus", - "buypass", - "cachefly", - "cdnfly", - "cloudflare", - "cloudns", - "cmcccloud", - "ctcccloud", - "cucccloud", - "desec", - "dnsla", - "dogecloud", - "dynv6", - "edgio", - "fastly", - "gname", - "gcore", - "godaddy", - "goedge", - "googletrustservices", - "huaweicloud", - "jdcloud", - "k8s", - "letsencrypt", - "letsencryptstaging", - "local", - "namecheap", - "namedotcom", - "namesilo", - "ns1", - "porkbun", - "powerdns", - "qiniu", - "qingcloud", - "rainyun", - "safeline", - "ssh", - "sslcom", - "tencentcloud", - "ucloud", - "upyun", - "vercel", - "volcengine", - "wangsu", - "webhook", - "westcn", - "zerossl" - ] - }`)); err != nil { - return err - } - - return app.Save(collection) + tracer.Printf("done") + return nil }, func(app core.App) error { return nil }) diff --git a/migrations/1744459000_upgrade.go b/migrations/1744459000_upgrade.go index 4b2bbba9..82ecbcf6 100644 --- a/migrations/1744459000_upgrade.go +++ b/migrations/1744459000_upgrade.go @@ -7,85 +7,97 @@ import ( func init() { m.Register(func(app core.App) error { - collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") - if err != nil { - return err + tracer := NewTracer("(v0.3)1744459000") + tracer.Printf("go ...") + + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "hwy7m03o", + "maxSelect": 1, + "name": "provider", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "1panel", + "acmehttpreq", + "akamai", + "aliyun", + "aws", + "azure", + "baiducloud", + "baishan", + "baotapanel", + "bunny", + "byteplus", + "buypass", + "cachefly", + "cdnfly", + "cloudflare", + "cloudns", + "cmcccloud", + "ctcccloud", + "cucccloud", + "desec", + "dnsla", + "dogecloud", + "dynv6", + "edgio", + "fastly", + "gname", + "gcore", + "godaddy", + "goedge", + "googletrustservices", + "huaweicloud", + "jdcloud", + "k8s", + "letsencrypt", + "letsencryptstaging", + "local", + "namecheap", + "namedotcom", + "namesilo", + "ns1", + "porkbun", + "powerdns", + "qiniu", + "qingcloud", + "rainyun", + "safeline", + "ssh", + "sslcom", + "tencentcloud", + "ucloud", + "upyun", + "vercel", + "volcengine", + "wangsu", + "webhook", + "westcn", + "zerossl" + ] + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + + tracer.Printf("collection '%s' updated", collection.Name) } - // update field - if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ - "hidden": false, - "id": "hwy7m03o", - "maxSelect": 1, - "name": "provider", - "presentable": false, - "required": false, - "system": false, - "type": "select", - "values": [ - "1panel", - "acmehttpreq", - "akamai", - "aliyun", - "aws", - "azure", - "baiducloud", - "baishan", - "baotapanel", - "bunny", - "byteplus", - "buypass", - "cachefly", - "cdnfly", - "cloudflare", - "cloudns", - "cmcccloud", - "ctcccloud", - "cucccloud", - "desec", - "dnsla", - "dogecloud", - "dynv6", - "edgio", - "fastly", - "gname", - "gcore", - "godaddy", - "goedge", - "googletrustservices", - "huaweicloud", - "jdcloud", - "k8s", - "letsencrypt", - "letsencryptstaging", - "local", - "namecheap", - "namedotcom", - "namesilo", - "ns1", - "porkbun", - "powerdns", - "qiniu", - "qingcloud", - "rainyun", - "safeline", - "ssh", - "sslcom", - "tencentcloud", - "ucloud", - "upyun", - "vercel", - "volcengine", - "wangsu", - "webhook", - "westcn", - "zerossl" - ] - }`)); err != nil { - return err - } - - return app.Save(collection) + tracer.Printf("done") + return nil }, func(app core.App) error { return nil }) diff --git a/migrations/1745308800_upgrade.go b/migrations/1745308800_upgrade.go index 989664fb..68d025fd 100644 --- a/migrations/1745308800_upgrade.go +++ b/migrations/1745308800_upgrade.go @@ -9,84 +9,93 @@ import ( func init() { m.Register(func(app core.App) error { - collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") - if err != nil { - return err - } - - // add temp field `providerTmp` - if err := collection.Fields.AddMarshaledJSONAt(3, []byte(`{ - "autogeneratePattern": "", - "hidden": false, - "id": "text2024822322", - "max": 0, - "min": 0, - "name": "providerTmp", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }`)); err != nil { - return err - } - if err := app.Save(collection); err != nil { - return err - } - - // copy `provider` to `providerTmp` - if _, err := app.DB().NewQuery("UPDATE access SET providerTmp = provider").Execute(); err != nil { - return err - } - - // remove old field `provider` - collection.Fields.RemoveById("hwy7m03o") - if err := json.Unmarshal([]byte(`{ - "indexes": [ - "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)" - ] - }`), &collection); err != nil { - return err - } - if err := app.Save(collection); err != nil { - return err - } - - // rename field `providerTmp` to `provider` - if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ - "autogeneratePattern": "", - "hidden": false, - "id": "text2024822322", - "max": 0, - "min": 0, - "name": "provider", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }`)); err != nil { - return err - } - if err := app.Save(collection); err != nil { - return err - } - - // rebuild indexes - if err := json.Unmarshal([]byte(`{ - "indexes": [ - "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)", - "CREATE INDEX `+"`"+`idx_frh0JT1Aqx`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`provider`+"`"+`)" - ] - }`), &collection); err != nil { - return err - } - if err := app.Save(collection); err != nil { - return err + tracer := NewTracer("(v0.3)1745308800") + tracer.Printf("go ...") + + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // add temp field `providerTmp` + if err := collection.Fields.AddMarshaledJSONAt(3, []byte(`{ + "autogeneratePattern": "", + "hidden": false, + "id": "text2024822322", + "max": 0, + "min": 0, + "name": "providerTmp", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + // copy `provider` to `providerTmp` + if _, err := app.DB().NewQuery("UPDATE access SET providerTmp = provider").Execute(); err != nil { + return err + } + + // remove old field `provider` + collection.Fields.RemoveById("hwy7m03o") + if err := json.Unmarshal([]byte(`{ + "indexes": [ + "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)" + ] + }`), &collection); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + // rename field `providerTmp` to `provider` + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "autogeneratePattern": "", + "hidden": false, + "id": "text2024822322", + "max": 0, + "min": 0, + "name": "provider", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + // rebuild indexes + if err := json.Unmarshal([]byte(`{ + "indexes": [ + "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)", + "CREATE INDEX `+"`"+`idx_frh0JT1Aqx`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`provider`+"`"+`)" + ] + }`), &collection); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + tracer.Printf("collection '%s' updated", collection.Name) } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1745726400_upgrade.go b/migrations/1745726400_upgrade.go index e4449f36..0513dac4 100644 --- a/migrations/1745726400_upgrade.go +++ b/migrations/1745726400_upgrade.go @@ -7,6 +7,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1745726400") + tracer.Printf("go ...") + // update collection `access` { collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") @@ -34,53 +37,62 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // migrate data { - accesses, err := app.FindAllRecords("access") + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") if err != nil { return err } - for _, access := range accesses { + records, err := app.FindAllRecords(collection) + if err != nil { + return err + } + + for _, record := range records { changed := false - if access.GetString("provider") == "buypass" { - access.Set("reserve", "ca") + if record.GetString("provider") == "buypass" { + record.Set("reserve", "ca") changed = true - } else if access.GetString("provider") == "googletrustservices" { - access.Set("reserve", "ca") + } else if record.GetString("provider") == "googletrustservices" { + record.Set("reserve", "ca") changed = true - } else if access.GetString("provider") == "sslcom" { - access.Set("reserve", "ca") + } else if record.GetString("provider") == "sslcom" { + record.Set("reserve", "ca") changed = true - } else if access.GetString("provider") == "zerossl" { - access.Set("reserve", "ca") + } else if record.GetString("provider") == "zerossl" { + record.Set("reserve", "ca") changed = true } - if access.GetString("provider") == "webhook" { + if record.GetString("provider") == "webhook" { config := make(map[string]any) - if err := access.UnmarshalJSONField("config", &config); err != nil { + if err := record.UnmarshalJSONField("config", &config); err != nil { return err } config["method"] = "POST" config["headers"] = "Content-Type: application/json" - access.Set("config", config) + record.Set("config", config) changed = true } if changed { - err = app.Save(access) - if err != nil { + if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1747314000_upgrade.go b/migrations/1747314000_upgrade.go index 19a25bb2..2c3ecdc4 100644 --- a/migrations/1747314000_upgrade.go +++ b/migrations/1747314000_upgrade.go @@ -7,36 +7,46 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1747314000") + tracer.Printf("go ...") + // migrate data { - accesses, err := app.FindAllRecords("access") + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") if err != nil { return err } - for _, access := range accesses { + records, err := app.FindAllRecords(collection) + if err != nil { + return err + } + + for _, record := range records { changed := false - if access.GetString("provider") == "goedge" { + if record.GetString("provider") == "goedge" { config := make(map[string]any) - if err := access.UnmarshalJSONField("config", &config); err != nil { + if err := record.UnmarshalJSONField("config", &config); err != nil { return err } config["apiRole"] = "user" - access.Set("config", config) + record.Set("config", config) changed = true } if changed { - err = app.Save(access) - if err != nil { + if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1747389600_upgrade.go b/migrations/1747389600_upgrade.go index a145679a..fabc562c 100644 --- a/migrations/1747389600_upgrade.go +++ b/migrations/1747389600_upgrade.go @@ -7,6 +7,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1747389600") + tracer.Printf("go ...") + // update collection `certificate` { collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") @@ -34,38 +37,47 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // migrate data { - accesses, err := app.FindAllRecords("access") + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") if err != nil { return err } - for _, access := range accesses { + records, err := app.FindAllRecords(collection) + if err != nil { + return err + } + + for _, record := range records { changed := false - if access.GetString("provider") == "1panel" { + if record.GetString("provider") == "1panel" { config := make(map[string]any) - if err := access.UnmarshalJSONField("config", &config); err != nil { + if err := record.UnmarshalJSONField("config", &config); err != nil { return err } config["apiVersion"] = "v1" - access.Set("config", config) + record.Set("config", config) changed = true } if changed { - err = app.Save(access) - if err != nil { + if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1748178000_upgrade.go b/migrations/1748178000_upgrade.go new file mode 100644 index 00000000..44783448 --- /dev/null +++ b/migrations/1748178000_upgrade.go @@ -0,0 +1,72 @@ +package migrations + +import ( + "slices" + + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1748178000") + tracer.Printf("go ...") + + // migrate data + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + records, err := app.FindAllRecords(collection) + if err != nil { + return err + } + + providersToUpdate := []string{ + "1panel", + "baotapanel", + "baotawaf", + "cdnfly", + "flexcdn", + "goedge", + "lecdn", + "powerdns", + "proxmoxve", + "ratpanel", + "safeline", + } + for _, record := range records { + changed := false + + if slices.Contains(providersToUpdate, record.GetString("provider")) { + config := make(map[string]any) + if err := record.UnmarshalJSONField("config", &config); err != nil { + return err + } + + if config["apiUrl"] != nil { + config["serverUrl"] = config["apiUrl"] + delete(config, "apiUrl") + record.Set("config", config) + changed = true + } + } + + if changed { + if err := app.Save(record); err != nil { + return err + } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) + } + } + } + + tracer.Printf("done") + return nil + }, func(app core.App) error { + return nil + }) +} diff --git a/migrations/1748228400_upgrade.go b/migrations/1748228400_upgrade.go new file mode 100644 index 00000000..afdfd850 --- /dev/null +++ b/migrations/1748228400_upgrade.go @@ -0,0 +1,45 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1748228400") + tracer.Printf("go ...") + + // update collection `certificate` + { + collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") + if err != nil { + return err + } + + // add field + if err := collection.Fields.AddMarshaledJSONAt(14, []byte(`{ + "hidden": false, + "id": "bool810050391", + "name": "acmeRenewed", + "presentable": false, + "required": false, + "system": false, + "type": "bool" + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + + tracer.Printf("collection '%s' updated", collection.Name) + } + + tracer.Printf("done") + return nil + }, func(app core.App) error { + return nil + }) +} diff --git a/migrations/tracer.go b/migrations/tracer.go new file mode 100644 index 00000000..04f58e54 --- /dev/null +++ b/migrations/tracer.go @@ -0,0 +1,22 @@ +package migrations + +import ( + "fmt" + "log/slog" +) + +type Tracer struct { + logger *slog.Logger + flag string +} + +func NewTracer(flag string) *Tracer { + return &Tracer{ + logger: slog.Default(), + flag: flag, + } +} + +func (l *Tracer) Printf(format string, args ...any) { + l.logger.Info("[CERTIMATE] migration " + l.flag + ": " + fmt.Sprintf(format, args...)) +} diff --git a/ui/package-lock.json b/ui/package-lock.json index 44a4d2b6..e1bbb340 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -17,27 +17,27 @@ "@uiw/codemirror-extensions-basic-setup": "^4.23.12", "@uiw/codemirror-theme-vscode": "^4.23.12", "@uiw/react-codemirror": "^4.23.12", - "ahooks": "^3.8.4", - "antd": "^5.25.1", + "ahooks": "^3.8.5", + "antd": "^5.25.3", "antd-zod": "^6.1.0", "clsx": "^2.1.1", "cron-parser": "^5.2.0", "file-saver": "^2.0.5", - "i18next": "^25.1.2", + "i18next": "^25.2.1", "i18next-browser-languagedetector": "^8.1.0", "immer": "^10.1.1", - "lucide-react": "^0.509.0", + "lucide-react": "^0.511.0", "nanoid": "^5.1.5", "pocketbase": "^0.26.0", "radash": "^12.1.0", "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", - "react-i18next": "^15.5.1", - "react-router-dom": "^7.6.0", + "react-i18next": "^15.5.2", + "react-router-dom": "^7.6.1", "tailwind-merge": "^2.6.0", - "zod": "^3.24.4", - "zustand": "^5.0.4" + "zod": "^3.25.28", + "zustand": "^5.0.5" }, "devDependencies": { "@types/file-saver": "^2.0.7", @@ -46,10 +46,10 @@ "@types/react": "^18.3.12", "@types/react-copy-to-clipboard": "^5.0.7", "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^8.32.0", - "@typescript-eslint/parser": "^8.32.0", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", "@vitejs/plugin-legacy": "^6.1.1", - "@vitejs/plugin-react": "^4.4.1", + "@vitejs/plugin-react": "^4.5.0", "autoprefixer": "^10.4.21", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.5", @@ -94,9 +94,9 @@ } }, "node_modules/@ant-design/colors": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-7.2.0.tgz", - "integrity": "sha512-bjTObSnZ9C/O8MB/B4OUtd/q9COomuJAR2SYfhxLyHvCKn4EKwCN3e+fWGMo7H5InAyV0wL17jdE9ALrdOW/6A==", + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-7.2.1.tgz", + "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==", "dependencies": { "@ant-design/fast-color": "^2.0.6" } @@ -2047,12 +2047,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", "engines": { "node": ">=6.9.0" } @@ -3225,6 +3222,12 @@ "react-dom": ">=18.0.0" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.9", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.9.tgz", + "integrity": "sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==", + "dev": true + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.40.0", "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", @@ -3613,18 +3616,18 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.0.tgz", - "integrity": "sha512-/jU9ettcntkBFmWUzzGgsClEi2ZFiikMX5eEQsmxIAWMOn4H3D4rvHssstmAHGVvrYnaMqdWWWg0b5M6IN/MTQ==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.32.0", - "@typescript-eslint/type-utils": "8.32.0", - "@typescript-eslint/utils": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, @@ -3641,16 +3644,25 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.32.0.tgz", - "integrity": "sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.32.0", - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/typescript-estree": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4" }, "engines": { @@ -3666,13 +3678,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.32.0.tgz", - "integrity": "sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0" + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3683,13 +3695,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.32.0.tgz", - "integrity": "sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.32.0", - "@typescript-eslint/utils": "8.32.0", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3706,9 +3718,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.32.0.tgz", - "integrity": "sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3719,13 +3731,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.0.tgz", - "integrity": "sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3745,15 +3757,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.32.0.tgz", - "integrity": "sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.32.0", - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/typescript-estree": "8.32.0" + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3768,12 +3780,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.0.tgz", - "integrity": "sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.32.0", + "@typescript-eslint/types": "8.32.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3922,14 +3934,15 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.4.1", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", - "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.5.0.tgz", + "integrity": "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==", "dev": true, "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@rolldown/pluginutils": "1.0.0-beta.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, @@ -3970,9 +3983,9 @@ } }, "node_modules/ahooks": { - "version": "3.8.4", - "resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.8.4.tgz", - "integrity": "sha512-39wDEw2ZHvypaT14EpMMk4AzosHWt0z9bulY0BeDsvc9PqJEV+Kjh/4TZfftSsotBMq52iYIOFPd3PR56e0ZJg==", + "version": "3.8.5", + "resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.8.5.tgz", + "integrity": "sha512-Y+MLoJpBXVdjsnnBjE5rOSPkQ4DK+8i5aPDzLJdIOsCpo/fiAeXcBY1Y7oWgtOK0TpOz0gFa/XcyO1UGdoqLcw==", "dependencies": { "@babel/runtime": "^7.21.0", "dayjs": "^1.9.1", @@ -3988,7 +4001,7 @@ "node": ">=8.0.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/ajv": { @@ -4017,11 +4030,11 @@ } }, "node_modules/antd": { - "version": "5.25.1", - "resolved": "https://registry.npmmirror.com/antd/-/antd-5.25.1.tgz", - "integrity": "sha512-4KC7KuPCjr0z3Vuw9DsF+ceqJaPLbuUI3lOX1sY8ix25ceamp+P8yxOmk3Y2JHCD2ZAhq+5IQ/DTJRN2adWYKQ==", + "version": "5.25.3", + "resolved": "https://registry.npmmirror.com/antd/-/antd-5.25.3.tgz", + "integrity": "sha512-tBBcAFRjmWM3sitxrL/FEbQL+MTQntYY5bGa5c1ZZZHXWCynkhS3Ch/gy25mGMUY1M/9Uw3pH029v/RGht1x3w==", "dependencies": { - "@ant-design/colors": "^7.2.0", + "@ant-design/colors": "^7.2.1", "@ant-design/cssinjs": "^1.23.0", "@ant-design/cssinjs-utils": "^1.1.3", "@ant-design/fast-color": "^2.0.6", @@ -4056,11 +4069,11 @@ "rc-rate": "~2.13.1", "rc-resize-observer": "^1.4.3", "rc-segmented": "~2.7.0", - "rc-select": "~14.16.7", + "rc-select": "~14.16.8", "rc-slider": "~11.1.8", "rc-steps": "~6.0.1", "rc-switch": "~4.1.0", - "rc-table": "~7.50.4", + "rc-table": "~7.50.5", "rc-tabs": "~15.6.1", "rc-textarea": "~1.10.0", "rc-tooltip": "~6.4.0", @@ -6081,9 +6094,9 @@ } }, "node_modules/i18next": { - "version": "25.1.2", - "resolved": "https://registry.npmmirror.com/i18next/-/i18next-25.1.2.tgz", - "integrity": "sha512-SP63m8LzdjkrAjruH7SCI3ndPSgjt4/wX7ouUUOzCW/eY+HzlIo19IQSfYA9X3qRiRP1SYtaTsg/Oz/PGsfD8w==", + "version": "25.2.1", + "resolved": "https://registry.npmmirror.com/i18next/-/i18next-25.2.1.tgz", + "integrity": "sha512-+UoXK5wh+VlE1Zy5p6MjcvctHXAhRwQKCxiJD8noKZzIXmnAX8gdHX5fLPA3MEVxEN4vbZkQFy8N0LyD9tUqPw==", "funding": [ { "type": "individual", @@ -6099,7 +6112,7 @@ } ], "dependencies": { - "@babel/runtime": "^7.26.10" + "@babel/runtime": "^7.27.1" }, "peerDependencies": { "typescript": "^5" @@ -6797,9 +6810,9 @@ } }, "node_modules/lucide-react": { - "version": "0.509.0", - "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.509.0.tgz", - "integrity": "sha512-xCJHn6Uh5qF6PGml25vveCTrHJZcqS1G1MVzWZK54ZQsOiCVJk4fwY3oyo5EXS2S+aqvTpWYIfJN+PesJ0quxg==", + "version": "0.511.0", + "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.511.0.tgz", + "integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -7874,9 +7887,9 @@ } }, "node_modules/rc-select": { - "version": "14.16.7", - "resolved": "https://registry.npmmirror.com/rc-select/-/rc-select-14.16.7.tgz", - "integrity": "sha512-lT9kO5gFHQdJzu9a0btcOtNaJHkhenSl8H5mcpgXN9VIMXP59rnkpbdHmPrteixWs1D5zFOTyoTYX3b7joADIQ==", + "version": "14.16.8", + "resolved": "https://registry.npmmirror.com/rc-select/-/rc-select-14.16.8.tgz", + "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/trigger": "^2.1.1", @@ -7943,9 +7956,9 @@ } }, "node_modules/rc-table": { - "version": "7.50.4", - "resolved": "https://registry.npmmirror.com/rc-table/-/rc-table-7.50.4.tgz", - "integrity": "sha512-Y+YuncnQqoS5e7yHvfvlv8BmCvwDYDX/2VixTBEhkMDk9itS9aBINp4nhzXFKiBP/frG4w0pS9d9Rgisl0T1Bw==", + "version": "7.50.5", + "resolved": "https://registry.npmmirror.com/rc-table/-/rc-table-7.50.5.tgz", + "integrity": "sha512-FDZu8aolhSYd3v9KOc3lZOVAU77wmRRu44R0Wfb8Oj1dXRUsloFaXMSl6f7yuWZUxArJTli7k8TEOX2mvhDl4A==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/context": "^1.4.0", @@ -8135,9 +8148,9 @@ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, "node_modules/react-i18next": { - "version": "15.5.1", - "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.5.1.tgz", - "integrity": "sha512-C8RZ7N7H0L+flitiX6ASjq9p5puVJU1Z8VyL3OgM/QOMRf40BMZX+5TkpxzZVcTmOLPX5zlti4InEX5pFyiVeA==", + "version": "15.5.2", + "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.5.2.tgz", + "integrity": "sha512-ePODyXgmZQAOYTbZXQn5rRsSBu3Gszo69jxW6aKmlSgxKAI1fOhDwSu6bT4EKHciWPKQ7v7lPrjeiadR6Gi+1A==", "dependencies": { "@babel/runtime": "^7.25.0", "html-parse-stringify": "^3.0.1" @@ -8179,9 +8192,9 @@ } }, "node_modules/react-router": { - "version": "7.6.0", - "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.6.0.tgz", - "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", + "version": "7.6.1", + "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.6.1.tgz", + "integrity": "sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==", "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" @@ -8200,11 +8213,11 @@ } }, "node_modules/react-router-dom": { - "version": "7.6.0", - "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.6.0.tgz", - "integrity": "sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==", + "version": "7.6.1", + "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.6.1.tgz", + "integrity": "sha512-vxU7ei//UfPYQ3iZvHuO1D/5fX3/JOqhNTbRR+WjSBWxf9bIvpWK+ftjmdfJHzPOuMQKe2fiEdG+dZX6E8uUpA==", "dependencies": { - "react-router": "7.6.0" + "react-router": "7.6.1" }, "engines": { "node": ">=20.0.0" @@ -8286,7 +8299,8 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true }, "node_modules/regenerator-transform": { "version": "0.15.2", @@ -9926,17 +9940,17 @@ } }, "node_modules/zod": { - "version": "3.24.4", - "resolved": "https://registry.npmmirror.com/zod/-/zod-3.24.4.tgz", - "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", + "version": "3.25.28", + "resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.28.tgz", + "integrity": "sha512-/nt/67WYKnr5by3YS7LroZJbtcCBurDKKPBPWWzaxvVCGuG/NOsiKkrjoOhI8mJ+SQUXEbUzeB3S+6XDUEEj7Q==", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zustand": { - "version": "5.0.4", - "resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.4.tgz", - "integrity": "sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==", + "version": "5.0.5", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.5.tgz", + "integrity": "sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg==", "engines": { "node": ">=12.20.0" }, diff --git a/ui/package.json b/ui/package.json index bec7eda9..b8383a99 100644 --- a/ui/package.json +++ b/ui/package.json @@ -19,27 +19,27 @@ "@uiw/codemirror-extensions-basic-setup": "^4.23.12", "@uiw/codemirror-theme-vscode": "^4.23.12", "@uiw/react-codemirror": "^4.23.12", - "ahooks": "^3.8.4", - "antd": "^5.25.1", + "ahooks": "^3.8.5", + "antd": "^5.25.3", "antd-zod": "^6.1.0", "clsx": "^2.1.1", "cron-parser": "^5.2.0", "file-saver": "^2.0.5", - "i18next": "^25.1.2", + "i18next": "^25.2.1", "i18next-browser-languagedetector": "^8.1.0", "immer": "^10.1.1", - "lucide-react": "^0.509.0", + "lucide-react": "^0.511.0", "nanoid": "^5.1.5", "pocketbase": "^0.26.0", "radash": "^12.1.0", "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", - "react-i18next": "^15.5.1", - "react-router-dom": "^7.6.0", + "react-i18next": "^15.5.2", + "react-router-dom": "^7.6.1", "tailwind-merge": "^2.6.0", - "zod": "^3.24.4", - "zustand": "^5.0.4" + "zod": "^3.25.28", + "zustand": "^5.0.5" }, "devDependencies": { "@types/file-saver": "^2.0.7", @@ -48,10 +48,10 @@ "@types/react": "^18.3.12", "@types/react-copy-to-clipboard": "^5.0.7", "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^8.32.0", - "@typescript-eslint/parser": "^8.32.0", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", "@vitejs/plugin-legacy": "^6.1.1", - "@vitejs/plugin-react": "^4.4.1", + "@vitejs/plugin-react": "^4.5.0", "autoprefixer": "^10.4.21", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.5", diff --git a/ui/public/imgs/providers/digitalocean.svg b/ui/public/imgs/providers/digitalocean.svg new file mode 100644 index 00000000..94b90bf9 --- /dev/null +++ b/ui/public/imgs/providers/digitalocean.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/public/imgs/providers/discord.svg b/ui/public/imgs/providers/discord.svg new file mode 100644 index 00000000..04f8f5e9 --- /dev/null +++ b/ui/public/imgs/providers/discord.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/duckdns.png b/ui/public/imgs/providers/duckdns.png new file mode 100644 index 00000000..c80f4a9e Binary files /dev/null and b/ui/public/imgs/providers/duckdns.png differ diff --git a/ui/public/imgs/providers/hetzner.svg b/ui/public/imgs/providers/hetzner.svg new file mode 100644 index 00000000..670cdda8 --- /dev/null +++ b/ui/public/imgs/providers/hetzner.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/public/imgs/providers/slack.svg b/ui/public/imgs/providers/slack.svg new file mode 100644 index 00000000..100af3d4 --- /dev/null +++ b/ui/public/imgs/providers/slack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/public/imgs/providers/unicloud.png b/ui/public/imgs/providers/unicloud.png new file mode 100644 index 00000000..a6b0ca9b Binary files /dev/null and b/ui/public/imgs/providers/unicloud.png differ diff --git a/ui/src/components/ModalForm.tsx b/ui/src/components/ModalForm.tsx index 8065d2f1..7fe54951 100644 --- a/ui/src/components/ModalForm.tsx +++ b/ui/src/components/ModalForm.tsx @@ -89,13 +89,6 @@ const ModalForm = = any>({ modalProps?.afterClose?.(); }, - onClose: async (e) => { - if (formPending) return; - - // 关闭 Modal 时 Promise.reject 阻止关闭 - await modalProps?.onClose?.(e as React.MouseEvent | React.KeyboardEvent); - setOpen(false); - }, }; const handleOkClick = async () => { diff --git a/ui/src/components/MultipleSplitValueInput.tsx b/ui/src/components/MultipleSplitValueInput.tsx new file mode 100644 index 00000000..5f94ec7d --- /dev/null +++ b/ui/src/components/MultipleSplitValueInput.tsx @@ -0,0 +1,108 @@ +import { type ChangeEvent, useEffect } from "react"; +import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; +import { nanoid } from "@ant-design/pro-components"; +import { useControllableValue } from "ahooks"; +import { Button, Form, Input, type InputProps, Space } from "antd"; + +import { useAntdForm } from "@/hooks"; +import ModalForm from "./ModalForm"; +import MultipleInput from "./MultipleInput"; + +type SplitOptions = { + removeEmpty?: boolean; + trim?: boolean; +}; + +export type MultipleSplitValueInputProps = Omit & { + defaultValue?: string; + delimiter?: string; + maxCount?: number; + minCount?: number; + modalTitle?: string; + modalWidth?: number; + placeholderInModal?: string; + showSortButton?: boolean; + splitOptions?: SplitOptions; + value?: string[]; + onChange?: (value: string) => void; +}; + +const DEFAULT_DELIMITER = ";"; + +const MultipleSplitValueInput = ({ + className, + style, + delimiter = DEFAULT_DELIMITER, + disabled, + maxCount, + minCount, + modalTitle, + modalWidth = 480, + placeholder, + placeholderInModal, + showSortButton = true, + splitOptions = {}, + onClear, + ...props +}: MultipleSplitValueInputProps) => { + const [value, setValue] = useControllableValue(props, { + valuePropName: "value", + defaultValuePropName: "defaultValue", + trigger: "onChange", + }); + + const { form: formInst, formProps } = useAntdForm({ + name: "componentMultipleSplitValueInput_" + nanoid(), + initialValues: { value: value?.split(delimiter) }, + onSubmit: (values) => { + const temp = values.value ?? []; + if (splitOptions.trim) { + temp.map((e) => e.trim()); + } + if (splitOptions.removeEmpty) { + temp.filter((e) => !!e); + } + + setValue(temp.join(delimiter)); + }, + }); + + useEffect(() => { + formInst.setFieldValue("value", value?.split(delimiter)); + }, [delimiter, value]); + + const handleChange = (e: ChangeEvent) => { + setValue(e.target.value); + }; + + const handleClear = () => { + setValue(""); + onClear?.(); + }; + + return ( + + + + + + } + validateTrigger="onSubmit" + width={modalWidth} + > + + + + + + ); +}; + +export default MultipleSplitValueInput; diff --git a/ui/src/components/access/AccessEditModal.tsx b/ui/src/components/access/AccessEditModal.tsx index 8d2d5204..a20d50b9 100644 --- a/ui/src/components/access/AccessEditModal.tsx +++ b/ui/src/components/access/AccessEditModal.tsx @@ -100,6 +100,7 @@ const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, .. }} afterClose={() => setOpen(false)} cancelButtonProps={{ disabled: formPending }} + cancelText={t("common.button.cancel")} closable confirmLoading={formPending} destroyOnHidden diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index fdf4f93f..44e389ed 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -29,9 +29,12 @@ import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig"; import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig"; import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig"; import AccessFormDeSECConfig from "./AccessFormDeSECConfig"; +import AccessFormDigitalOceanConfig from "./AccessFormDigitalOceanConfig"; import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig"; +import AccessFormDiscordBotConfig from "./AccessFormDiscordBotConfig"; import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig"; import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; +import AccessFormDuckDNSConfig from "./AccessFormDuckDNSConfig"; import AccessFormDynv6Config from "./AccessFormDynv6Config"; import AccessFormEdgioConfig from "./AccessFormEdgioConfig"; import AccessFormEmailConfig from "./AccessFormEmailConfig"; @@ -41,6 +44,7 @@ import AccessFormGnameConfig from "./AccessFormGnameConfig"; import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; import AccessFormGoEdgeConfig from "./AccessFormGoEdgeConfig"; import AccessFormGoogleTrustServicesConfig from "./AccessFormGoogleTrustServicesConfig"; +import AccessFormHetznerConfig from "./AccessFormHetznerConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; @@ -60,11 +64,13 @@ import AccessFormQiniuConfig from "./AccessFormQiniuConfig"; import AccessFormRainYunConfig from "./AccessFormRainYunConfig"; import AccessFormRatPanelConfig from "./AccessFormRatPanelConfig"; import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig"; +import AccessFormSlackBotConfig from "./AccessFormSlackBotConfig"; import AccessFormSSHConfig from "./AccessFormSSHConfig"; import AccessFormSSLComConfig from "./AccessFormSSLComConfig"; import AccessFormTelegramBotConfig from "./AccessFormTelegramBotConfig"; import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig"; import AccessFormUCloudConfig from "./AccessFormUCloudConfig"; +import AccessFormUniCloudConfig from "./AccessFormUniCloudConfig"; import AccessFormUpyunConfig from "./AccessFormUpyunConfig"; import AccessFormVercelConfig from "./AccessFormVercelConfig"; import AccessFormVolcEngineConfig from "./AccessFormVolcEngineConfig"; @@ -100,9 +106,9 @@ const AccessForm = forwardRef(({ className, const formSchema = z.object({ name: z .string({ message: t("access.form.name.placeholder") }) + .trim() .min(1, t("access.form.name.placeholder")) - .max(64, t("common.errmsg.string_max", { max: 64 })) - .trim(), + .max(64, t("common.errmsg.string_max", { max: 64 })), provider: z.nativeEnum(ACCESS_PROVIDERS, { message: usage === "ca-only" @@ -215,12 +221,18 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.DESEC: return ; + case ACCESS_PROVIDERS.DIGITALOCEAN: + return ; case ACCESS_PROVIDERS.DINGTALKBOT: return ; + case ACCESS_PROVIDERS.DISCORDBOT: + return ; case ACCESS_PROVIDERS.DNSLA: return ; case ACCESS_PROVIDERS.DOGECLOUD: return ; + case ACCESS_PROVIDERS.DUCKDNS: + return ; case ACCESS_PROVIDERS.DYNV6: return ; case ACCESS_PROVIDERS.EDGIO: @@ -239,6 +251,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.GOOGLETRUSTSERVICES: return ; + case ACCESS_PROVIDERS.HETZNER: + return ; case ACCESS_PROVIDERS.HUAWEICLOUD: return ; case ACCESS_PROVIDERS.JDCLOUD: @@ -277,6 +291,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.SAFELINE: return ; + case ACCESS_PROVIDERS.SLACKBOT: + return ; case ACCESS_PROVIDERS.SSH: return ; case ACCESS_PROVIDERS.TELEGRAMBOT: @@ -287,6 +303,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.UCLOUD: return ; + case ACCESS_PROVIDERS.UNICLOUD: + return ; case ACCESS_PROVIDERS.UPYUN: return ; case ACCESS_PROVIDERS.VERCEL: diff --git a/ui/src/components/access/AccessForm1PanelConfig.tsx b/ui/src/components/access/AccessForm1PanelConfig.tsx index 29481f15..56792a02 100644 --- a/ui/src/components/access/AccessForm1PanelConfig.tsx +++ b/ui/src/components/access/AccessForm1PanelConfig.tsx @@ -17,7 +17,7 @@ export type AccessForm1PanelConfigProps = { const initFormModel = (): AccessForm1PanelConfigFieldValues => { return { - apiUrl: "http://:20410/", + serverUrl: "http://:20410/", apiVersion: "v1", apiKey: "", }; @@ -27,7 +27,7 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiVersion: z.string().nonempty(t("access.form.1panel_api_version.placeholder")), apiKey: z .string() @@ -51,8 +51,8 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal name={formName} onValuesChange={handleFormChange} > - - + + @@ -68,10 +68,10 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal - + diff --git a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx index dd355b5e..d9cc2752 100644 --- a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx +++ b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormBaotaPanelConfigProps = { const initFormModel = (): AccessFormBaotaPanelConfigFieldValues => { return { - apiUrl: "http://:8888/", + serverUrl: "http://:8888/", apiKey: "", }; }; @@ -26,7 +26,7 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiKey: z.string().nonempty(t("access.form.baotapanel_api_key.placeholder")).trim(), allowInsecureConnections: z.boolean().nullish(), }); @@ -45,8 +45,8 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormBaotaWAFConfig.tsx b/ui/src/components/access/AccessFormBaotaWAFConfig.tsx index e87ed596..edf18642 100644 --- a/ui/src/components/access/AccessFormBaotaWAFConfig.tsx +++ b/ui/src/components/access/AccessFormBaotaWAFConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormBaotaWAFConfigProps = { const initFormModel = (): AccessFormBaotaWAFConfigFieldValues => { return { - apiUrl: "http://:8379/", + serverUrl: "http://:8379/", apiKey: "", }; }; @@ -26,7 +26,7 @@ const AccessFormBaotaWAFConfig = ({ form: formInst, formName, disabled, initialV const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiKey: z.string().nonempty(t("access.form.baotawaf_api_key.placeholder")).trim(), allowInsecureConnections: z.boolean().nullish(), }); @@ -45,8 +45,8 @@ const AccessFormBaotaWAFConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormCdnflyConfig.tsx b/ui/src/components/access/AccessFormCdnflyConfig.tsx index e95e230b..10422c3b 100644 --- a/ui/src/components/access/AccessFormCdnflyConfig.tsx +++ b/ui/src/components/access/AccessFormCdnflyConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormCdnflyConfigProps = { const initFormModel = (): AccessFormCdnflyConfigFieldValues => { return { - apiUrl: "http://:88/", + serverUrl: "http://:88/", apiKey: "", apiSecret: "", }; @@ -27,7 +27,7 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiKey: z .string() .min(1, t("access.form.cdnfly_api_key.placeholder")) @@ -55,8 +55,8 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormDigitalOceanConfig.tsx b/ui/src/components/access/AccessFormDigitalOceanConfig.tsx new file mode 100644 index 00000000..f4aafc4f --- /dev/null +++ b/ui/src/components/access/AccessFormDigitalOceanConfig.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForDigitalOcean } from "@/domain/access"; + +type AccessFormDigitalOceanConfigFieldValues = Nullish; + +export type AccessFormDigitalOceanConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormDigitalOceanConfigFieldValues; + onValuesChange?: (values: AccessFormDigitalOceanConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormDigitalOceanConfigFieldValues => { + return { + accessToken: "", + }; +}; + +const AccessFormDigitalOceanConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDigitalOceanConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + accessToken: z.string().nonempty(t("access.form.digitalocean_access_token.placeholder")).trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormDigitalOceanConfig; diff --git a/ui/src/components/access/AccessFormDiscordBotConfig.tsx b/ui/src/components/access/AccessFormDiscordBotConfig.tsx new file mode 100644 index 00000000..16848686 --- /dev/null +++ b/ui/src/components/access/AccessFormDiscordBotConfig.tsx @@ -0,0 +1,71 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForDiscordBot } from "@/domain/access"; + +type AccessFormDiscordBotConfigFieldValues = Nullish; + +export type AccessFormDiscordBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormDiscordBotConfigFieldValues; + onValuesChange?: (values: AccessFormDiscordBotConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormDiscordBotConfigFieldValues => { + return { + botToken: "", + }; +}; + +const AccessFormDiscordBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDiscordBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + botToken: z + .string({ message: t("access.form.discordbot_token.placeholder") }) + .min(1, t("access.form.discordbot_token.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), + defaultChannelId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormDiscordBotConfig; diff --git a/ui/src/components/access/AccessFormDuckDNSConfig.tsx b/ui/src/components/access/AccessFormDuckDNSConfig.tsx new file mode 100644 index 00000000..969f78f8 --- /dev/null +++ b/ui/src/components/access/AccessFormDuckDNSConfig.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForDuckDNS } from "@/domain/access"; + +type AccessFormDuckDNSConfigFieldValues = Nullish; + +export type AccessFormDuckDNSConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormDuckDNSConfigFieldValues; + onValuesChange?: (values: AccessFormDuckDNSConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormDuckDNSConfigFieldValues => { + return { + token: "", + }; +}; + +const AccessFormDuckDNSConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDuckDNSConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + token: z.string().nonempty(t("access.form.duckdns_token.placeholder")).trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormDuckDNSConfig; diff --git a/ui/src/components/access/AccessFormFlexCDNConfig.tsx b/ui/src/components/access/AccessFormFlexCDNConfig.tsx index 6ca020bf..71aa9607 100644 --- a/ui/src/components/access/AccessFormFlexCDNConfig.tsx +++ b/ui/src/components/access/AccessFormFlexCDNConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormFlexCDNConfigProps = { const initFormModel = (): AccessFormFlexCDNConfigFieldValues => { return { - apiUrl: "http://:8000/", + serverUrl: "http://:8000/", apiRole: "user", accessKeyId: "", accessKey: "", @@ -28,7 +28,7 @@ const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialVa const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: 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"), }), @@ -51,8 +51,8 @@ const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialVa name={formName} onValuesChange={handleFormChange} > - - + + @@ -77,10 +77,10 @@ const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialVa - + diff --git a/ui/src/components/access/AccessFormGoEdgeConfig.tsx b/ui/src/components/access/AccessFormGoEdgeConfig.tsx index 9c03f2be..641e2276 100644 --- a/ui/src/components/access/AccessFormGoEdgeConfig.tsx +++ b/ui/src/components/access/AccessFormGoEdgeConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormGoEdgeConfigProps = { const initFormModel = (): AccessFormGoEdgeConfigFieldValues => { return { - apiUrl: "http://:7788/", + serverUrl: "http://:7788/", apiRole: "user", accessKeyId: "", accessKey: "", @@ -28,7 +28,7 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), role: z.union([z.literal("user"), z.literal("admin")], { message: t("access.form.goedge_api_role.placeholder"), }), @@ -51,8 +51,8 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal name={formName} onValuesChange={handleFormChange} > - - + + @@ -77,10 +77,10 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal - + diff --git a/ui/src/components/access/AccessFormHetznerConfig.tsx b/ui/src/components/access/AccessFormHetznerConfig.tsx new file mode 100644 index 00000000..12bf21b2 --- /dev/null +++ b/ui/src/components/access/AccessFormHetznerConfig.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForHetzner } from "@/domain/access"; + +type AccessFormHetznerConfigFieldValues = Nullish; + +export type AccessFormHetznerConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormHetznerConfigFieldValues; + onValuesChange?: (values: AccessFormHetznerConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormHetznerConfigFieldValues => { + return { + apiToken: "", + }; +}; + +const AccessFormHetznerConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormHetznerConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiToken: z.string().nonempty(t("access.form.hetzner_api_token.placeholder")).trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormHetznerConfig; diff --git a/ui/src/components/access/AccessFormLeCDNConfig.tsx b/ui/src/components/access/AccessFormLeCDNConfig.tsx index 4af5a639..282afa8d 100644 --- a/ui/src/components/access/AccessFormLeCDNConfig.tsx +++ b/ui/src/components/access/AccessFormLeCDNConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormLeCDNConfigProps = { const initFormModel = (): AccessFormLeCDNConfigFieldValues => { return { - apiUrl: "http://:5090/", + serverUrl: "http://:5090/", apiVersion: "v3", apiRole: "user", username: "", @@ -29,7 +29,7 @@ const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValu const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: 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"), }), @@ -52,8 +52,8 @@ const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValu name={formName} onValuesChange={handleFormChange} > - - + + @@ -72,10 +72,10 @@ const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValu - + diff --git a/ui/src/components/access/AccessFormPowerDNSConfig.tsx b/ui/src/components/access/AccessFormPowerDNSConfig.tsx index b2bfb081..33447cc8 100644 --- a/ui/src/components/access/AccessFormPowerDNSConfig.tsx +++ b/ui/src/components/access/AccessFormPowerDNSConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormPowerDNSConfigProps = { const initFormModel = (): AccessFormPowerDNSConfigFieldValues => { return { - apiUrl: "http://:8082/", + serverUrl: "http://:8082/", apiKey: "", }; }; @@ -26,7 +26,7 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiKey: z .string() .min(1, t("access.form.powerdns_api_key.placeholder")) @@ -49,8 +49,8 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormProxmoxVEConfig.tsx b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx index d0a66745..29280b1c 100644 --- a/ui/src/components/access/AccessFormProxmoxVEConfig.tsx +++ b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormProxmoxVEConfigProps = { const initFormModel = (): AccessFormProxmoxVEConfigFieldValues => { return { - apiUrl: "http://:8006/", + serverUrl: "http://:8006/", apiToken: "", }; }; @@ -26,7 +26,7 @@ const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initial const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiToken: z.string().nonempty(t("access.form.proxmoxve_api_token.placeholder")).trim(), apiTokenSecret: z.string().nullish(), allowInsecureConnections: z.boolean().nullish(), @@ -46,8 +46,8 @@ const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initial name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormRatPanelConfig.tsx b/ui/src/components/access/AccessFormRatPanelConfig.tsx index ca3d2182..04a71cd1 100644 --- a/ui/src/components/access/AccessFormRatPanelConfig.tsx +++ b/ui/src/components/access/AccessFormRatPanelConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormRatPanelConfigProps = { const initFormModel = (): AccessFormRatPanelConfigFieldValues => { return { - apiUrl: "http://:8888/", + serverUrl: "http://:8888/", accessTokenId: 1, accessToken: "", }; @@ -27,7 +27,7 @@ const AccessFormRatPanelConfig = ({ form: formInst, formName, disabled, initialV const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: 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(), @@ -47,8 +47,8 @@ const AccessFormRatPanelConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormSafeLineConfig.tsx b/ui/src/components/access/AccessFormSafeLineConfig.tsx index 2fde089d..c698d066 100644 --- a/ui/src/components/access/AccessFormSafeLineConfig.tsx +++ b/ui/src/components/access/AccessFormSafeLineConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormSafeLineConfigProps = { const initFormModel = (): AccessFormSafeLineConfigFieldValues => { return { - apiUrl: "http://:9443/", + serverUrl: "http://:9443/", apiToken: "", }; }; @@ -26,7 +26,7 @@ const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialV const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiToken: z .string() .min(1, t("access.form.safeline_api_token.placeholder")) @@ -49,8 +49,8 @@ const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormSlackBotConfig.tsx b/ui/src/components/access/AccessFormSlackBotConfig.tsx new file mode 100644 index 00000000..3bea5f58 --- /dev/null +++ b/ui/src/components/access/AccessFormSlackBotConfig.tsx @@ -0,0 +1,71 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForSlackBot } from "@/domain/access"; + +type AccessFormSlackBotConfigFieldValues = Nullish; + +export type AccessFormSlackBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormSlackBotConfigFieldValues; + onValuesChange?: (values: AccessFormSlackBotConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormSlackBotConfigFieldValues => { + return { + botToken: "", + }; +}; + +const AccessFormSlackBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormSlackBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + botToken: z + .string({ message: t("access.form.slackbot_token.placeholder") }) + .min(1, t("access.form.slackbot_token.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), + defaultChannelId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormSlackBotConfig; diff --git a/ui/src/components/access/AccessFormTelegramBotConfig.tsx b/ui/src/components/access/AccessFormTelegramBotConfig.tsx index 3181c71d..82747694 100644 --- a/ui/src/components/access/AccessFormTelegramBotConfig.tsx +++ b/ui/src/components/access/AccessFormTelegramBotConfig.tsx @@ -26,9 +26,10 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi const formSchema = z.object({ botToken: z - .string({ message: t("access.form.telegram_bot_token.placeholder") }) - .min(1, t("access.form.telegram_bot_token.placeholder")) - .max(256, t("common.errmsg.string_max", { max: 256 })), + .string({ message: t("access.form.telegrambot_token.placeholder") }) + .min(1, t("access.form.telegrambot_token.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), defaultChatId: z .preprocess( (v) => (v == null || v === "" ? undefined : Number(v)), @@ -38,7 +39,7 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi .refine((v) => { if (v == null || v + "" === "") return true; return !Number.isNaN(+v!) && +v! !== 0; - }, t("access.form.telegram_bot_default_chat_id.placeholder")) + }, t("access.form.telegrambot_default_chat_id.placeholder")) ) .nullish(), }); @@ -59,20 +60,20 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi > } + tooltip={} > - + } + tooltip={} > - + ); diff --git a/ui/src/components/access/AccessFormUniCloudConfig.tsx b/ui/src/components/access/AccessFormUniCloudConfig.tsx new file mode 100644 index 00000000..d281f1fe --- /dev/null +++ b/ui/src/components/access/AccessFormUniCloudConfig.tsx @@ -0,0 +1,68 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForUniCloud } from "@/domain/access"; + +type AccessFormUniCloudConfigFieldValues = Nullish; + +export type AccessFormUniCloudConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormUniCloudConfigFieldValues; + onValuesChange?: (values: AccessFormUniCloudConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormUniCloudConfigFieldValues => { + return { + username: "", + password: "", + }; +}; + +const AccessFormUniCloudConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormUniCloudConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + username: z.string().trim().nonempty(t("access.form.unicloud_username.placeholder")), + password: z.string().trim().nonempty(t("access.form.unicloud_password.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormUniCloudConfig; diff --git a/ui/src/components/access/AccessFormUpyunConfig.tsx b/ui/src/components/access/AccessFormUpyunConfig.tsx index 8cc06d97..665c50cf 100644 --- a/ui/src/components/access/AccessFormUpyunConfig.tsx +++ b/ui/src/components/access/AccessFormUpyunConfig.tsx @@ -33,9 +33,9 @@ const AccessFormUpyunConfig = ({ form: formInst, formName, disabled, initialValu .max(64, t("common.errmsg.string_max", { max: 64 })), password: z .string() + .trim() .min(1, t("access.form.upyun_password.placeholder")) - .max(64, t("common.errmsg.string_max", { max: 64 })) - .trim(), + .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); diff --git a/ui/src/components/access/AccessFormWebhookConfig.tsx b/ui/src/components/access/AccessFormWebhookConfig.tsx index 6e6ec87a..d79f848d 100644 --- a/ui/src/components/access/AccessFormWebhookConfig.tsx +++ b/ui/src/components/access/AccessFormWebhookConfig.tsx @@ -362,10 +362,10 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
- + diff --git a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx index 22cb57d5..7faa148e 100644 --- a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx @@ -1,12 +1,7 @@ import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router"; -import { - FormOutlined as FormOutlinedIcon, - PlusOutlined as PlusOutlinedIcon, - QuestionCircleOutlined as QuestionCircleOutlinedIcon, - RightOutlined as RightOutlinedIcon, -} from "@ant-design/icons"; +import { PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon, RightOutlined as RightOutlinedIcon } from "@ant-design/icons"; import { useControllableValue } from "ahooks"; import { AutoComplete, @@ -19,7 +14,6 @@ import { Input, InputNumber, Select, - Space, Switch, Tooltip, Typography, @@ -29,8 +23,7 @@ import { z } from "zod"; import AccessEditModal from "@/components/access/AccessEditModal"; import AccessSelect from "@/components/access/AccessSelect"; -import ModalForm from "@/components/ModalForm"; -import MultipleInput from "@/components/MultipleInput"; +import MultipleSplitValueInput from "@/components/MultipleSplitValueInput"; import ACMEDns01ProviderSelect from "@/components/provider/ACMEDns01ProviderSelect"; import CAProviderSelect from "@/components/provider/CAProviderSelect"; import Show from "@/components/Show"; @@ -152,11 +145,9 @@ const ApplyNodeConfigForm = forwardRef("domains", formInst); const fieldProvider = Form.useWatch("provider", { form: formInst, preserve: true }); const fieldProviderAccessId = Form.useWatch("providerAccessId", formInst); const fieldCAProvider = Form.useWatch("caProvider", formInst); - const fieldNameservers = Form.useWatch("nameservers", formInst); const [showProvider, setShowProvider] = useState(false); useEffect(() => { @@ -294,25 +285,17 @@ const ApplyNodeConfigForm = forwardRef
} > - - - - - - - - } - onChange={(v) => { - formInst.setFieldValue("domains", v); - }} - /> - + } > - - - { - formInst.setFieldValue("nameservers", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("nameservers", undefined); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("nameservers", value); - }} - /> - + void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - domains: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || validDomainName(e.trim(), { allowWildcard: true })); - }, t("common.errmsg.domain_invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeApplyConfigFormDomainsModalInput", - initialValues: { domains: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.domains - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - -const NameserversModalInput = memo(({ trigger, value, onChange }: { trigger?: React.ReactNode; value?: string; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - nameservers: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || validIPv4Address(e) || validIPv6Address(e) || validDomainName(e)); - }, t("common.errmsg.domain_invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeApplyConfigFormNameserversModalInput", - initialValues: { nameservers: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.nameservers - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - export default memo(ApplyNodeConfigForm); diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index 49ed12ec..0443327e 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -82,6 +82,7 @@ import DeployNodeConfigFormTencentCloudVODConfig from "./DeployNodeConfigFormTen import DeployNodeConfigFormTencentCloudWAFConfig from "./DeployNodeConfigFormTencentCloudWAFConfig"; import DeployNodeConfigFormUCloudUCDNConfig from "./DeployNodeConfigFormUCloudUCDNConfig.tsx"; import DeployNodeConfigFormUCloudUS3Config from "./DeployNodeConfigFormUCloudUS3Config.tsx"; +import DeployNodeConfigFormUniCloudWebHostConfig from "./DeployNodeConfigFormUniCloudWebHostConfig.tsx"; import DeployNodeConfigFormUpyunCDNConfig from "./DeployNodeConfigFormUpyunCDNConfig.tsx"; import DeployNodeConfigFormUpyunFileConfig from "./DeployNodeConfigFormUpyunFileConfig.tsx"; import DeployNodeConfigFormVolcEngineALBConfig from "./DeployNodeConfigFormVolcEngineALBConfig.tsx"; @@ -318,6 +319,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.UCLOUD_US3: return ; + case DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST: + return ; case DEPLOYMENT_PROVIDERS.UPYUN_CDN: return ; case DEPLOYMENT_PROVIDERS.UPYUN_FILE: diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx index 4e3db0db..980e89fe 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx @@ -1,13 +1,9 @@ -import { memo } from "react"; import { useTranslation } from "react-i18next"; -import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; -import { Alert, Button, Form, type FormInstance, Input, Space } from "antd"; +import { Alert, Form, type FormInstance, Input } 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 MultipleSplitValueInput from "@/components/MultipleSplitValueInput"; type DeployNodeConfigFormAliyunCASDeployConfigFieldValues = Nullish<{ region: string; @@ -61,9 +57,6 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({ }); const formRule = createSchemaFieldRule(formSchema); - const fieldResourceIds = Form.useWatch("resourceIds", formInst); - const fieldContactIds = Form.useWatch("contactIds", formInst); - const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); }; @@ -87,69 +80,31 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({ } > - - - { - formInst.setFieldValue("resourceIds", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("resourceIds", ""); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("resourceIds", value); - }} - /> - + } > - - - { - formInst.setFieldValue("contactIds", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("contactIds", ""); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("contactIds", value); - }} - /> - + @@ -159,84 +114,4 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({ ); }; -const ResourceIdsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - resourceIds: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || /^[1-9]\d*$/.test(e)); - }, t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.errmsg.invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeDeployConfigFormAliyunCASResourceIdsModalInput", - initialValues: { resourceIds: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.resourceIds - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - -const ContactIdsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - contactIds: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || /^[1-9]\d*$/.test(e)); - }, t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.errmsg.invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeDeployConfigFormAliyunCASDeploymentJobContactIdsModalInput", - initialValues: { contactIds: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.contactIds - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - export default DeployNodeConfigFormAliyunCASDeployConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx index acc9d187..54507cdb 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx @@ -1,14 +1,10 @@ -import { memo } from "react"; import { useTranslation } from "react-i18next"; -import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; -import { Button, Form, type FormInstance, Input, Select, Space } from "antd"; +import { Form, type FormInstance, Input, Select } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; -import ModalForm from "@/components/ModalForm"; -import MultipleInput from "@/components/MultipleInput"; +import MultipleSplitValueInput from "@/components/MultipleSplitValueInput"; import Show from "@/components/Show"; -import { useAntdForm } from "@/hooks"; type DeployNodeConfigFormBaotaPanelSiteConfigFieldValues = Nullish<{ siteType: string; @@ -71,7 +67,6 @@ const DeployNodeConfigFormBaotaPanelSiteConfig = ({ const formRule = createSchemaFieldRule(formSchema); const fieldSiteType = Form.useWatch("siteType", formInst); - const fieldSiteNames = Form.useWatch("siteNames", formInst); const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); @@ -110,80 +105,21 @@ const DeployNodeConfigFormBaotaPanelSiteConfig = ({ } > - - - { - formInst.setFieldValue("siteNames", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("siteNames", ""); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("siteNames", value); - }} - /> - + ); }; -const SiteNamesModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - siteNames: z.array(z.string()).refine((v) => { - return v.every((e) => !!e?.trim()); - }, t("workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeDeployConfigFormBaotaPanelSiteNamesModalInput", - initialValues: { siteNames: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.siteNames - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - export default DeployNodeConfigFormBaotaPanelSiteConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx index 2a4e7d12..4fcb4e76 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx @@ -1,13 +1,9 @@ -import { memo } from "react"; import { useTranslation } from "react-i18next"; -import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; -import { Alert, AutoComplete, Button, Form, type FormInstance, Input, Space } from "antd"; +import { Alert, AutoComplete, Form, type FormInstance, Input } 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 MultipleSplitValueInput from "@/components/MultipleSplitValueInput"; type DeployNodeConfigFormTencentCloudSSLDeployConfigFieldValues = Nullish<{ region: string; @@ -56,8 +52,6 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({ }); const formRule = createSchemaFieldRule(formSchema); - const fieldResourceIds = Form.useWatch("resourceIds", formInst); - const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); }; @@ -94,36 +88,17 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({ } > - - - { - formInst.setFieldValue("resourceIds", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("resourceIds", ""); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("resourceIds", value); - }} - /> - + @@ -133,44 +108,4 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({ ); }; -const ResourceIdsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - resourceIds: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || /^[A-Za-z0-9*._-|]+$/.test(e)); - }, t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.errmsg.invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeDeployConfigFormTencentCloudSSLDeployResourceIdsModalInput", - initialValues: { resourceIds: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.resourceIds - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - export default DeployNodeConfigFormTencentCloudSSLDeployConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormUniCloudWebHostConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormUniCloudWebHostConfig.tsx new file mode 100644 index 00000000..b16a7a39 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormUniCloudWebHostConfig.tsx @@ -0,0 +1,85 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormUniCloudWebHostConfigFieldValues = Nullish<{ + spaceProvider: string; + spaceId: string; + domain: string; +}>; + +export type DeployNodeConfigFormUniCloudWebHostConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormUniCloudWebHostConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormUniCloudWebHostConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormUniCloudWebHostConfigFieldValues => { + return { + spaceProvider: "tencent", + spaceId: "", + domain: "", + }; +}; + +const DeployNodeConfigFormUniCloudWebHostConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormUniCloudWebHostConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + spaceProvider: z.string().trim().nonempty(t("workflow_node.deploy.form.unicloud_webhost_space_provider.placeholder")), + spaceId: z.string().trim().nonempty(t("workflow_node.deploy.form.unicloud_webhost_space_id.placeholder")), + domain: z.string().refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + + + +
+ ); +}; + +export default DeployNodeConfigFormUniCloudWebHostConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx index e09f5266..c18d570c 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { Form, type FormInstance, Input } from "antd"; +import { Alert, Form, type FormInstance, Input } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; @@ -44,6 +44,10 @@ const DeployNodeConfigFormUpyunCDNConfig = ({ form: formInst, formName, disabled name={formName} onValuesChange={handleFormChange} > + + } /> + + + + } /> + + ("domains", formInst); - const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); }; @@ -68,79 +62,20 @@ const DeployNodeConfigFormWangsuCDNConfig = ({ onValuesChange={handleFormChange} > } > - - - { - 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/NotifyNodeConfigForm.tsx b/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx index d303c2ba..3c612df8 100644 --- a/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx @@ -17,8 +17,10 @@ import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks import { useAccessesStore } from "@/stores/access"; import { useNotifyChannelsStore } from "@/stores/notify"; +import NotifyNodeConfigFormDiscordBotConfig from "./NotifyNodeConfigFormDiscordBotConfig"; import NotifyNodeConfigFormEmailConfig from "./NotifyNodeConfigFormEmailConfig"; import NotifyNodeConfigFormMattermostConfig from "./NotifyNodeConfigFormMattermostConfig"; +import NotifyNodeConfigFormSlackBotConfig from "./NotifyNodeConfigFormSlackBotConfig"; import NotifyNodeConfigFormTelegramBotConfig from "./NotifyNodeConfigFormTelegramBotConfig"; import NotifyNodeConfigFormWebhookConfig from "./NotifyNodeConfigFormWebhookConfig"; @@ -110,10 +112,14 @@ const NotifyNodeConfigForm = forwardRef; case NOTIFICATION_PROVIDERS.EMAIL: return ; case NOTIFICATION_PROVIDERS.MATTERMOST: return ; + case NOTIFICATION_PROVIDERS.SLACKBOT: + return ; case NOTIFICATION_PROVIDERS.TELEGRAMBOT: return ; case NOTIFICATION_PROVIDERS.WEBHOOK: diff --git a/ui/src/components/workflow/node/NotifyNodeConfigFormDiscordBotConfig.tsx b/ui/src/components/workflow/node/NotifyNodeConfigFormDiscordBotConfig.tsx new file mode 100644 index 00000000..200f2684 --- /dev/null +++ b/ui/src/components/workflow/node/NotifyNodeConfigFormDiscordBotConfig.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 NotifyNodeConfigFormDiscordBotConfigFieldValues = Nullish<{ + channelId?: string; +}>; + +export type NotifyNodeConfigFormDiscordBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: NotifyNodeConfigFormDiscordBotConfigFieldValues; + onValuesChange?: (values: NotifyNodeConfigFormDiscordBotConfigFieldValues) => void; +}; + +const initFormModel = (): NotifyNodeConfigFormDiscordBotConfigFieldValues => { + return {}; +}; + +const NotifyNodeConfigFormDiscordBotConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: NotifyNodeConfigFormDiscordBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + channelId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default NotifyNodeConfigFormDiscordBotConfig; diff --git a/ui/src/components/workflow/node/NotifyNodeConfigFormSlackBotConfig.tsx b/ui/src/components/workflow/node/NotifyNodeConfigFormSlackBotConfig.tsx new file mode 100644 index 00000000..5c304060 --- /dev/null +++ b/ui/src/components/workflow/node/NotifyNodeConfigFormSlackBotConfig.tsx @@ -0,0 +1,55 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type NotifyNodeConfigFormSlackBotConfigFieldValues = Nullish<{ + channelId?: string; +}>; + +export type NotifyNodeConfigFormSlackBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: NotifyNodeConfigFormSlackBotConfigFieldValues; + onValuesChange?: (values: NotifyNodeConfigFormSlackBotConfigFieldValues) => void; +}; + +const initFormModel = (): NotifyNodeConfigFormSlackBotConfigFieldValues => { + return {}; +}; + +const NotifyNodeConfigFormSlackBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: NotifyNodeConfigFormSlackBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + channelId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default NotifyNodeConfigFormSlackBotConfig; diff --git a/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx b/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx index a40142ee..29eaa807 100644 --- a/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx +++ b/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx @@ -38,7 +38,7 @@ const NotifyNodeConfigFormTelegramBotConfig = ({ .refine((v) => { if (v == null || v + "" === "") return true; return !Number.isNaN(+v!) && +v! !== 0; - }, t("workflow_node.notify.form.telegram_bot_chat_id.placeholder")) + }, t("workflow_node.notify.form.telegrambot_chat_id.placeholder")) ) .nullish(), }); @@ -59,11 +59,11 @@ const NotifyNodeConfigFormTelegramBotConfig = ({ > } + tooltip={} > - + ); diff --git a/ui/src/components/workflow/node/_SharedNode.tsx b/ui/src/components/workflow/node/_SharedNode.tsx index 9c55e096..cd067e36 100644 --- a/ui/src/components/workflow/node/_SharedNode.tsx +++ b/ui/src/components/workflow/node/_SharedNode.tsx @@ -1,18 +1,17 @@ -import { memo, useRef } from "react"; +import { memo, useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; import { CloseCircleOutlined as CloseCircleOutlinedIcon, - CopyOutlined as CopyOutlinedIcon, EllipsisOutlined as EllipsisOutlinedIcon, FormOutlined as FormOutlinedIcon, MoreOutlined as MoreOutlinedIcon, } from "@ant-design/icons"; import { useControllableValue } from "ahooks"; -import { Button, Card, Drawer, Dropdown, Input, type InputRef, Modal, Popover, Space } from "antd"; +import { Button, Card, Drawer, Dropdown, Input, type InputRef, type MenuProps, Modal, Popover, Space } from "antd"; import { produce } from "immer"; import { isEqual } from "radash"; -import { type WorkflowNode, WorkflowNodeType, newNode } from "@/domain/workflow"; +import { type WorkflowNode, WorkflowNodeType } from "@/domain/workflow"; import { useZustandShallowSelector } from "@/hooks"; import { useWorkflowStore } from "@/stores/workflow"; @@ -60,12 +59,13 @@ const SharedNodeTitle = ({ className, style, node, disabled }: SharedNodeTitlePr type SharedNodeMenuProps = SharedNodeProps & { branchId?: string; branchIndex?: number; + menus?: Array<"rename" | "duplicate" | "remove">; trigger: React.ReactNode; afterUpdate?: () => void; afterDelete?: () => void; }; -const isBranchingNode = (node: WorkflowNode) => { +const isNodeBranchLike = (node: WorkflowNode) => { return ( node.type === WorkflowNodeType.Branch || node.type === WorkflowNodeType.Condition || @@ -75,10 +75,14 @@ const isBranchingNode = (node: WorkflowNode) => { ); }; -const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterUpdate, afterDelete }: SharedNodeMenuProps) => { +const isNodeReadOnly = (node: WorkflowNode) => { + return node.type === WorkflowNodeType.Start || node.type === WorkflowNodeType.End; +}; + +const SharedNodeMenu = ({ menus, trigger, node, disabled, branchId, branchIndex, afterUpdate, afterDelete }: SharedNodeMenuProps) => { const { t } = useTranslation(); - const { updateNode, removeNode, removeBranch, addNode } = useWorkflowStore(useZustandShallowSelector(["updateNode", "removeNode", "removeBranch", "addNode"])); + const { updateNode, removeNode, removeBranch } = useWorkflowStore(useZustandShallowSelector(["updateNode", "removeNode", "removeBranch"])); const [modalApi, ModelContextHolder] = Modal.useModal(); @@ -101,51 +105,8 @@ const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterU afterUpdate?.(); }; - const handleCopyNode = async () => { - try { - // Clone the node by creating a new node of the same type - const clonedNode = newNode(node.type); - // Copy over configurations - clonedNode.name = `${node.name} (副本)`; - - // Copy all configurable properties - if (node.config) { - clonedNode.config = JSON.parse(JSON.stringify(node.config)); - } - - if (node.inputs) { - clonedNode.inputs = JSON.parse(JSON.stringify(node.inputs)); - } - - if (node.outputs) { - clonedNode.outputs = JSON.parse(JSON.stringify(node.outputs)); - } - - if (node.validated !== undefined) { - clonedNode.validated = node.validated; - } - - // For branch nodes, we need to deep copy the branches structure - if (node.branches && node.type === WorkflowNodeType.Branch) { - // For branch nodes, we'll keep the structure but with new IDs - // This ensures we don't run into ID conflicts - clonedNode.branches = newNode(WorkflowNodeType.Branch).branches; - } else if (node.branches && node.type === WorkflowNodeType.ExecuteResultBranch) { - // For execute result branches, we'll keep the structure but with new IDs - clonedNode.branches = newNode(WorkflowNodeType.ExecuteResultBranch).branches; - } - - // Add the cloned node after the current node - await addNode(clonedNode, node.id); - - afterUpdate?.(); - } catch (err) { - console.error("Failed to copy node:", err); - } - }; - const handleDeleteClick = async () => { - if (isBranchingNode(node)) { + if (isNodeBranchLike(node)) { await removeBranch(branchId!, branchIndex!); } else { await removeNode(node.id); @@ -154,63 +115,76 @@ const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterU afterDelete?.(); }; + const menuItems = useMemo(() => { + let temp = [ + { + key: "rename", + disabled: disabled, + label: isNodeBranchLike(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"), + icon: , + onClick: () => { + nameRef.current = node.name; + + const dialog = modalApi.confirm({ + title: isNodeBranchLike(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"), + content: ( +
+ (nameRef.current = e.target.value)} + onPressEnter={async () => { + await handleRenameConfirm(); + dialog.destroy(); + }} + /> +
+ ), + icon: null, + okText: t("common.button.save"), + onOk: handleRenameConfirm, + }); + setTimeout(() => nameInputRef.current?.focus(), 1); + }, + }, + { + type: "divider", + }, + { + key: "remove", + disabled: disabled || isNodeReadOnly(node), + label: isNodeBranchLike(node) ? t("workflow_node.action.remove_branch") : t("workflow_node.action.remove_node"), + icon: , + danger: true, + onClick: handleDeleteClick, + }, + ] satisfies MenuProps["items"]; + + if (menus) { + temp = temp.filter((item) => item.type === "divider" || menus.includes(item.key as "rename" | "remove")); + temp = temp.filter((item, index, array) => { + if (item.type !== "divider") return true; + return index === 0 || array[index - 1].type !== "divider"; + }); + if (temp[0]?.type === "divider") { + temp.shift(); + } + if (temp[temp.length - 1]?.type === "divider") { + temp.pop(); + } + } + + return temp; + }, [disabled, node]); + return ( <> {ModelContextHolder} , - onClick: () => { - nameRef.current = node.name; - - const dialog = modalApi.confirm({ - title: isBranchingNode(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"), - content: ( -
- (nameRef.current = e.target.value)} - onPressEnter={async () => { - await handleRenameConfirm(); - dialog.destroy(); - }} - /> -
- ), - icon: null, - okText: t("common.button.save"), - onOk: handleRenameConfirm, - }); - setTimeout(() => nameInputRef.current?.focus(), 1); - }, - }, - { - type: "divider", - }, - { - key: "copy", - disabled: disabled || node.type === WorkflowNodeType.Start, - label: "复制节点", - icon: , - onClick: handleCopyNode, - }, - { - key: "remove", - disabled: disabled || node.type === WorkflowNodeType.Start, - label: isBranchingNode(node) ? t("workflow_node.action.remove_branch") : t("workflow_node.action.remove_node"), - icon: , - danger: true, - onClick: handleDeleteClick, - }, - ], + items: menuItems, }} trigger={["click"]} > @@ -315,7 +289,6 @@ const SharedNodeConfigDrawer = ({ const { promise, resolve, reject } = Promise.withResolvers(); if (changed) { - console.log(oldValues, newValues); modalApi.confirm({ title: t("common.text.operation_confirm"), content: t("workflow_node.unsaved_changes.confirm"), @@ -339,6 +312,7 @@ const SharedNodeConfigDrawer = ({ destroyOnHidden extra={ } type="text" />} @@ -376,4 +350,4 @@ export default { Menu: memo(SharedNodeMenu), Block: memo(SharedNodeBlock), ConfigDrawer: memo(SharedNodeConfigDrawer), -}; +}; \ No newline at end of file diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index f60f39c8..69979aac 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -24,9 +24,12 @@ export interface AccessModel extends BaseModel { | AccessConfigForClouDNS | AccessConfigForCMCCCloud | AccessConfigForDeSEC + | AccessConfigForDigitalOcean | AccessConfigForDingTalkBot + | AccessConfigForDiscordBot | AccessConfigForDNSLA | AccessConfigForDogeCloud + | AccessConfigForDuckDNS | AccessConfigForDynv6 | AccessConfigForEdgio | AccessConfigForEmail @@ -36,6 +39,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForGoDaddy | AccessConfigForGoEdge | AccessConfigForGoogleTrustServices + | AccessConfigForHetzner | AccessConfigForHuaweiCloud | AccessConfigForJDCloud | AccessConfigForKubernetes @@ -54,11 +58,13 @@ export interface AccessModel extends BaseModel { | AccessConfigForRainYun | AccessConfigForRatPanel | AccessConfigForSafeLine + | AccessConfigForSlackBot | AccessConfigForSSH | AccessConfigForSSLCom | AccessConfigForTelegramBot | AccessConfigForTencentCloud | AccessConfigForUCloud + | AccessConfigForUniCloud | AccessConfigForUpyun | AccessConfigForVercel | AccessConfigForVolcEngine @@ -73,7 +79,7 @@ export interface AccessModel extends BaseModel { // #region AccessConfig export type AccessConfigFor1Panel = { - apiUrl: string; + serverUrl: string; apiVersion: string; apiKey: string; allowInsecureConnections?: boolean; @@ -119,13 +125,13 @@ export type AccessConfigForBaishan = { }; export type AccessConfigForBaotaPanel = { - apiUrl: string; + serverUrl: string; apiKey: string; allowInsecureConnections?: boolean; }; export type AccessConfigForBaotaWAF = { - apiUrl: string; + serverUrl: string; apiKey: string; allowInsecureConnections?: boolean; }; @@ -144,7 +150,7 @@ export type AccessConfigForCacheFly = { }; export type AccessConfigForCdnfly = { - apiUrl: string; + serverUrl: string; apiKey: string; apiSecret: string; allowInsecureConnections?: boolean; @@ -169,11 +175,20 @@ export type AccessConfigForDeSEC = { token: string; }; +export type AccessConfigForDigitalOcean = { + accessToken: string; +}; + export type AccessConfigForDingTalkBot = { webhookUrl: string; secret?: string; }; +export type AccessConfigForDiscordBot = { + botToken: string; + defaultChannelId?: string; +}; + export type AccessConfigForDNSLA = { apiId: string; apiSecret: string; @@ -184,6 +199,10 @@ export type AccessConfigForDogeCloud = { secretKey: string; }; +export type AccessConfigForDuckDNS = { + token: string; +}; + export type AccessConfigForDynv6 = { httpToken: string; }; @@ -204,7 +223,7 @@ export type AccessConfigForEmail = { }; export type AccessConfigForFlexCDN = { - apiUrl: string; + serverUrl: string; apiRole: string; accessKeyId: string; accessKey: string; @@ -226,7 +245,7 @@ export type AccessConfigForGoDaddy = { }; export type AccessConfigForGoEdge = { - apiUrl: string; + serverUrl: string; apiRole: string; accessKeyId: string; accessKey: string; @@ -238,6 +257,10 @@ export type AccessConfigForGoogleTrustServices = { eabHmacKey: string; }; +export type AccessConfigForHetzner = { + apiToken: string; +}; + export type AccessConfigForHuaweiCloud = { accessKeyId: string; secretAccessKey: string; @@ -257,7 +280,7 @@ export type AccessConfigForLarkBot = { }; export type AccessConfigForLeCDN = { - apiUrl: string; + serverUrl: string; apiVersion: string; apiRole: string; username: string; @@ -306,13 +329,13 @@ export type AccessConfigForPorkbun = { }; export type AccessConfigForPowerDNS = { - apiUrl: string; + serverUrl: string; apiKey: string; allowInsecureConnections?: boolean; }; export type AccessConfigForProxmoxVE = { - apiUrl: string; + serverUrl: string; apiToken: string; apiTokenSecret?: string; allowInsecureConnections?: boolean; @@ -328,18 +351,23 @@ export type AccessConfigForRainYun = { }; export type AccessConfigForRatPanel = { - apiUrl: string; + serverUrl: string; accessTokenId: number; accessToken: string; allowInsecureConnections?: boolean; }; export type AccessConfigForSafeLine = { - apiUrl: string; + serverUrl: string; apiToken: string; allowInsecureConnections?: boolean; }; +export type AccessConfigForSlackBot = { + botToken: string; + defaultChannelId?: string; +}; + export type AccessConfigForSSH = { host: string; port: number; @@ -370,6 +398,11 @@ export type AccessConfigForUCloud = { projectId?: string; }; +export type AccessConfigForUniCloud = { + username: string; + password: string; +}; + export type AccessConfigForUpyun = { username: string; password: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 15542455..594674f1 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -23,9 +23,12 @@ export const ACCESS_PROVIDERS = Object.freeze({ CLOUDNS: "cloudns", CMCCCLOUD: "cmcccloud", DESEC: "desec", + DIGITALOCEAN: "digitalocean", DINGTALKBOT: "dingtalkbot", + DISCORDBOT: "discordbot", DNSLA: "dnsla", DOGECLOUD: "dogecloud", + DUCKDNS: "duckdns", DYNV6: "dynv6", EDGIO: "edgio", EMAIL: "email", @@ -35,6 +38,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ GODADDY: "godaddy", GOEDGE: "goedge", GOOGLETRUSTSERVICES: "googletrustservices", + HETZNER: "hetzner", HUAWEICLOUD: "huaweicloud", JDCLOUD: "jdcloud", KUBERNETES: "k8s", @@ -57,11 +61,13 @@ export const ACCESS_PROVIDERS = Object.freeze({ RAINYUN: "rainyun", RATPANEL: "ratpanel", SAFELINE: "safeline", + SLACKBOT: "slackbot", SSH: "ssh", SSLCOM: "sslcom", TELEGRAMBOT: "telegrambot", TENCENTCLOUD: "tencentcloud", UCLOUD: "ucloud", + UNICLOUD: "unicloud", UPYUN: "upyun", VERCEL: "vercel", VOLCENGINE: "volcengine", @@ -122,26 +128,30 @@ export const accessProvidersMap: Map [ e[0] as string, { @@ -253,11 +265,14 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({ CLOUDNS: `${ACCESS_PROVIDERS.CLOUDNS}`, CMCCCLOUD: `${ACCESS_PROVIDERS.CMCCCLOUD}`, DESEC: `${ACCESS_PROVIDERS.DESEC}`, + DIGITALOCEAN: `${ACCESS_PROVIDERS.DIGITALOCEAN}`, DNSLA: `${ACCESS_PROVIDERS.DNSLA}`, + DUCKDNS: `${ACCESS_PROVIDERS.DUCKDNS}`, DYNV6: `${ACCESS_PROVIDERS.DYNV6}`, GCORE: `${ACCESS_PROVIDERS.GCORE}`, GNAME: `${ACCESS_PROVIDERS.GNAME}`, GODADDY: `${ACCESS_PROVIDERS.GODADDY}`, + HETZNER: `${ACCESS_PROVIDERS.HETZNER}`, HUAWEICLOUD: `${ACCESS_PROVIDERS.HUAWEICLOUD}`, // 兼容旧值,等同于 `HUAWEICLOUD_DNS` HUAWEICLOUD_DNS: `${ACCESS_PROVIDERS.HUAWEICLOUD}-dns`, JDCLOUD: `${ACCESS_PROVIDERS.JDCLOUD}`, // 兼容旧值,等同于 `JDCLOUD_DNS` @@ -309,11 +324,14 @@ export const acmeDns01ProvidersMap: Map [ type, @@ -576,9 +596,11 @@ export const deploymentProvidersMap: Map [ type, { diff --git a/ui/src/domain/version.ts b/ui/src/domain/version.ts index 442a7506..b7741526 100644 --- a/ui/src/domain/version.ts +++ b/ui/src/domain/version.ts @@ -1 +1 @@ -export const version = "v0.3.13"; +export const version = "v0.3.14"; diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index 41b81636..bf453f1d 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -34,16 +34,16 @@ "access.form.certificate_authority.placeholder": "Please select a certificate authority", "access.form.notification_channel.label": "Notification channel", "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_server_url.label": "1Panel server URL", + "access.form.1panel_server_url.placeholder": "Please enter 1Panel server 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.common_allow_insecure_conns.label": "Insecure SSL/TLS connections", + "access.form.common_allow_insecure_conns.switch.on": "Allow", + "access.form.common_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", @@ -96,30 +96,18 @@ "access.form.bunny_api_key.label": "Bunny API key", "access.form.bunny_api_key.placeholder": "Please enter Bunny API key", "access.form.bunny_api_key.tooltip": "For more information, see https://docs.bunny.net/reference/bunnynet-api-overview", - "access.form.upyun_username.label": "UPYUN subaccount username", - "access.form.upyun_username.placeholder": "Please enter UPYUN subaccount username", - "access.form.upyun_username.tooltip": "For more information, see https://console.upyun.com/account/subaccount/", - "access.form.upyun_password.label": "UPYUN subaccount password", - "access.form.upyun_password.placeholder": "Please enter UPYUN subaccount password", - "access.form.upyun_password.tooltip": "For more information, see https://console.upyun.com/account/subaccount/", "access.form.baishan_api_token.label": "Baishan Cloud API token", "access.form.baishan_api_token.placeholder": "Please enter Baishan Cloud API token", - "access.form.baotapanel_api_url.label": "aaPanel URL", - "access.form.baotapanel_api_url.placeholder": "Please enter aaPanel URL", + "access.form.baotapanel_server_url.label": "aaPanel server URL", + "access.form.baotapanel_server_url.placeholder": "Please enter aaPanel server URL", "access.form.baotapanel_api_key.label": "aaPanel API key", "access.form.baotapanel_api_key.placeholder": "Please enter aaPanel API key", "access.form.baotapanel_api_key.tooltip": "For more information, see https://www.bt.cn/bbs/thread-20376-1-1.html", - "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_server_url.label": "aaWAF server URL", + "access.form.baotawaf_server_url.placeholder": "Please enter aaWAF server 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", @@ -129,17 +117,14 @@ "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 URL", - "access.form.cdnfly_api_url.placeholder": "Please enter Cdnfly URL", + "access.form.cdnfly_server_url.label": "Cdnfly server URL", + "access.form.cdnfly_server_url.placeholder": "Please enter Cdnfly server 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", "access.form.cdnfly_api_secret.label": "Cdnfly user API secret", "access.form.cdnfly_api_secret.placeholder": "Please enter Cdnfly user API secret", "access.form.cdnfly_api_secret.tooltip": "For more information, see https://doc.cdnfly.cn/shiyongjieshao.html", - "access.form.cdnfly_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.cdnfly_allow_insecure_conns.switch.on": "Allow", - "access.form.cdnfly_allow_insecure_conns.switch.off": "Disallow", "access.form.cloudflare_dns_api_token.label": "Cloudflare DNS API token", "access.form.cloudflare_dns_api_token.placeholder": "Please enter Cloudflare DNS API token", "access.form.cloudflare_dns_api_token.tooltip": "For more information, see https://developers.cloudflare.com/fundamentals/api/get-started/create-token/", @@ -161,12 +146,21 @@ "access.form.desec_token.label": "deSEC token", "access.form.desec_token.placeholder": "Please enter deSEC token", "access.form.desec_token.tooltip": "For more information, see https://desec.readthedocs.io/en/latest/auth/tokens.html", + "access.form.digitalocean_access_token.label": "DigitalOcean access token", + "access.form.digitalocean_access_token.placeholder": "Please enter DigitalOcean access token", + "access.form.digitalocean_access_token.tooltip": "For more information, see https://docs.digitalocean.com/reference/api/create-personal-access-token/", "access.form.dingtalkbot_webhook_url.label": "DingTalk bot Webhook URL", "access.form.dingtalkbot_webhook_url.placeholder": "Please enter DingTalk bot Webhook URL", "access.form.dingtalkbot_webhook_url.tooltip": "For more information, see https://open.dingtalk.com/document/orgapp/obtain-the-webhook-address-of-a-custom-robot", "access.form.dingtalkbot_secret.label": "DingTalk bot secret", "access.form.dingtalkbot_secret.placeholder": "Please enter DingTalk bot secret", "access.form.dingtalkbot_secret.tooltip": "For more information, see https://open.dingtalk.com/document/orgapp/customize-robot-security-settings", + "access.form.discordbot_token.label": "Discord bot token", + "access.form.discordbot_token.placeholder": "Please enter Discord bot token", + "access.form.discordbot_token.tooltip": "For more information, see https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token", + "access.form.discordbot_default_channel_id.label": "Default Discord channel ID (Optional)", + "access.form.discordbot_default_channel_id.placeholder": "Please enter default Discord channel ID", + "access.form.discordbot_default_channel_id.tooltip": "For more information, see https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", "access.form.dnsla_api_id.label": "DNS.LA API ID", "access.form.dnsla_api_id.placeholder": "Please enter DNS.LA API ID", "access.form.dnsla_api_id.tooltip": "For more information, see https://www.dns.la/docs/ApiDoc", @@ -179,6 +173,9 @@ "access.form.dogecloud_secret_key.label": "Doge Cloud SecretKey", "access.form.dogecloud_secret_key.placeholder": "Please enter Doge Cloud SecretKey", "access.form.dogecloud_secret_key.tooltip": "For more information, see https://console.dogecloud.com/", + "access.form.duckdns_token.label": "DuckDNS token", + "access.form.duckdns_token.placeholder": "Please enter DuckDNS token", + "access.form.duckdns_token.tooltip": "For more information, see https://www.duckdns.org/spec.jsp", "access.form.dynv6_http_token.label": "dynv6 HTTP token", "access.form.dynv6_http_token.placeholder": "Please enter dynv6 HTTP token", "access.form.dynv6_http_token.tooltip": "For more information, see https://dynv6.com/keys", @@ -201,8 +198,8 @@ "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_server_url.label": "FlexCDN server URL", + "access.form.flexcdn_server_url.placeholder": "Please enter FlexCDN server 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", @@ -213,9 +210,6 @@ "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", @@ -231,8 +225,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 URL", - "access.form.goedge_api_url.placeholder": "Please enter GoEdge URL", + "access.form.goedge_server_url.label": "GoEdge server URL", + "access.form.goedge_server_url.placeholder": "Please enter GoEdge server 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", @@ -243,15 +237,15 @@ "access.form.goedge_access_key.label": "GoEdge AccessKey", "access.form.goedge_access_key.placeholder": "Please enter GoEdge AccessKey", "access.form.goedge_access_key.tooltip": "For more information, see https://goedge.cloud/docs/API/Auth.md", - "access.form.goedge_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.goedge_allow_insecure_conns.switch.on": "Allow", - "access.form.goedge_allow_insecure_conns.switch.off": "Disallow", "access.form.googletrustservices_eab_kid.label": "ACME EAB KID", "access.form.googletrustservices_eab_kid.placeholder": "Please enter ACME EAB KID", "access.form.googletrustservices_eab_kid.tooltip": "For more information, see https://cloud.google.com/certificate-manager/docs/public-ca-tutorial", "access.form.googletrustservices_eab_hmac_key.label": "ACME EAB HMAC key", "access.form.googletrustservices_eab_hmac_key.placeholder": "Please enter ACME EAB HMAC key", "access.form.googletrustservices_eab_hmac_key.tooltip": "For more information, see https://cloud.google.com/certificate-manager/docs/public-ca-tutorial", + "access.form.hetzner_api_token.label": "Hetzner API token", + "access.form.hetzner_api_token.placeholder": "Please enter Hetzner API token", + "access.form.hetzner_api_token.tooltip": "For more information, see https://docs.hetzner.com/cloud/api/getting-started/generating-api-token", "access.form.huaweicloud_access_key_id.label": "Huawei Cloud AccessKeyId", "access.form.huaweicloud_access_key_id.placeholder": "Please enter Huawei Cloud AccessKeyId", "access.form.huaweicloud_access_key_id.tooltip": "For more information, see https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html", @@ -270,8 +264,8 @@ "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_server_url.label": "LeCDN server URL", + "access.form.lecdn_server_url.placeholder": "Please enter LeCDN server 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", @@ -282,9 +276,6 @@ "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", @@ -293,7 +284,7 @@ "access.form.mattermost_password.placeholder": "Please enter Mattermost password", "access.form.mattermost_default_channel_id.label": "Default Mattermost channel ID (Optional)", "access.form.mattermost_default_channel_id.placeholder": "Please enter default Mattermost channel ID", - "access.form.mattermost_default_channel_id.tooltip": "How to get the channel ID? Select the target channel from the left sidebar, click on the channel name at the top, and choose ”Channel Details.” You can directly see the channel ID on the pop-up page.", + "access.form.mattermost_default_channel_id.tooltip": "How to get it? Select the target channel from the left sidebar, click on the channel name at the top, and choose ”Channel Details.” You can directly see the channel ID on the pop-up page.", "access.form.namecheap_username.label": "Namecheap username", "access.form.namecheap_username.placeholder": "Please enter Namecheap username", "access.form.namecheap_username.tooltip": "For more information, see https://www.namecheap.com/support/api/intro/", @@ -330,25 +321,19 @@ "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 URL", - "access.form.powerdns_api_url.placeholder": "Please enter PowerDNS URL", + "access.form.powerdns_server_url.label": "PowerDNS server URL", + "access.form.powerdns_server_url.placeholder": "Please enter PowerDNS server 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", - "access.form.powerdns_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.powerdns_allow_insecure_conns.switch.on": "Allow", - "access.form.powerdns_allow_insecure_conns.switch.off": "Disallow", - "access.form.proxmoxve_api_url.label": "Proxmox VE URL", - "access.form.proxmoxve_api_url.placeholder": "Please enter Proxmox VE URL", + "access.form.proxmoxve_server_url.label": "Proxmox VE server URL", + "access.form.proxmoxve_server_url.placeholder": "Please enter Proxmox VE server URL", "access.form.proxmoxve_api_token.label": "Proxmox VE API token", "access.form.proxmoxve_api_token.placeholder": "Please enter Proxmox VE API token", "access.form.proxmoxve_api_token.tooltip": "For more information, see https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens", "access.form.proxmoxve_api_token_secret.label": "Proxmox VE API token secret (Optional)", "access.form.proxmoxve_api_token_secret.placeholder": "Please enter Proxmox VE API token secret", "access.form.proxmoxve_api_token_secret.tooltip": "For more information, see https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens", - "access.form.proxmoxve_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.proxmoxve_allow_insecure_conns.switch.on": "Allow", - "access.form.proxmoxve_allow_insecure_conns.switch.off": "Disallow", "access.form.qiniu_access_key.label": "Qiniu AccessKey", "access.form.qiniu_access_key.placeholder": "Please enter Qiniu AccessKey", "access.form.qiniu_access_key.tooltip": "For more information, see https://portal.qiniu.com/", @@ -358,25 +343,25 @@ "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_server_url.label": "RatPanel server URL", + "access.form.ratpanel_server_url.placeholder": "Please enter RatPanel server 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_server_url.label": "SafeLine server URL", + "access.form.safeline_server_url.placeholder": "Please enter SafeLine server URL", "access.form.safeline_api_token.label": "SafeLine API token", "access.form.safeline_api_token.placeholder": "Please enter SafeLine API token", "access.form.safeline_api_token.tooltip": "For more information, see https://docs.waf.chaitin.com/en/reference/articles/openapi", - "access.form.safeline_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.safeline_allow_insecure_conns.switch.on": "Allow", - "access.form.safeline_allow_insecure_conns.switch.off": "Disallow", + "access.form.slackbot_token.label": "Slack bot token", + "access.form.slackbot_token.placeholder": "Please enter Slack bot token", + "access.form.slackbot_token.tooltip": "For more information, see https://docs.slack.dev/authentication/tokens#bot", + "access.form.slackbot_default_channel_id.label": "Default Slack channel ID (Optional)", + "access.form.slackbot_default_channel_id.placeholder": "Please enter default Slack channel ID", + "access.form.slackbot_default_channel_id.tooltip": "How to get it? Please refer to https://www.youtube.com/watch?v=Uz5Yi5C2pwQ", "access.form.ssh_host.label": "Server host", "access.form.ssh_host.placeholder": "Please enter server host", "access.form.ssh_port.label": "Server port", @@ -401,12 +386,12 @@ "access.form.sslcom_eab_hmac_key.label": "ACME EAB HMAC key", "access.form.sslcom_eab_hmac_key.placeholder": "Please enter ACME EAB HMAC key", "access.form.sslcom_eab_hmac_key.tooltip": "For more information, see https://www.ssl.com/how-to/generate-acme-credentials-for-reseller-customers/", - "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_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.telegrambot_token.label": "Telegram bot token", + "access.form.telegrambot_token.placeholder": "Please enter Telegram bot token", + "access.form.telegrambot_token.tooltip": "How to get it? Please refer to https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", + "access.form.telegrambot_default_chat_id.label": "Default Telegram chat ID (Optional)", + "access.form.telegrambot_default_chat_id.placeholder": "Please enter default Telegram chat ID", + "access.form.telegrambot_default_chat_id.tooltip": "How to get it? 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", @@ -422,6 +407,16 @@ "access.form.ucloud_project_id.label": "UCloud project ID (Optional)", "access.form.ucloud_project_id.placeholder": "Please enter UCloud project ID", "access.form.ucloud_project_id.tooltip": "For more information, see https://console.ucloud-global.com/uaccount/iam/project_manage", + "access.form.unicloud_username.label": "uniCloud username", + "access.form.unicloud_username.placeholder": "Please enter uniCloud username", + "access.form.unicloud_password.label": "uniCloud password", + "access.form.unicloud_password.placeholder": "Please enter uniCloud password", + "access.form.upyun_username.label": "UPYUN subaccount username", + "access.form.upyun_username.placeholder": "Please enter UPYUN subaccount username", + "access.form.upyun_username.tooltip": "For more information, see https://console.upyun.com/account/subaccount/", + "access.form.upyun_password.label": "UPYUN subaccount password", + "access.form.upyun_password.placeholder": "Please enter UPYUN subaccount password", + "access.form.upyun_password.tooltip": "For more information, see https://console.upyun.com/account/subaccount/", "access.form.vercel_api_access_token.label": "Vercel API access token", "access.form.vercel_api_access_token.placeholder": "Please enter Vercel API access token", "access.form.vercel_api_access_token.tooltip": "For more information, see https://vercel.com/guides/how-do-i-use-a-vercel-api-access-token", @@ -466,9 +461,6 @@ "access.form.webhook_preset_data.option.pushplus.label": "PushPlus", "access.form.webhook_preset_data.option.serverchan.label": "ServerChan", "access.form.webhook_preset_data.option.common.label": "General template", - "access.form.webhook_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.webhook_allow_insecure_conns.switch.on": "Allow", - "access.form.webhook_allow_insecure_conns.switch.off": "Disallow", "access.form.wecombot_webhook_url.label": "WeCom bot Webhook URL", "access.form.wecombot_webhook_url.placeholder": "Please enter WeCom bot Webhook URL", "access.form.wecombot_webhook_url.tooltip": "For more information, see https://open.work.weixin.qq.com/help2/pc/18401", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index a3bbfecc..9e59d9d0 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -58,14 +58,17 @@ "provider.ctcccloud": "China Telecom Cloud (State Cloud)", "provider.cucccloud": "China Unicom Cloud", "provider.desec": "deSEC", + "provider.digitalocean": "DigitalOcean", "provider.dingtalkbot": "DingTalk Bot", + "provider.discordbot": "Discord Bot", "provider.dnsla": "DNS.LA", "provider.dogecloud": "Doge Cloud", "provider.dogecloud.cdn": "Doge Cloud - CDN (Content Delivery Network)", + "provider.duckdns": "Duck DNS", "provider.dynv6": "dynv6", "provider.edgio": "Edgio", "provider.edgio.applications": "Edgio - Applications", - "provider.email": "Email", + "provider.email": "Email (SMTP)", "provider.fastly": "Fastly", "provider.flexcdn": "FlexCDN", "provider.gcore": "Gcore", @@ -74,6 +77,7 @@ "provider.godaddy": "GoDaddy", "provider.goedge": "GoEdge", "provider.googletrustservices": "Google Trust Services", + "provider.hetzner": "Hetzner", "provider.huaweicloud": "Huawei Cloud", "provider.huaweicloud.cdn": "Huawei Cloud - CDN (Content Delivery Network)", "provider.huaweicloud.dns": "Huawei Cloud - DNS (Domain Name Service)", @@ -92,7 +96,7 @@ "provider.lecdn": "LeCDN", "provider.letsencrypt": "Let's Encrypt", "provider.letsencryptstaging": "Let's Encrypt Staging Environment", - "provider.local": "Local deployment", + "provider.local": "Local host", "provider.mattermost": "Mattermost", "provider.namecheap": "Namecheap", "provider.namedotcom": "Name.com", @@ -114,7 +118,8 @@ "provider.ratpanel.console": "RatPanel - Console", "provider.ratpanel.site": "RatPanel - Website", "provider.safeline": "SafeLine", - "provider.ssh": "SSH deployment", + "provider.slackbot": "Slack Bot", + "provider.ssh": "Remote host (SSH)", "provider.sslcom": "SSL.com", "provider.telegrambot": "Telegram Bot", "provider.tencentcloud": "Tencent Cloud", @@ -133,6 +138,8 @@ "provider.ucloud": "UCloud", "provider.ucloud.ucdn": "UCloud - UCDN (UCloud Content Delivery Network)", "provider.ucloud.us3": "UCloud - US3 (UCloud Object-based Storage)", + "provider.unicloud": "uniCloud (DCloud)", + "provider.unicloud.webhost": "uniCloud (DCloud) - Web Host", "provider.upyun": "UPYUN", "provider.upyun.cdn": "UPYUN - CDN (Content Delivery Network)", "provider.upyun.file": "UPYUN - USS (Storage Service)", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 80237287..6de6eda8 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -109,7 +109,7 @@ "workflow_node.deploy.form.certificate.placeholder": "Please select certificate", "workflow_node.deploy.form.certificate.tooltip": "The certificate to be deployed comes from the previous nodes of application or upload.", "workflow_node.deploy.form.params_config.label": "Parameter settings", - "workflow_node.deploy.form.1panel_console_auto_restart.label": "Auto restart after deployment", + "workflow_node.deploy.form.1panel_console_auto_restart.label": "Auto restart 1Panel after deployment", "workflow_node.deploy.form.1panel_site_resource_type.label": "Resource type", "workflow_node.deploy.form.1panel_site_resource_type.placeholder": "Please select resource type", "workflow_node.deploy.form.1panel_site_resource_type.option.website.label": "Website", @@ -331,7 +331,7 @@ "workflow_node.deploy.form.baishan_cdn_certificate_id.label": "Baishan Cloud CDN certificate ID (Optional)", "workflow_node.deploy.form.baishan_cdn_certificate_id.placeholder": "Please enter Baishan Cloud CDN certificate ID", "workflow_node.deploy.form.baishan_cdn_certificate_id.tooltip": "For more information, see https://cdnx.console.baishan.com/#/cdn/cert", - "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "Auto restart after deployment", + "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "Auto restart aaPanel after deployment", "workflow_node.deploy.form.baotapanel_site_type.label": "aaPanel site type", "workflow_node.deploy.form.baotapanel_site_type.placeholder": "Please select aaPanel site type", "workflow_node.deploy.form.baotapanel_site_type.option.php.label": "PHP sites", @@ -530,7 +530,7 @@ "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", - "workflow_node.deploy.form.proxmoxve_auto_restart.label": "Auto restart after deployment", + "workflow_node.deploy.form.proxmoxve_auto_restart.label": "Auto restart Proxmox VE after deployment", "workflow_node.deploy.form.qiniu_cdn_domain.label": "Qiniu CDN domain", "workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "Please enter Qiniu CDN domain name", "workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "For more information, see https://portal.qiniu.com/cdn", @@ -696,9 +696,21 @@ "workflow_node.deploy.form.ucloud_us3_domain.label": "UCloud US3 domain", "workflow_node.deploy.form.ucloud_us3_domain.placeholder": "Please enter UCloud US3 domain name", "workflow_node.deploy.form.ucloud_us3_domain.tooltip": "For more information, see https://console.ucloud-global.com/ufile", + "workflow_node.deploy.form.unicloud_webhost.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the uniCloud, please create a GitHub Issue.", + "workflow_node.deploy.form.unicloud_webhost_space_provider.label": "uniCloud space provider", + "workflow_node.deploy.form.unicloud_webhost_space_provider.placeholder": "Please select uniCloud space provider", + "workflow_node.deploy.form.unicloud_webhost_space_provider.option.aliyun.label": "Alibaba Cloud", + "workflow_node.deploy.form.unicloud_webhost_space_provider.option.tencent.label": "Tencent Cloud", + "workflow_node.deploy.form.unicloud_webhost_space_id.label": "uniCloud space ID", + "workflow_node.deploy.form.unicloud_webhost_space_id.placeholder": "uniCloud space ID", + "workflow_node.deploy.form.unicloud_webhost_space_id.tooltip": "For more information, see https://doc.dcloud.net.cn/uniCloud/concepts/space.html", + "workflow_node.deploy.form.unicloud_webhost_domain.label": "uniCloud Web host domain", + "workflow_node.deploy.form.unicloud_webhost_domain.placeholder": "uniCloud Web host domain", + "workflow_node.deploy.form.upyun_cdn.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the UPYUN, please create a GitHub Issue.", "workflow_node.deploy.form.upyun_cdn_domain.label": "UPYUN CDN domain", "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "Please enter UPYUN CDN domain name", "workflow_node.deploy.form.upyun_cdn_domain.tooltip": "For more information, see https://console.upyun.com/services/cdn/", + "workflow_node.deploy.form.upyun_file.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the UPYUN, please create a GitHub Issue.", "workflow_node.deploy.form.upyun_file_domain.label": "UPYUN bucket domain", "workflow_node.deploy.form.upyun_file_domain.placeholder": "Please enter UPYUN bucket domain name", "workflow_node.deploy.form.upyun_file_domain.tooltip": "For more information, see https://console.upyun.com/services/file/", @@ -815,6 +827,9 @@ "workflow_node.notify.form.provider_access.placeholder": "Please select an authorization of notification provider", "workflow_node.notify.form.provider_access.button": "Create", "workflow_node.notify.form.params_config.label": "Parameter settings", + "workflow_node.notify.form.discordbot_channel_id.label": "Discord channel ID (Optional)", + "workflow_node.notify.form.discordbot_channel_id.placeholder": "Please enter Discord channel ID to override the default value", + "workflow_node.notify.form.discordbot_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.", "workflow_node.notify.form.email_sender_address.label": "Sender email address (Optional)", "workflow_node.notify.form.email_sender_address.placeholder": "Please enter sender email address to override the default value", "workflow_node.notify.form.email_sender_address.tooltip": "Leave it blank to use the default sender email address provided by the authorization.", @@ -822,11 +837,14 @@ "workflow_node.notify.form.email_receiver_address.placeholder": "Please enter receiver email address to override the default value", "workflow_node.notify.form.email_receiver_address.tooltip": "Leave it blank to use the default receiver email address provided by the selected authorization.", "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.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_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.slackbot_channel_id.label": "Slack channel ID (Optional)", + "workflow_node.notify.form.slackbot_channel_id.placeholder": "Please enter Slack channel ID to override the default value", + "workflow_node.notify.form.slackbot_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.", + "workflow_node.notify.form.telegrambot_chat_id.label": "Telegram chat ID (Optional)", + "workflow_node.notify.form.telegrambot_chat_id.placeholder": "Please enter Telegram chat ID to override the default value", + "workflow_node.notify.form.telegrambot_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 a5e89438..fb51668f 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -34,16 +34,16 @@ "access.form.certificate_authority.placeholder": "请选择证书颁发机构", "access.form.notification_channel.label": "通知渠道", "access.form.notification_channel.placeholder": "请选择通知渠道", - "access.form.1panel_api_url.label": "1Panel URL", - "access.form.1panel_api_url.placeholder": "请输入 1Panel URL", + "access.form.common_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", + "access.form.common_allow_insecure_conns.switch.on": "允许", + "access.form.common_allow_insecure_conns.switch.off": "不允许", + "access.form.1panel_server_url.label": "1Panel 服务地址", + "access.form.1panel_server_url.placeholder": "请输入 1Panel 服务地址", "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", @@ -95,22 +95,16 @@ "access.form.baiducloud_secret_access_key.tooltip": "这是什么?请参阅 https://cloud.baidu.com/doc/Reference/s/jjwvz2e3p", "access.form.baishan_api_token.label": "白山云 API Token", "access.form.baishan_api_token.placeholder": "请输入白山云 API Token", - "access.form.baotapanel_api_url.label": "宝塔面板 URL", - "access.form.baotapanel_api_url.placeholder": "请输入宝塔面板 URL", + "access.form.baotapanel_server_url.label": "宝塔面板服务地址", + "access.form.baotapanel_server_url.placeholder": "请输入宝塔面板服务地址", "access.form.baotapanel_api_key.label": "宝塔面板接口密钥", "access.form.baotapanel_api_key.placeholder": "请输入宝塔面板接口密钥", "access.form.baotapanel_api_key.tooltip": "这是什么?请参阅 https://www.bt.cn/bbs/thread-113890-1-1.html", - "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_server_url.label": "堡塔云 WAF 服务地址", + "access.form.baotawaf_server_url.placeholder": "请输入堡塔云 WAF 服务地址", "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", @@ -123,17 +117,14 @@ "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 URL", - "access.form.cdnfly_api_url.placeholder": "请输入 Cdnfly URL", + "access.form.cdnfly_server_url.label": "Cdnfly 服务地址", + "access.form.cdnfly_server_url.placeholder": "请输入 Cdnfly 服务地址", "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", "access.form.cdnfly_api_secret.label": "Cdnfly 用户端 API Secret", "access.form.cdnfly_api_secret.placeholder": "请输入 Cdnfly 用户端 API Secret", "access.form.cdnfly_api_secret.tooltip": "这是什么?请参阅 https://doc.cdnfly.cn/shiyongjieshao.html", - "access.form.cdnfly_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.cdnfly_allow_insecure_conns.switch.on": "允许", - "access.form.cdnfly_allow_insecure_conns.switch.off": "不允许", "access.form.cloudflare_dns_api_token.label": "Cloudflare DNS API 令牌", "access.form.cloudflare_dns_api_token.placeholder": "请输入 Cloudflare DNS API 令牌", "access.form.cloudflare_dns_api_token.tooltip": "这是什么?请参阅 https://developers.cloudflare.com/fundamentals/api/get-started/create-token/", @@ -155,12 +146,21 @@ "access.form.desec_token.label": "deSEC Token", "access.form.desec_token.placeholder": "请输入 deSEC Token", "access.form.desec_token.tooltip": "这是什么?请参阅 https://desec.readthedocs.io/en/latest/auth/tokens.html", + "access.form.digitalocean_access_token.label": "DigitalOcean Access Token", + "access.form.digitalocean_access_token.placeholder": "请输入 DigitalOcean Access Token", + "access.form.digitalocean_access_token.tooltip": "这是什么?请参阅 https://docs.digitalocean.com/reference/api/create-personal-access-token/", "access.form.dingtalkbot_webhook_url.label": "钉钉群机器人 Webhook 地址", "access.form.dingtalkbot_webhook_url.placeholder": "请输入钉钉群机器人 Webhook 地址", "access.form.dingtalkbot_webhook_url.tooltip": "这是什么?请参阅 https://open.dingtalk.com/document/orgapp/obtain-the-webhook-address-of-a-custom-robot", "access.form.dingtalkbot_secret.label": "钉钉群机器人加签密钥", "access.form.dingtalkbot_secret.placeholder": "请输入钉钉群机器人加签密钥", "access.form.dingtalkbot_secret.tooltip": "这是什么?请参阅 https://open.dingtalk.com/document/orgapp/customize-robot-security-settings", + "access.form.discordbot_token.label": "Discord 机器人 API Token", + "access.form.discordbot_token.placeholder": "请输入 Discord 机器人 API Token", + "access.form.discordbot_token.tooltip": "这是什么?请参阅 https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token", + "access.form.discordbot_default_channel_id.label": "默认的 Discord 频道 ID(可选)", + "access.form.discordbot_default_channel_id.placeholder": "请输入默认的 Discord 频道 ID", + "access.form.discordbot_default_channel_id.tooltip": "这是什么?请参阅 https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", "access.form.dnsla_api_id.label": "DNS.LA API ID", "access.form.dnsla_api_id.placeholder": "请输入 DNS.LA API ID", "access.form.dnsla_api_id.tooltip": "这是什么?请参阅 https://www.dns.la/docs/ApiDoc", @@ -173,6 +173,9 @@ "access.form.dogecloud_secret_key.label": "多吉云 SecretKey", "access.form.dogecloud_secret_key.placeholder": "请输入多吉云 SecretKey", "access.form.dogecloud_secret_key.tooltip": "这是什么?请参阅 https://console.dogecloud.com/", + "access.form.duckdns_token.label": "DuckDNS Token", + "access.form.duckdns_token.placeholder": "请输入 DuckDNS Token", + "access.form.duckdns_token.tooltip": "这是什么?请参阅 https://www.duckdns.org/spec.jsp", "access.form.dynv6_http_token.label": "dynv6 HTTP Token", "access.form.dynv6_http_token.placeholder": "请输入 dynv6 HTTP Token", "access.form.dynv6_http_token.tooltip": "这是什么?请参阅 https://dynv6.com/keys", @@ -195,8 +198,8 @@ "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_server_url.label": "FlexCDN 服务地址", + "access.form.flexcdn_server_url.placeholder": "请输入 FlexCDN 服务地址", "access.form.flexcdn_api_role.label": "FlexCDN 用户角色", "access.form.flexcdn_api_role.placeholder": "请选择 FlexCDN 用户角色", "access.form.flexcdn_api_role.option.user.label": "平台用户", @@ -207,9 +210,6 @@ "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", @@ -225,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 URL", - "access.form.goedge_api_url.placeholder": "请输入 GoEdge URL", + "access.form.goedge_server_url.label": "GoEdge 服务地址", + "access.form.goedge_server_url.placeholder": "请输入 GoEdge 服务地址", "access.form.goedge_api_role.label": "GoEdge 用户角色", "access.form.goedge_api_role.placeholder": "请选择 GoEdge 用户角色", "access.form.goedge_api_role.option.user.label": "平台用户", @@ -237,15 +237,15 @@ "access.form.goedge_access_key.label": "GoEdge AccessKey", "access.form.goedge_access_key.placeholder": "请输入 GoEdge AccessKey", "access.form.goedge_access_key.tooltip": "这是什么?请参阅 https://goedge.cloud/docs/API/Auth.md", - "access.form.goedge_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.goedge_allow_insecure_conns.switch.on": "允许", - "access.form.goedge_allow_insecure_conns.switch.off": "不允许", "access.form.googletrustservices_eab_kid.label": "ACME EAB KID", "access.form.googletrustservices_eab_kid.placeholder": "请输入 ACME EAB KID", "access.form.googletrustservices_eab_kid.tooltip": "这是什么?请参阅 https://cloud.google.com/certificate-manager/docs/public-ca-tutorial", "access.form.googletrustservices_eab_hmac_key.label": "ACME EAB HMAC Key", "access.form.googletrustservices_eab_hmac_key.placeholder": "请输入 ACME EAB HMAC Key", "access.form.googletrustservices_eab_hmac_key.tooltip": "这是什么?请参阅 https://cloud.google.com/certificate-manager/docs/public-ca-tutorial", + "access.form.hetzner_api_token.label": "Hetzner API Token", + "access.form.hetzner_api_token.placeholder": "请输入 Hetzner API Token", + "access.form.hetzner_api_token.tooltip": "这是什么?请参阅 https://docs.hetzner.com/cloud/api/getting-started/generating-api-token", "access.form.huaweicloud_access_key_id.label": "华为云 AccessKeyId", "access.form.huaweicloud_access_key_id.placeholder": "请输入华为云 AccessKeyId", "access.form.huaweicloud_access_key_id.tooltip": "这是什么?请参阅 https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html", @@ -264,8 +264,8 @@ "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_server_url.label": "LeCDN 服务地址", + "access.form.lecdn_server_url.placeholder": "请输入 LeCDN 服务地址", "access.form.lecdn_api_version.label": "LeCDN 版本", "access.form.lecdn_api_version.placeholder": "请选择 LeCDN 版本", "access.form.lecdn_api_role.label": "LeCDN 用户角色", @@ -276,9 +276,6 @@ "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 用户名", @@ -287,7 +284,7 @@ "access.form.mattermost_password.placeholder": "请输入 Mattermost 密码", "access.form.mattermost_default_channel_id.label": "默认的 Mattermost 频道 ID(可选)", "access.form.mattermost_default_channel_id.placeholder": "请输入默认的 Mattermost 频道 ID", - "access.form.mattermost_default_channel_id.tooltip": "如何获取频道 ID?从左侧边栏中选择目标频道,点击顶部的频道名称,选择“频道详情”,即可在弹出页面中直接看到频道 ID。", + "access.form.mattermost_default_channel_id.tooltip": "如何获取此参数?从左侧边栏中选择目标频道,点击顶部的频道名称,选择“频道详情”,即可在弹出页面中直接看到频道 ID。", "access.form.namecheap_username.label": "Namecheap 用户名", "access.form.namecheap_username.placeholder": "请输入 Namecheap 用户名", "access.form.namecheap_username.tooltip": "这是什么?请参阅 https://www.namecheap.com/support/api/intro/", @@ -324,25 +321,19 @@ "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 URL", - "access.form.powerdns_api_url.placeholder": "请输入 PowerDNS URL", + "access.form.powerdns_server_url.label": "PowerDNS 服务地址", + "access.form.powerdns_server_url.placeholder": "请输入 PowerDNS 服务地址", "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", - "access.form.powerdns_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.powerdns_allow_insecure_conns.switch.on": "允许", - "access.form.powerdns_allow_insecure_conns.switch.off": "不允许", - "access.form.proxmoxve_api_url.label": "Proxmox VE URL", - "access.form.proxmoxve_api_url.placeholder": "请输入 Proxmox VE URL", + "access.form.proxmoxve_server_url.label": "Proxmox VE 服务地址", + "access.form.proxmoxve_server_url.placeholder": "请输入 Proxmox VE 服务地址", "access.form.proxmoxve_api_token.label": "Proxmox VE API Token", "access.form.proxmoxve_api_token.placeholder": "请输入 Proxmox VE API Token", "access.form.proxmoxve_api_token.tooltip": "这是什么?请参阅 https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens", "access.form.proxmoxve_api_token_secret.label": "Proxmox VE API Token Secret(可选)", "access.form.proxmoxve_api_token_secret.placeholder": "请输入 Proxmox VE API Token Secret", "access.form.proxmoxve_api_token_secret.tooltip": "这是什么?请参阅 https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens", - "access.form.proxmoxve_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.proxmoxve_allow_insecure_conns.switch.on": "允许", - "access.form.proxmoxve_allow_insecure_conns.switch.off": "不允许", "access.form.qiniu_access_key.label": "七牛云 AccessKey", "access.form.qiniu_access_key.placeholder": "请输入七牛云 AccessKey", "access.form.qiniu_access_key.tooltip": "这是什么?请参阅 https://portal.qiniu.com/", @@ -352,25 +343,25 @@ "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_server_url.label": "耗子面板服务地址", + "access.form.ratpanel_server_url.placeholder": "请输入耗子面板服务地址", "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_server_url.label": "雷池服务地址", + "access.form.safeline_server_url.placeholder": "请输入雷池服务地址", "access.form.safeline_api_token.label": "雷池 API Token", "access.form.safeline_api_token.placeholder": "请输入雷池 API Token", "access.form.safeline_api_token.tooltip": "这是什么?请参阅 https://docs.waf-ce.chaitin.cn/zh/更多技术文档/OPENAPI", - "access.form.safeline_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.safeline_allow_insecure_conns.switch.on": "允许", - "access.form.safeline_allow_insecure_conns.switch.off": "不允许", + "access.form.slackbot_token.label": "Slack 机器人 Token", + "access.form.slackbot_token.placeholder": "请输入 Slack 机器人 Token", + "access.form.slackbot_token.tooltip": "这是什么?请参阅 https://docs.slack.dev/authentication/tokens#bot", + "access.form.slackbot_default_channel_id.label": "默认的 Slack 频道 ID(可选)", + "access.form.slackbot_default_channel_id.placeholder": "请输入默认的 Slack 频道 ID", + "access.form.slackbot_default_channel_id.tooltip": "如何获取此参数?请参阅 https://www.youtube.com/watch?v=Uz5Yi5C2pwQ", "access.form.ssh_host.label": "服务器地址", "access.form.ssh_host.placeholder": "请输入服务器地址", "access.form.ssh_port.label": "服务器端口", @@ -395,12 +386,12 @@ "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.tooltip": "如何获取机器人 API Token?请参阅 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.telegrambot_token.label": "Telegram 机器人 API Token", + "access.form.telegrambot_token.placeholder": "请输入 Telegram 机器人 API Token", + "access.form.telegrambot_token.tooltip": "如何获取此参数?请参阅 https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", + "access.form.telegrambot_default_chat_id.label": "默认的 Telegram 会话 ID(可选)", + "access.form.telegrambot_default_chat_id.placeholder": "请输入默认的 Telegram 会话 ID", + "access.form.telegrambot_default_chat_id.tooltip": "如何获取此参数?请参阅 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", @@ -416,6 +407,10 @@ "access.form.ucloud_project_id.label": "优刻得项目 ID(可选)", "access.form.ucloud_project_id.placeholder": "请输入优刻得项目 ID", "access.form.ucloud_project_id.tooltip": "这是什么?请参阅 https://console.ucloud.cn/uaccount/iam/project_manage", + "access.form.unicloud_username.label": "uniCloud 控制台账号", + "access.form.unicloud_username.placeholder": "请输入 uniCloud 控制台账号", + "access.form.unicloud_password.label": "uniCloud 控制台密码", + "access.form.unicloud_password.placeholder": "请输入 uniCloud 控制台密码", "access.form.upyun_username.label": "又拍云子账号用户名", "access.form.upyun_username.placeholder": "请输入又拍云子账号用户名", "access.form.upyun_username.tooltip": "这是什么?请参阅 https://console.upyun.com/account/subaccount/

请关闭该账号的二次登录验证。", @@ -466,9 +461,6 @@ "access.form.webhook_preset_data.option.pushplus.label": "PushPlus 推送加", "access.form.webhook_preset_data.option.serverchan.label": "Server 酱", "access.form.webhook_preset_data.option.common.label": "通用模板", - "access.form.webhook_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.webhook_allow_insecure_conns.switch.on": "允许", - "access.form.webhook_allow_insecure_conns.switch.off": "不允许", "access.form.wecombot_webhook_url.label": "企业微信群机器人 Webhook 地址", "access.form.wecombot_webhook_url.placeholder": "请输入企业微信群机器人 Webhook 地址", "access.form.wecombot_webhook_url.tooltip": "这是什么?请参阅 https://open.work.weixin.qq.com/help2/pc/18401", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index b9fb8777..27aa8d0e 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -58,14 +58,17 @@ "provider.ctcccloud": "联通云", "provider.cucccloud": "天翼云", "provider.desec": "deSEC", + "provider.digitalocean": "DigitalOcean", "provider.dingtalkbot": "钉钉群机器人", + "provider.discordbot": "Discord 机器人", "provider.dnsla": "DNS.LA", "provider.dogecloud": "多吉云", "provider.dogecloud.cdn": "多吉云 - 内容分发网络 CDN", + "provider.duckdns": "Duck DNS", "provider.dynv6": "dynv6", "provider.edgio": "Edgio", "provider.edgio.applications": "Edgio - Applications", - "provider.email": "邮件", + "provider.email": "邮件(SMTP)", "provider.fastly": "Fastly", "provider.flexcdn": "FlexCDN", "provider.gcore": "Gcore", @@ -74,6 +77,7 @@ "provider.godaddy": "GoDaddy", "provider.goedge": "GoEdge", "provider.googletrustservices": "Google Trust Services", + "provider.hetzner": "Hetzner", "provider.huaweicloud": "华为云", "provider.huaweicloud.cdn": "华为云 - 内容分发网络 CDN", "provider.huaweicloud.dns": "华为云 - 云解析 DNS", @@ -92,7 +96,7 @@ "provider.lecdn": "LeCDN", "provider.letsencrypt": "Let's Encrypt", "provider.letsencryptstaging": "Let's Encrypt 测试环境", - "provider.local": "本地部署", + "provider.local": "本地主机", "provider.mattermost": "Mattermost", "provider.namecheap": "Namecheap", "provider.namedotcom": "Name.com", @@ -114,9 +118,10 @@ "provider.ratpanel.console": "耗子面板 - 控制台", "provider.ratpanel.site": "耗子面板 - 网站", "provider.safeline": "雷池", - "provider.ssh": "SSH 部署", + "provider.slackbot": "Slack 机器人", + "provider.ssh": "远程主机(SSH)", "provider.sslcom": "SSL.com", - "provider.telegrambot": "Telegram 群机器人", + "provider.telegrambot": "Telegram 机器人", "provider.tencentcloud": "腾讯云", "provider.tencentcloud.cdn": "腾讯云 - 内容分发网络 CDN", "provider.tencentcloud.clb": "腾讯云 - 负载均衡 CLB", @@ -133,6 +138,8 @@ "provider.ucloud": "优刻得", "provider.ucloud.ucdn": "优刻得 - 内容分发 UCDN", "provider.ucloud.us3": "优刻得 - 对象存储 US3", + "provider.unicloud": "uniCloud (DCloud)", + "provider.unicloud.webhost": "uniCloud (DCloud) - 前端网页托管", "provider.upyun": "又拍云", "provider.upyun.cdn": "又拍云 - 云分发 CDN", "provider.upyun.file": "又拍云 - 云存储 USS", diff --git a/ui/src/i18n/locales/zh/nls.settings.json b/ui/src/i18n/locales/zh/nls.settings.json index cde8ec0f..b3da01aa 100644 --- a/ui/src/i18n/locales/zh/nls.settings.json +++ b/ui/src/i18n/locales/zh/nls.settings.json @@ -28,8 +28,8 @@ "settings.notification.channel.switch.off": "停用", "settings.notification.push_test.button": "推送测试消息", "settings.notification.push_test.pushed": "已推送", - "settings.notification.channel.form.bark_server_url.label": "服务器地址", - "settings.notification.channel.form.bark_server_url.placeholder": "请输入服务器地址", + "settings.notification.channel.form.bark_server_url.label": "服务地址", + "settings.notification.channel.form.bark_server_url.placeholder": "请输入服务地址", "settings.notification.channel.form.bark_server_url.tooltip": "这是什么?请参阅 https://bark.day.app/

为空时,将使用 Bark 默认服务器。", "settings.notification.channel.form.bark_device_key.label": "设备密钥", "settings.notification.channel.form.bark_device_key.placeholder": "请输入设备密钥", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index faf40816..6f8f09a6 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -108,7 +108,7 @@ "workflow_node.deploy.form.certificate.placeholder": "请选择待部署证书", "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_console_auto_restart.label": "部署后自动重启 1Panel 服务", "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": "替换指定网站的证书", @@ -330,7 +330,7 @@ "workflow_node.deploy.form.baishan_cdn_certificate_id.label": "白山云 CDN 原证书 ID(可选)", "workflow_node.deploy.form.baishan_cdn_certificate_id.placeholder": "请输入白山云 CDN 原证书 ID", "workflow_node.deploy.form.baishan_cdn_certificate_id.tooltip": "这是什么?请参阅 https://cdnx.console.baishan.com/#/cdn/cert

不填写时,将上传新证书;否则,将替换原证书。", - "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "部署后自动重启 1Panel 服务", + "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "部署后自动重启宝塔面板服务", "workflow_node.deploy.form.baotapanel_site_type.label": "宝塔面板网站类型", "workflow_node.deploy.form.baotapanel_site_type.placeholder": "请选择宝塔面板网站类型", "workflow_node.deploy.form.baotapanel_site_type.option.php.label": "PHP", @@ -695,9 +695,21 @@ "workflow_node.deploy.form.ucloud_us3_domain.label": "优刻得 US3 自定义域名", "workflow_node.deploy.form.ucloud_us3_domain.placeholder": "请输入优刻得 US3 自定义域名", "workflow_node.deploy.form.ucloud_us3_domain.tooltip": "这是什么?请参阅 https://console.ucloud.cn/ufile", + "workflow_node.deploy.form.unicloud_webhost.guide": "小贴士:由于 uniCloud 未提供相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇 uniCloud 接口变更,请到 GitHub 发起 Issue 告知。", + "workflow_node.deploy.form.unicloud_webhost_space_provider.label": "uniCloud 服务空间提供商", + "workflow_node.deploy.form.unicloud_webhost_space_provider.placeholder": "请选择 uniCloud 服务空间提供商", + "workflow_node.deploy.form.unicloud_webhost_space_provider.option.aliyun.label": "阿里云", + "workflow_node.deploy.form.unicloud_webhost_space_provider.option.tencent.label": "腾讯云", + "workflow_node.deploy.form.unicloud_webhost_space_id.label": "uniCloud 服务空间 ID", + "workflow_node.deploy.form.unicloud_webhost_space_id.placeholder": "请输入 uniCloud 服务空间 ID", + "workflow_node.deploy.form.unicloud_webhost_space_id.tooltip": "这是什么?请参阅 https://doc.dcloud.net.cn/uniCloud/concepts/space.html", + "workflow_node.deploy.form.unicloud_webhost_domain.label": "uniCloud 前端网页托管网站域名", + "workflow_node.deploy.form.unicloud_webhost_domain.placeholder": "请输入 uniCloud 前端网页托管网站域名", + "workflow_node.deploy.form.upyun_cdn.guide": "小贴士:由于又拍云未提供相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇又拍云接口变更,请到 GitHub 发起 Issue 告知。", "workflow_node.deploy.form.upyun_cdn_domain.label": "又拍云 CDN 加速域名", "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "请输入又拍云 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.upyun_cdn_domain.tooltip": "这是什么?请参阅 https://console.upyun.com/services/cdn/", + "workflow_node.deploy.form.upyun_file.guide": "小贴士:由于又拍云未提供相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇又拍云接口变更,请到 GitHub 发起 Issue 告知。", "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/", @@ -814,6 +826,9 @@ "workflow_node.notify.form.provider_access.placeholder": "请选择通知渠道授权", "workflow_node.notify.form.provider_access.button": "新建", "workflow_node.notify.form.params_config.label": "参数设置", + "workflow_node.notify.form.discordbot_channel_id.label": "Discord 频道 ID(可选)", + "workflow_node.notify.form.discordbot_channel_id.placeholder": "请输入 Discord 频道 ID 以覆盖默认值", + "workflow_node.notify.form.discordbot_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。", "workflow_node.notify.form.email_sender_address.label": "发送邮箱地址(可选)", "workflow_node.notify.form.email_sender_address.placeholder": "请输入发送邮箱地址以覆盖默认值", "workflow_node.notify.form.email_sender_address.tooltip": "不填写时,将使用所选通知渠道授权的默认发送邮箱地址。", @@ -823,9 +838,12 @@ "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_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.slackbot_channel_id.label": "Slack 频道 ID(可选)", + "workflow_node.notify.form.slackbot_channel_id.placeholder": "请输入 Slack 频道 ID 以覆盖默认值", + "workflow_node.notify.form.slackbot_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。", + "workflow_node.notify.form.telegrambot_chat_id.label": "Telegram 会话 ID(可选)", + "workflow_node.notify.form.telegrambot_chat_id.placeholder": "请输入 Telegram 会话 ID 以覆盖默认值", + "workflow_node.notify.form.telegrambot_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 回调数据。",