diff --git a/README.md b/README.md index 6f90fb32..0899200e 100644 --- a/README.md +++ b/README.md @@ -90,17 +90,23 @@ make local.run | :----------------------------------------------------------------- | :-------------------------------------- | | [阿里云](https://www.aliyun.com/) | | | [腾讯云](https://cloud.tencent.com/) | | +| [百度智能云](https://cloud.baidu.com/) | | | [华为云](https://www.huaweicloud.com/) | | | [火山引擎](https://www.volcengine.com/) | | +| [京东云](https://www.jdcloud.com/) | | | [AWS Route53](https://aws.amazon.com/route53/) | | | [Azure](https://azure.microsoft.com/) | | | [CloudFlare](https://www.cloudflare.com/) | | -| [ClouDNS](https://www.cloudns.net//) | | +| [ClouDNS](https://www.cloudns.net/) | | +| [DNS.LA](https://www.dns.la/) | | +| [Gcore](https://gcore.com/) | | | [GNAME](https://www.gname.com/) | | | [GoDaddy](https://www.godaddy.com/) | | | [Name.com](https://www.name.com/) | | +| [Namecheap](https://www.namecheap.com/) | | | [NameSilo](https://www.namesilo.com/) | | | [IBM NS1 Connect](https://www.ibm.com/cn-zh/products/ns1-connect/) | | +| [移动云](https://ecloud.10086.cn/) | | | [雨云](https://www.rainyun.com/) | | | [西部数码](https://www.west.cn/) | | | [PowerDNS](https://www.powerdns.com/) | | @@ -116,24 +122,30 @@ make local.run <summary>[展开查看]</summary> -| 提供商 | 备注 | -| :-------------------------------------- | :----------------------------------------------------------------------- | -| 本地部署 | 可部署到本地服务器 | -| SSH 部署 | 可部署到远程服务器(通过 SSH+SFTP/SCP) | -| Webhook 回调 | 可部署到 Webhook | -| [Kubernetes](https://kubernetes.io/) | 可部署到 Kubernetes Secret | -| [阿里云](https://www.aliyun.com/) | 可部署到阿里云 OSS、CDN、DCDN、ESA、SLB(CLB/ALB/NLB)、WAF、Live 等服务 | -| [腾讯云](https://cloud.tencent.com/) | 可部署到腾讯云 COS、CDN、ECDN、EdgeOne、CLB、CSS 等服务 | -| [百度智能云](https://cloud.baidu.com/) | 可部署到百度智能云 CDN 等服务 | -| [华为云](https://www.huaweicloud.com/) | 可部署到华为云 CDN、ELB 等服务 | -| [火山引擎](https://www.volcengine.com/) | 可部署到火山引擎 TOS、CDN、DCDN、CLB、Live 等服务 | -| [七牛云](https://www.qiniu.com/) | 可部署到七牛云 CDN、直播云等服务 | -| [多吉云](https://www.dogecloud.com/) | 可部署到多吉云 CDN | -| [优刻得](https://www.ucloud.cn/) | 可部署到优刻得 US3、UCDN 等服务 | -| [宝塔面板](https://www.bt.cn/) | 可部署到宝塔面板 | -| [AWS](https://aws.amazon.com/) | 可部署到 AWS CloudFront 等服务 | -| [BytePlus](https://www.byteplus.com/) | 可部署到 BytePlus CDN 等服务 | -| [Edgio](https://edg.io/) | 可部署到 Edgio Applications 等服务 | +| 提供商 | 备注 | +| :-------------------------------------- | :---------------------------------------------------------------------------- | +| 本地部署 | 可部署到本地服务器 | +| SSH 部署 | 可部署到远程服务器(通过 SSH+SFTP/SCP) | +| Webhook 回调 | 可部署到 Webhook | +| [Kubernetes](https://kubernetes.io/) | 可部署到 Kubernetes Secret | +| [阿里云](https://www.aliyun.com/) | 可部署到阿里云 OSS、CDN、DCDN、ESA、SLB(CLB/ALB/NLB)、WAF、Live、VOD 等服务 | +| [腾讯云](https://cloud.tencent.com/) | 可部署到腾讯云 COS、CDN、ECDN、EdgeOne、CLB、WAF、CSS、VOD 等服务 | +| [百度智能云](https://cloud.baidu.com/) | 可部署到百度智能云 CDN 等服务 | +| [华为云](https://www.huaweicloud.com/) | 可部署到华为云 CDN、ELB、WAF 等服务 | +| [火山引擎](https://www.volcengine.com/) | 可部署到火山引擎 TOS、CDN、DCDN、CLB、ImageX、Live 等服务 | +| [京东云](https://www.jdcloud.com/) | 可部署到京东云 CDN、ALB、视频直播、视频点播等服务 | +| [七牛云](https://www.qiniu.com/) | 可部署到七牛云 CDN、直播云等服务 | +| [白山云](https://www.baishan.com/) | 可部署到白山云 CDN | +| [多吉云](https://www.dogecloud.com/) | 可部署到多吉云 CDN | +| [优刻得](https://www.ucloud.cn/) | 可部署到优刻得 US3、UCDN 等服务 | +| [雷池](https://waf-ce.chaitin.cn/) | 可部署到雷池 WAF | +| [宝塔面板](https://www.bt.cn/) | 可部署到宝塔面板 | +| [AWS](https://aws.amazon.com/) | 可部署到 AWS CloudFront | +| [BytePlus](https://www.byteplus.com/) | 可部署到 BytePlus CDN | +| [CacheFly](https://www.cachefly.com/) | 可部署到 CacheFly CDN | +| [Cdnfly](https://www.cdnfly.cn/) | 可部署到 Cdnfly CDN | +| [Edgio](https://edg.io/) | 可部署到 Edgio Applications | +| [Gcore](https://gcore.com/) | 可部署到 Gcore CDN | </details> diff --git a/README_EN.md b/README_EN.md index d5300b07..b92fc82a 100644 --- a/README_EN.md +++ b/README_EN.md @@ -89,17 +89,23 @@ The following DNS providers are supported: | :----------------------------------------------------------- | :------------------------------------ | | [Alibaba Cloud](https://www.alibabacloud.com/) | | | [Tencent Cloud](https://www.tencentcloud.com/) | | +| [Baidu AI Cloud](https://intl.cloud.baidu.com/) | | | [Huawei Cloud](https://www.huaweicloud.com/) | | | [Volcengine](https://www.volcengine.com/) | | +| [JD Cloud](https://www.jdcloud.com/) | | | [AWS Route53](https://aws.amazon.com/route53/) | | | [Azure DNS](https://azure.microsoft.com/) | | | [CloudFlare](https://www.cloudflare.com/) | | -| [ClouDNS](https://www.cloudns.net//) | | +| [ClouDNS](https://www.cloudns.net/) | | +| [DNS.LA](https://www.dns.la/) | | +| [Gcore](https://gcore.com/) | | | [GNAME](https://www.gname.com/) | | | [GoDaddy](https://www.godaddy.com/) | | | [Name.com](https://www.name.com/) | | +| [Namecheap](https://www.namecheap.com/) | | | [NameSilo](https://www.namesilo.com/) | | | [IBM NS1 Connect](https://www.ibm.com/products/ns1-connect/) | | +| [CMCC Cloud](https://ecloud.10086.cn/) | | | [Rain Yun](https://www.rainyun.com/) | | | [West.cn](https://www.west.cn/) | | | [PowerDNS](https://www.powerdns.com/) | | @@ -115,24 +121,30 @@ The following hosting providers are supported: <summary>[Fold/Unfold to view ...]</summary> -| Provider | Remarks | -| :---------------------------------------------- | :------------------------------------------------------------------------------- | -| Local | Supports deployment to local servers | -| SSH | Supports deployment to remote servers (via SSH+SFTP/SCP) | -| Webhook | Supports deployment to Webhook | -| [Kubernetes](https://kubernetes.io/) | Supports deployment to Kubernetes Secret | -| [Alibaba Cloud](https://www.alibabacloud.com/) | Supports deployment to Alibaba Cloud OSS, CDN, DCDN, SLB(CLB/ALB/NLB), WAF, Live | -| [Tencent Cloud](https://www.tencentcloud.com/) | Supports deployment to Tencent Cloud COS, CDN, ECDN, EdgeOne, CLB, CSS | -| [Baidu AI Cloud](https://intl.cloud.baidu.com/) | Supports deployment to Baidu AI CLoud CDN | -| [Huawei Cloud](https://www.huaweicloud.com/) | Supports deployment to Huawei Cloud CDN, ELB | -| [Volcengine](https://www.volcengine.com/) | Supports deployment to Volcengine TOS, CDN, DCDN, CLB, Live | -| [Qiniu Cloud](https://www.qiniu.com/) | Supports deployment to Qiniu Cloud CDN, Pili | -| [Doge Cloud](https://www.dogecloud.com/) | Supports deployment to Doge Cloud CDN | -| [BaoTa Panel](https://www.bt.cn/) | Supports deployment to BaoTa Panel sites | -| [UCloud](https://www.ucloud-global.com/) | Supports deployment to UCloud US3, UCDN | -| [AWS](https://aws.amazon.com/) | Supports deployment to AWS CloudFront | -| [BytePlus](https://www.byteplus.com/) | Supports deployment to BytePlus CDN | -| [Edgio](https://edg.io/) | Supports deployment to Edgio Applications | +| Provider | Remarks | +| :---------------------------------------------- | :------------------------------------------------------------------------------------ | +| Local | Supports deployment to local servers | +| SSH | Supports deployment to remote servers (via SSH+SFTP/SCP) | +| Webhook | Supports deployment to Webhook | +| [Kubernetes](https://kubernetes.io/) | Supports deployment to Kubernetes Secret | +| [Alibaba Cloud](https://www.alibabacloud.com/) | Supports deployment to Alibaba Cloud OSS, CDN, DCDN, SLB(CLB/ALB/NLB), WAF, Live, VOD | +| [Tencent Cloud](https://www.tencentcloud.com/) | Supports deployment to Tencent Cloud COS, CDN, ECDN, EdgeOne, CLB, WAF, CSS, VOD | +| [Baidu AI Cloud](https://intl.cloud.baidu.com/) | Supports deployment to Baidu AI CLoud CDN | +| [Huawei Cloud](https://www.huaweicloud.com/) | Supports deployment to Huawei Cloud CDN, ELB, WAF | +| [Volcengine](https://www.volcengine.com/) | Supports deployment to Volcengine TOS, CDN, DCDN, CLB, ImageX, Live | +| [JD Cloud](https://www.jdcloud.com/) | Supports deployment to JD Cloud CDN, ALB, Live Video, VOD | +| [Qiniu Cloud](https://www.qiniu.com/) | Supports deployment to Qiniu Cloud CDN, Pili | +| [Baishan Cloud](https://intl.baishancloud.com/) | Supports deployment to Baishan Cloud CDN | +| [Doge Cloud](https://www.dogecloud.com/) | Supports deployment to Doge Cloud CDN | +| [UCloud](https://www.ucloud-global.com/) | Supports deployment to UCloud US3, UCDN | +| [SafeLine](https://waf.chaitin.com/) | Supports deployment to SafeLine WAF | +| [BaoTa Panel](https://www.bt.cn/) | Supports deployment to BaoTa Panel sites | +| [AWS](https://aws.amazon.com/) | Supports deployment to AWS CloudFront | +| [BytePlus](https://www.byteplus.com/) | Supports deployment to BytePlus CDN | +| [CacheFly](https://www.cachefly.com/) | Supports deployment to CacheFly CDN | +| [Cdnfly](https://www.cdnfly.cn/) | Supports deployment to Cdnfly CDN | +| [Edgio](https://edg.io/) | Supports deployment to Edgio Applications | +| [Gcore](https://gcore.com/) | Supports deployment to Gcore CDN | </details> diff --git a/go.mod b/go.mod index d3280f69..40efa5a4 100644 --- a/go.mod +++ b/go.mod @@ -19,12 +19,12 @@ require ( github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/aws/aws-sdk-go-v2/service/acm v1.30.18 github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.10 - github.com/baidubce/bce-sdk-go v0.9.216 + github.com/baidubce/bce-sdk-go v0.9.217 github.com/byteplus-sdk/byteplus-sdk-golang v1.0.41 - github.com/go-acme/lego/v4 v4.21.0 + github.com/go-acme/lego/v4 v4.22.2 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.135 + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.136 github.com/nikoksr/notify v1.3.0 github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 github.com/pkg/sftp v1.13.7 @@ -34,13 +34,13 @@ require ( github.com/qiniu/go-sdk/v7 v7.25.2 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1096 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1096 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1096 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1102 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1096 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1096 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1096 github.com/ucloud/ucloud-sdk-go v0.22.31 github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.9 - github.com/volcengine/volc-sdk-golang v1.0.194 + github.com/volcengine/volc-sdk-golang v1.0.195 github.com/volcengine/volcengine-go-sdk v1.0.180 golang.org/x/crypto v0.33.0 golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac @@ -57,11 +57,17 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect + github.com/G-Core/gcorelabscdn-go v1.0.26 // indirect github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1 // indirect github.com/alibabacloud-go/tea-fileform v1.1.1 // indirect github.com/alibabacloud-go/tea-oss-sdk v1.1.3 // indirect github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect + github.com/alibabacloud-go/tea-rpc v1.1.3 // indirect + github.com/alibabacloud-go/tea-rpc-utils v1.1.0 // indirect github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect + github.com/alibabacloud-go/vod-20170321 v1.0.1 // indirect + github.com/alibabacloud-go/vod-20170321/v4 v4.6.1 // indirect + github.com/avast/retry-go v3.0.0+incompatible // indirect github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1 // indirect github.com/blinkbean/dingtalk v1.1.3 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect @@ -75,12 +81,14 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.16.0 // indirect github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/jdcloud-api/jdcloud-sdk-go v1.62.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect @@ -94,7 +102,11 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1102 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1099 // indirect github.com/x448/float16 v0.8.4 // indirect + gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 // indirect + gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 // indirect go.mongodb.org/mongo-driver v1.17.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -201,3 +213,7 @@ require ( modernc.org/memory v1.8.2 // indirect modernc.org/sqlite v1.34.5 // indirect ) + +replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 => ./internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0 + +replace gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 => ./internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1 diff --git a/go.sum b/go.sum index e9239294..d15c396f 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,8 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0 github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/G-Core/gcorelabscdn-go v1.0.26 h1:22SqETUw64s+It/op1T7y3ukEOU62CJOsUcsfSkhvZs= +github.com/G-Core/gcorelabscdn-go v1.0.26/go.mod h1:iSGXaTvZBzDHQW+rKFS918BgFVpONcyLEijwh8WsXpE= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= @@ -169,6 +171,11 @@ github.com/alibabacloud-go/tea-oss-sdk v1.1.3 h1:EhAHI6edMeqgkZEqP7r4nc9iMWAUBKG github.com/alibabacloud-go/tea-oss-sdk v1.1.3/go.mod h1:yUnodpR3Bf2rudLE7V/Gft5txjJF30Pk+hH77K/Eab0= github.com/alibabacloud-go/tea-oss-utils v1.1.0 h1:y65crjjcZ2Pbb6UZtC2deuIZHDVTS3IaDWE7M9nVLRc= github.com/alibabacloud-go/tea-oss-utils v1.1.0/go.mod h1:PFCF12e9yEKyBUIn7X1IrF/pNjvxgkHy0CgxX4+xRuY= +github.com/alibabacloud-go/tea-rpc v1.1.3 h1:uuxAIT9PB6MMABQfV/EMSnREZjh629WXu+hmPNF1IAs= +github.com/alibabacloud-go/tea-rpc v1.1.3/go.mod h1:uwhvnxPK69jcAYkVyP1WCFhTh1oVLiibUseSUpC7L8g= +github.com/alibabacloud-go/tea-rpc-utils v1.1.0 h1:kIG7+9sMRaDzvCbXfowycEwFRdnLAglRFQ/dnc0/JNE= +github.com/alibabacloud-go/tea-rpc-utils v1.1.0/go.mod h1:rxGY+fLbm3Fj3oJpeU0hBTmz52Ux50nm7JL01tyPv9c= +github.com/alibabacloud-go/tea-utils v1.3.0/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= github.com/alibabacloud-go/tea-utils v1.3.6/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA= @@ -184,12 +191,17 @@ github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0= github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/alibabacloud-go/vod-20170321 v1.0.1 h1:ZiBggVoJegu0Q3iarHZyveTOJJE0kUym6RCSLM9epoc= +github.com/alibabacloud-go/vod-20170321 v1.0.1/go.mod h1:eKaYMCAd22pgBFMz0Ci/o2l+UJSrq24LLSF/XyaTiac= +github.com/alibabacloud-go/vod-20170321/v4 v4.6.1 h1:6JTNq23lMo3wOui5qjpUJu2VKBgSHR4ArMgbKDOej7Q= +github.com/alibabacloud-go/vod-20170321/v4 v4.6.1/go.mod h1:TkgLKMSLu0qZN8Qdcu8svfHREyI64kjFvrp/GhrD4VQ= github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.4 h1:Od0KgA73DyG9X2XFwuZZTkDv2pzA6B5mhYapyyca6QE= github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.4/go.mod h1:DohGoS8BnMxHXghHebtjPP7+GMdxPsRN19T3nn2HcCU= github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 h1:YBkf7H5CSgrlb3C1aWcpDt7Vk8UEGFPeD2OOirtt6IM= github.com/aliyun/alibaba-cloud-sdk-go v1.63.83/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/aliyun/credentials-go v1.1.0/go.mod h1:ZXrrxv386Mj6z8NpihLKpexQE550m7j3LlyCvYub9aE= github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM= @@ -205,6 +217,7 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= @@ -258,6 +271,8 @@ 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.216 h1:jRq4C1UGYcvHo6Gst2kuUzhWwJM6EqXCmhIsTKQvf4k= github.com/baidubce/bce-sdk-go v0.9.216/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= +github.com/baidubce/bce-sdk-go v0.9.217 h1:dbMeVzpr9BGItTFHB1s2KSrpz0ayJC1y366VUMmaF0k= +github.com/baidubce/bce-sdk-go v0.9.217/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= @@ -361,6 +376,8 @@ github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-acme/lego/v4 v4.21.0 h1:arEW+8o5p7VI8Bk1kr/PDlgD1DrxtTH1gJ4b7mehL8o= github.com/go-acme/lego/v4 v4.21.0/go.mod h1:HrSWzm3Ckj45Ie3i+p1zKVobbQoMOaGu9m4up0dUeDI= +github.com/go-acme/lego/v4 v4.22.2 h1:ck+HllWrV/rZGeYohsKQ5iKNnU/WAZxwOdiu6cxky+0= +github.com/go-acme/lego/v4 v4.22.2/go.mod h1:E2FndyI3Ekv0usNJt46mFb9LVpV/XBYT+4E3tz02Tzo= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -422,6 +439,8 @@ github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -557,6 +576,8 @@ github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKEN github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.135 h1:UbNMlPfh0GhRY3iVkvv4fXFJ+bLqXoVCwjqe6geFdPs= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.135/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.136 h1:T785NUg5245nWpPVHLVR8lBd+zGQYR14Vi/TCX1iu3A= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.136/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= @@ -569,6 +590,8 @@ github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/U github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jdcloud-api/jdcloud-sdk-go v1.62.0 h1:uPfyOSY16mBrhggriDNeySFB4ZkzMMXpNac2P0fbDRw= +github.com/jdcloud-api/jdcloud-sdk-go v1.62.0/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -826,6 +849,10 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1096/go.mod h1 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1096 h1:DMokC7T0UF8wMfT1kD+mX3M+hc2C06gmFvQ9gsfRPmI= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1096/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1099 h1:4fQ53ORk6Eayw1H2kg43PoBnUuhGR6WRG6rtec/i3oI= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1099/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1102 h1:DxsNhw67OHyQME20IULmi8lgNY9MHuQ+qS1XH1/yTvM= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1102/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 h1:kwctN0WQYt8/iKP+iRCTCwdzEMIXsXklbRIib5rjeQ8= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084/go.mod h1:qE67ApiBzeRvzeDsV+GxyIDbVIDemsKpHXllQATz/Vw= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1096 h1:h9FP40Ycg45egJlZcjbLyc4IUeFoq+wSpR43sHMALtM= @@ -834,6 +861,10 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1096 h1:7ZmPus github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1096/go.mod h1:aMpGcDskqqhXtfMaeo2egO61tgh/zt07L1ohSPwmjWk= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1096 h1:N62IFKL1ZRNQ7WPLNn8x9eYnwM4lOUIVY3buW6kbGtg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1096/go.mod h1:4PZRRpZp+jvYBUbUajsoZREnk7sJXMnPAiGB4IX8IkM= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1102 h1:B0mJk0ojVOFCMLrBoxLNVgrGih11EezTekRffkACCAY= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1102/go.mod h1:0yyQ1r35jteb5DV4mcJZ5uh9NStWzjMYz9iSMnDMdJA= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1099 h1:kD+8RKF0uJCr7VaurAUA11NNAoln0HaagMCgQV6EnUw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1099/go.mod h1:ellbjD8eHKHS4ixscLdiPJI8QoFIk0YNEgaDjxXMECM= 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= @@ -850,6 +881,8 @@ github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.9/go.mod h1:IrjK84IJJTuOZOTMv/P1 github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU= github.com/volcengine/volc-sdk-golang v1.0.194 h1:3o0INQzdtYJWvdGrtX02booCqPL5TsWSq2W1Ur7Bzlo= github.com/volcengine/volc-sdk-golang v1.0.194/go.mod h1:u0VtPvlXWpXDTmc9IHkaW1q+5Jjwus4oAqRhNMDRInE= +github.com/volcengine/volc-sdk-golang v1.0.195 h1:hKX4pBhmKcB3652BTdcAmtgizEPBnoQUpTM+j5blMA4= +github.com/volcengine/volc-sdk-golang v1.0.195/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= github.com/volcengine/volcengine-go-sdk v1.0.180 h1:lzcNlaxeGIUdXgDuVH7KJwZYZjIZzaCAYPDh91htU6U= github.com/volcengine/volcengine-go-sdk v1.0.180/go.mod h1:gfEDc1s7SYaGoY+WH2dRrS3qiuDJMkwqyfXWCa7+7oA= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index d992e9c1..cc8a3cfd 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -6,23 +6,29 @@ import ( "github.com/go-acme/lego/v4/challenge" "github.com/usual2970/certimate/internal/domain" - providerACMEHttpReq "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq" - providerAliyun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun" - providerAWSRoute53 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53" - providerAzureDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns" - providerCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare" - providerClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns" - providerGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname" - providerGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy" - providerHuaweiCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud" - providerNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom" - providerNameSilo "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo" - providerNS1 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1" - providerPowerDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns" - providerRainYun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun" - providerTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud" - providerVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine" - providerWestcn "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn" + pACMEHttpReq "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq" + pAliyun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun" + pAWSRoute53 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53" + pAzureDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns" + pBaiduCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud" + pCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare" + pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns" + pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud" + pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla" + 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" + 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" + pNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom" + pNameSilo "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo" + pNS1 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1" + pPowerDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns" + pRainYun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun" + pTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud" + pVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine" + pWestcn "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn" "github.com/usual2970/certimate/internal/pkg/utils/maps" ) @@ -39,7 +45,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerACMEHttpReq.NewChallengeProvider(&providerACMEHttpReq.ACMEHttpReqApplicantConfig{ + applicant, err := pACMEHttpReq.NewChallengeProvider(&pACMEHttpReq.ChallengeProviderConfig{ Endpoint: access.Endpoint, Mode: access.Mode, Username: access.Username, @@ -56,7 +62,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerAliyun.NewChallengeProvider(&providerAliyun.AliyunApplicantConfig{ + applicant, err := pAliyun.NewChallengeProvider(&pAliyun.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, DnsPropagationTimeout: options.DnsPropagationTimeout, @@ -72,7 +78,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerAWSRoute53.NewChallengeProvider(&providerAWSRoute53.AWSRoute53ApplicantConfig{ + applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"), @@ -83,14 +89,14 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } - case domain.ApplyDNSProviderTypeAzureDNS: + case domain.ApplyDNSProviderTypeAzure, domain.ApplyDNSProviderTypeAzureDNS: { access := domain.AccessConfigForAzure{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerAzureDNS.NewChallengeProvider(&providerAzureDNS.AzureDNSApplicantConfig{ + applicant, err := pAzureDNS.NewChallengeProvider(&pAzureDNS.ChallengeProviderConfig{ TenantId: access.TenantId, ClientId: access.ClientId, ClientSecret: access.ClientSecret, @@ -101,6 +107,22 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } + case domain.ApplyDNSProviderTypeBaiduCloud, domain.ApplyDNSProviderTypeBaiduCloudDNS: + { + access := domain.AccessConfigForBaiduCloud{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pBaiduCloud.NewChallengeProvider(&pBaiduCloud.ChallengeProviderConfig{ + AccessKeyId: access.AccessKeyId, + SecretAccessKey: access.SecretAccessKey, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ApplyDNSProviderTypeCloudflare: { access := domain.AccessConfigForCloudflare{} @@ -108,7 +130,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerCloudflare.NewChallengeProvider(&providerCloudflare.CloudflareApplicantConfig{ + applicant, err := pCloudflare.NewChallengeProvider(&pCloudflare.ChallengeProviderConfig{ DnsApiToken: access.DnsApiToken, DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, @@ -123,7 +145,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerClouDNS.NewChallengeProvider(&providerClouDNS.ClouDNSApplicantConfig{ + applicant, err := pClouDNS.NewChallengeProvider(&pClouDNS.ChallengeProviderConfig{ AuthId: access.AuthId, AuthPassword: access.AuthPassword, DnsPropagationTimeout: options.DnsPropagationTimeout, @@ -132,6 +154,53 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } + case domain.ApplyDNSProviderTypeCMCCCloud: + { + access := domain.AccessConfigForCMCCCloud{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pCMCCCloud.NewChallengeProvider(&pCMCCCloud.ChallengeProviderConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + + case domain.ApplyDNSProviderTypeDNSLA: + { + access := domain.AccessConfigForDNSLA{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pDNSLA.NewChallengeProvider(&pDNSLA.ChallengeProviderConfig{ + ApiId: access.ApiId, + ApiSecret: access.ApiSecret, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + + case domain.ApplyDNSProviderTypeGcore: + { + access := domain.AccessConfigForGcore{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pGcore.NewChallengeProvider(&pGcore.ChallengeProviderConfig{ + ApiToken: access.ApiToken, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ApplyDNSProviderTypeGname: { access := domain.AccessConfigForGname{} @@ -139,7 +208,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerGname.NewChallengeProvider(&providerGname.GnameApplicantConfig{ + applicant, err := pGname.NewChallengeProvider(&pGname.ChallengeProviderConfig{ AppId: access.AppId, AppKey: access.AppKey, DnsPropagationTimeout: options.DnsPropagationTimeout, @@ -155,7 +224,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerGoDaddy.NewChallengeProvider(&providerGoDaddy.GoDaddyApplicantConfig{ + applicant, err := pGoDaddy.NewChallengeProvider(&pGoDaddy.ChallengeProviderConfig{ ApiKey: access.ApiKey, ApiSecret: access.ApiSecret, DnsPropagationTimeout: options.DnsPropagationTimeout, @@ -171,7 +240,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerHuaweiCloud.NewChallengeProvider(&providerHuaweiCloud.HuaweiCloudApplicantConfig{ + applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"), @@ -181,6 +250,39 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } + case domain.ApplyDNSProviderTypeJDCloud, domain.ApplyDNSProviderTypeJDCloudDNS: + { + access := domain.AccessConfigForJDCloud{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + RegionId: maps.GetValueAsString(options.ProviderApplyConfig, "region_id"), + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + + case domain.ApplyDNSProviderTypeNamecheap: + { + access := domain.AccessConfigForNamecheap{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pNamecheap.NewChallengeProvider(&pNamecheap.ChallengeProviderConfig{ + Username: access.Username, + ApiKey: access.ApiKey, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ApplyDNSProviderTypeNameDotCom: { access := domain.AccessConfigForNameDotCom{} @@ -188,7 +290,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerNameDotCom.NewChallengeProvider(&providerNameDotCom.NameDotComApplicantConfig{ + applicant, err := pNameDotCom.NewChallengeProvider(&pNameDotCom.ChallengeProviderConfig{ Username: access.Username, ApiToken: access.ApiToken, DnsPropagationTimeout: options.DnsPropagationTimeout, @@ -204,7 +306,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerNameSilo.NewChallengeProvider(&providerNameSilo.NameSiloApplicantConfig{ + applicant, err := pNameSilo.NewChallengeProvider(&pNameSilo.ChallengeProviderConfig{ ApiKey: access.ApiKey, DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, @@ -219,7 +321,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerNS1.NewChallengeProvider(&providerNS1.NS1ApplicantConfig{ + applicant, err := pNS1.NewChallengeProvider(&pNS1.ChallengeProviderConfig{ ApiKey: access.ApiKey, DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, @@ -234,7 +336,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerPowerDNS.NewChallengeProvider(&providerPowerDNS.PowerDNSApplicantConfig{ + applicant, err := pPowerDNS.NewChallengeProvider(&pPowerDNS.ChallengeProviderConfig{ ApiUrl: access.ApiUrl, ApiKey: access.ApiKey, DnsPropagationTimeout: options.DnsPropagationTimeout, @@ -250,7 +352,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerRainYun.NewChallengeProvider(&providerRainYun.RainYunApplicantConfig{ + applicant, err := pRainYun.NewChallengeProvider(&pRainYun.ChallengeProviderConfig{ ApiKey: access.ApiKey, DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, @@ -265,7 +367,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerTencentCloud.NewChallengeProvider(&providerTencentCloud.TencentCloudApplicantConfig{ + applicant, err := pTencentCloud.NewChallengeProvider(&pTencentCloud.ChallengeProviderConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, DnsPropagationTimeout: options.DnsPropagationTimeout, @@ -281,7 +383,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerVolcEngine.NewChallengeProvider(&providerVolcEngine.VolcEngineApplicantConfig{ + applicant, err := pVolcEngine.NewChallengeProvider(&pVolcEngine.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, DnsPropagationTimeout: options.DnsPropagationTimeout, @@ -297,7 +399,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - applicant, err := providerWestcn.NewChallengeProvider(&providerWestcn.WestcnApplicantConfig{ + applicant, err := pWestcn.NewChallengeProvider(&pWestcn.ChallengeProviderConfig{ Username: access.Username, ApiPassword: access.ApiPassword, DnsPropagationTimeout: options.DnsPropagationTimeout, diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index e64c8a88..f95442a6 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -42,7 +42,7 @@ func NewWithDeployNode(node *domain.WorkflowNode, certdata struct { return nil, fmt.Errorf("failed to unmarshal access config: %w", err) } - deployer, logger, err := createDeployer(&deployerOptions{ + deployer, err := createDeployer(&deployerOptions{ Provider: domain.DeployProviderType(nodeConfig.Provider), ProviderAccessConfig: accessConfig, ProviderDeployConfig: nodeConfig.ProviderConfig, @@ -52,7 +52,7 @@ func NewWithDeployNode(node *domain.WorkflowNode, certdata struct { } return &proxyDeployer{ - logger: logger, + logger: logger.NewNilLogger(), deployer: deployer, deployCertificate: certdata.Certificate, deployPrivateKey: certdata.PrivateKey, diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index a9878f8a..6a8a0b6c 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -6,162 +6,183 @@ import ( "github.com/usual2970/certimate/internal/domain" "github.com/usual2970/certimate/internal/pkg/core/deployer" - providerAliyunALB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-alb" - providerAliyunCASDeploy "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cas-deploy" - providerAliyunCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cdn" - providerAliyunCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-clb" - providerAliyunDCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-dcdn" - providerAliyunESA "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-esa" - providerAliyunLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-live" - providerAliyunNLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-nlb" - providerAliyunOSS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-oss" - providerAliyunWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-waf" - providerAWSCloudFront "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-cloudfront" - providerBaiduCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-cdn" - providerBaotaPanelSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-site" - providerBytePlusCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/byteplus-cdn" - providerDogeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/dogecloud-cdn" - providerEdgioApplications "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/edgio-applications" - providerHuaweiCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-cdn" - providerHuaweiCloudELB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-elb" - providerK8sSecret "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/k8s-secret" - providerLocal "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/local" - providerQiniuCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/qiniu-cdn" - providerQiniuPili "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/qiniu-pili" - providerSSH "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ssh" - providerTencentCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-cdn" - providerTencentCloudCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-clb" - providerTencentCloudCOS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-cos" - providerTencentCloudCSS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-css" - providerTencentCloudECDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-ecdn" - providerTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-eo" - providerTencentCloudSSLDeploy "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy" - providerUCloudUCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-ucdn" - providerUCloudUS3 "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-us3" - providerVolcEngineCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-cdn" - providerVolcEngineCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-clb" - providerVolcEngineDCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-dcdn" - providerVolcEngineLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-live" - providerVolcEngineTOS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-tos" - providerWebhook "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/webhook" - "github.com/usual2970/certimate/internal/pkg/core/logger" + pAliyunALB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-alb" + pAliyunCASDeploy "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cas-deploy" + pAliyunCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cdn" + pAliyunCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-clb" + pAliyunDCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-dcdn" + pAliyunESA "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-esa" + pAliyunLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-live" + pAliyunNLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-nlb" + pAliyunOSS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-oss" + pAliyunVOD "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-vod" + pAliyunWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-waf" + pAWSCloudFront "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-cloudfront" + pBaiduCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-cdn" + pBaishanCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baishan-cdn" + pBaotaPanelConsole "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-console" + pBaotaPanelSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-site" + pBytePlusCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/byteplus-cdn" + pCacheFly "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cachefly" + pCdnfly "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cdnfly" + pDogeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/dogecloud-cdn" + pEdgioApplications "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/edgio-applications" + pGcoreCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/gcore-cdn" + pHuaweiCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-cdn" + pHuaweiCloudELB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-elb" + pHuaweiCloudWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-waf" + pJDCloudALB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-alb" + pJDCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-cdn" + pJDCloudLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-live" + pJDCloudVOD "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-vod" + pK8sSecret "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/k8s-secret" + pLocal "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/local" + pQiniuCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/qiniu-cdn" + pQiniuPili "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/qiniu-pili" + pSafeLine "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/safeline" + pSSH "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ssh" + pTencentCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-cdn" + pTencentCloudCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-clb" + pTencentCloudCOS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-cos" + pTencentCloudCSS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-css" + pTencentCloudECDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-ecdn" + pTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-eo" + pTencentCloudSSLDeploy "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy" + pTencentCloudVOD "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-vod" + 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" + pVolcEngineCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-cdn" + pVolcEngineCLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-clb" + pVolcEngineDCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-dcdn" + pVolcEngineImageX "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-imagex" + pVolcEngineLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-live" + pVolcEngineTOS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-tos" + pWebhook "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/webhook" "github.com/usual2970/certimate/internal/pkg/utils/maps" "github.com/usual2970/certimate/internal/pkg/utils/slices" ) -func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, error) { - logger := logger.NewDefaultLogger() - +func createDeployer(options *deployerOptions) (deployer.Deployer, error) { /* 注意:如果追加新的常量值,请保持以 ASCII 排序。 NOTICE: If you add new constant, please keep ASCII order. */ switch options.Provider { - case domain.DeployProviderTypeAliyunALB, domain.DeployProviderTypeAliyunCASDeploy, domain.DeployProviderTypeAliyunCDN, domain.DeployProviderTypeAliyunCLB, domain.DeployProviderTypeAliyunDCDN, domain.DeployProviderTypeAliyunESA, domain.DeployProviderTypeAliyunLive, domain.DeployProviderTypeAliyunNLB, domain.DeployProviderTypeAliyunOSS, domain.DeployProviderTypeAliyunWAF: + case domain.DeployProviderTypeAliyunALB, domain.DeployProviderTypeAliyunCASDeploy, domain.DeployProviderTypeAliyunCDN, domain.DeployProviderTypeAliyunCLB, domain.DeployProviderTypeAliyunDCDN, domain.DeployProviderTypeAliyunESA, domain.DeployProviderTypeAliyunLive, domain.DeployProviderTypeAliyunNLB, domain.DeployProviderTypeAliyunOSS, domain.DeployProviderTypeAliyunVOD, domain.DeployProviderTypeAliyunWAF: { access := domain.AccessConfigForAliyun{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } switch options.Provider { case domain.DeployProviderTypeAliyunALB: - deployer, err := providerAliyunALB.NewWithLogger(&providerAliyunALB.AliyunALBDeployerConfig{ + deployer, err := pAliyunALB.NewDeployer(&pAliyunALB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), - ResourceType: providerAliyunALB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + ResourceType: pAliyunALB.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"), ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeAliyunCASDeploy: - deployer, err := providerAliyunCASDeploy.NewWithLogger(&providerAliyunCASDeploy.AliyunCASDeployDeployerConfig{ + deployer, err := pAliyunCASDeploy.NewDeployer(&pAliyunCASDeploy.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), ResourceIds: slices.Filter(strings.Split(maps.GetValueAsString(options.ProviderDeployConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), ContactIds: slices.Filter(strings.Split(maps.GetValueAsString(options.ProviderDeployConfig, "contactIds"), ";"), func(s string) bool { return s != "" }), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeAliyunCDN: - deployer, err := providerAliyunCDN.NewWithLogger(&providerAliyunCDN.AliyunCDNDeployerConfig{ + deployer, err := pAliyunCDN.NewDeployer(&pAliyunCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeAliyunCLB: - deployer, err := providerAliyunCLB.NewWithLogger(&providerAliyunCLB.AliyunCLBDeployerConfig{ + deployer, err := pAliyunCLB.NewDeployer(&pAliyunCLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), - ResourceType: providerAliyunCLB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + ResourceType: pAliyunCLB.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"), ListenerPort: maps.GetValueOrDefaultAsInt32(options.ProviderDeployConfig, "listenerPort", 443), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeAliyunDCDN: - deployer, err := providerAliyunDCDN.NewWithLogger(&providerAliyunDCDN.AliyunDCDNDeployerConfig{ + deployer, err := pAliyunDCDN.NewDeployer(&pAliyunDCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeAliyunESA: - deployer, err := providerAliyunESA.NewWithLogger(&providerAliyunESA.AliyunESADeployerConfig{ + deployer, err := pAliyunESA.NewDeployer(&pAliyunESA.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), SiteId: maps.GetValueAsInt64(options.ProviderDeployConfig, "siteId"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeAliyunLive: - deployer, err := providerAliyunLive.NewWithLogger(&providerAliyunLive.AliyunLiveDeployerConfig{ + deployer, err := pAliyunLive.NewDeployer(&pAliyunLive.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeAliyunNLB: - deployer, err := providerAliyunNLB.NewWithLogger(&providerAliyunNLB.AliyunNLBDeployerConfig{ + deployer, err := pAliyunNLB.NewDeployer(&pAliyunNLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), - ResourceType: providerAliyunNLB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + ResourceType: pAliyunNLB.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"), ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeAliyunOSS: - deployer, err := providerAliyunOSS.NewWithLogger(&providerAliyunOSS.AliyunOSSDeployerConfig{ + deployer, err := pAliyunOSS.NewDeployer(&pAliyunOSS.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), Bucket: maps.GetValueAsString(options.ProviderDeployConfig, "bucket"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err + + case domain.DeployProviderTypeAliyunVOD: + deployer, err := pAliyunVOD.NewDeployer(&pAliyunVOD.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + }) + return deployer, err case domain.DeployProviderTypeAliyunWAF: - deployer, err := providerAliyunWAF.NewWithLogger(&providerAliyunWAF.AliyunWAFDeployerConfig{ + deployer, err := pAliyunWAF.NewDeployer(&pAliyunWAF.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), InstanceId: maps.GetValueAsString(options.ProviderDeployConfig, "instanceId"), - }, logger) - return deployer, logger, err + }) + return deployer, err default: break @@ -172,18 +193,18 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, { access := domain.AccessConfigForAWS{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } switch options.Provider { case domain.DeployProviderTypeAWSCloudFront: - deployer, err := providerAWSCloudFront.NewWithLogger(&providerAWSCloudFront.AWSCloudFrontDeployerConfig{ + deployer, err := pAWSCloudFront.NewDeployer(&pAWSCloudFront.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), DistributionId: maps.GetValueAsString(options.ProviderDeployConfig, "distributionId"), - }, logger) - return deployer, logger, err + }) + return deployer, err default: break @@ -194,117 +215,263 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, { access := domain.AccessConfigForBaiduCloud{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } switch options.Provider { case domain.DeployProviderTypeBaiduCloudCDN: - deployer, err := providerBaiduCloudCDN.NewWithLogger(&providerBaiduCloudCDN.BaiduCloudCDNDeployerConfig{ + deployer, err := pBaiduCloudCDN.NewDeployer(&pBaiduCloudCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err default: break } } - case domain.DeployProviderTypeBaotaPanelSite: + case domain.DeployProviderTypeBaishanCDN: + { + access := domain.AccessConfigForBaishan{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + switch options.Provider { + case domain.DeployProviderTypeBaishanCDN: + deployer, err := pBaishanCDN.NewDeployer(&pBaishanCDN.DeployerConfig{ + ApiToken: access.ApiToken, + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + }) + return deployer, err + + default: + break + } + } + + case domain.DeployProviderTypeBaotaPanelConsole, domain.DeployProviderTypeBaotaPanelSite: { access := domain.AccessConfigForBaotaPanel{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - deployer, err := providerBaotaPanelSite.NewWithLogger(&providerBaotaPanelSite.BaotaPanelSiteDeployerConfig{ - ApiUrl: access.ApiUrl, - ApiKey: access.ApiKey, - SiteName: maps.GetValueAsString(options.ProviderDeployConfig, "siteName"), - }, logger) - return deployer, logger, err + switch options.Provider { + case domain.DeployProviderTypeBaotaPanelConsole: + deployer, err := pBaotaPanelConsole.NewDeployer(&pBaotaPanelConsole.DeployerConfig{ + ApiUrl: access.ApiUrl, + ApiKey: access.ApiKey, + AutoRestart: maps.GetValueAsBool(options.ProviderDeployConfig, "autoRestart"), + }) + return deployer, err + + case domain.DeployProviderTypeBaotaPanelSite: + deployer, err := pBaotaPanelSite.NewDeployer(&pBaotaPanelSite.DeployerConfig{ + ApiUrl: access.ApiUrl, + ApiKey: access.ApiKey, + SiteType: maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "siteType", "other"), + SiteName: maps.GetValueAsString(options.ProviderDeployConfig, "siteName"), + SiteNames: slices.Filter(strings.Split(maps.GetValueAsString(options.ProviderDeployConfig, "siteNames"), ";"), func(s string) bool { return s != "" }), + }) + return deployer, err + + default: + break + } } case domain.DeployProviderTypeBytePlusCDN: { access := domain.AccessConfigForBytePlus{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } switch options.Provider { case domain.DeployProviderTypeBytePlusCDN: - deployer, err := providerBytePlusCDN.NewWithLogger(&providerBytePlusCDN.BytePlusCDNDeployerConfig{ + deployer, err := pBytePlusCDN.NewDeployer(&pBytePlusCDN.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err default: break } } + case domain.DeployProviderTypeCacheFly: + { + access := domain.AccessConfigForCacheFly{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pCacheFly.NewDeployer(&pCacheFly.DeployerConfig{ + ApiToken: access.ApiToken, + }) + return deployer, err + } + + case domain.DeployProviderTypeCdnfly: + { + access := domain.AccessConfigForCdnfly{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pCdnfly.NewDeployer(&pCdnfly.DeployerConfig{ + ApiUrl: access.ApiUrl, + ApiKey: access.ApiKey, + ApiSecret: access.ApiSecret, + ResourceType: pCdnfly.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + SiteId: maps.GetValueAsString(options.ProviderDeployConfig, "siteId"), + CertificateId: maps.GetValueAsString(options.ProviderDeployConfig, "certificateId"), + }) + return deployer, err + } + case domain.DeployProviderTypeDogeCloudCDN: { access := domain.AccessConfigForDogeCloud{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - deployer, err := providerDogeCDN.NewWithLogger(&providerDogeCDN.DogeCloudCDNDeployerConfig{ + deployer, err := pDogeCDN.NewDeployer(&pDogeCDN.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err } case domain.DeployProviderTypeEdgioApplications: { access := domain.AccessConfigForEdgio{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - deployer, err := providerEdgioApplications.NewWithLogger(&providerEdgioApplications.EdgioApplicationsDeployerConfig{ + deployer, err := pEdgioApplications.NewDeployer(&pEdgioApplications.DeployerConfig{ ClientId: access.ClientId, ClientSecret: access.ClientSecret, EnvironmentId: maps.GetValueAsString(options.ProviderDeployConfig, "environmentId"), - }, logger) - return deployer, logger, err + }) + return deployer, err } - case domain.DeployProviderTypeHuaweiCloudCDN, domain.DeployProviderTypeHuaweiCloudELB: + case domain.DeployProviderTypeGcoreCDN: + { + access := domain.AccessConfigForGcore{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + switch options.Provider { + case domain.DeployProviderTypeGcoreCDN: + deployer, err := pGcoreCDN.NewDeployer(&pGcoreCDN.DeployerConfig{ + ApiToken: access.ApiToken, + ResourceId: maps.GetValueAsInt64(options.ProviderDeployConfig, "resourceId"), + }) + return deployer, err + + default: + break + } + } + + case domain.DeployProviderTypeHuaweiCloudCDN, domain.DeployProviderTypeHuaweiCloudELB, domain.DeployProviderTypeHuaweiCloudWAF: { access := domain.AccessConfigForHuaweiCloud{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } switch options.Provider { case domain.DeployProviderTypeHuaweiCloudCDN: - deployer, err := providerHuaweiCloudCDN.NewWithLogger(&providerHuaweiCloudCDN.HuaweiCloudCDNDeployerConfig{ + deployer, err := pHuaweiCloudCDN.NewDeployer(&pHuaweiCloudCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeHuaweiCloudELB: - deployer, err := providerHuaweiCloudELB.NewWithLogger(&providerHuaweiCloudELB.HuaweiCloudELBDeployerConfig{ + deployer, err := pHuaweiCloudELB.NewDeployer(&pHuaweiCloudELB.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), - ResourceType: providerHuaweiCloudELB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + ResourceType: pHuaweiCloudELB.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), CertificateId: maps.GetValueAsString(options.ProviderDeployConfig, "certificateId"), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"), ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"), - }, logger) - return deployer, logger, err + }) + return deployer, err + + case domain.DeployProviderTypeHuaweiCloudWAF: + deployer, err := pHuaweiCloudWAF.NewDeployer(&pHuaweiCloudWAF.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + SecretAccessKey: access.SecretAccessKey, + Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), + ResourceType: pHuaweiCloudWAF.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + CertificateId: maps.GetValueAsString(options.ProviderDeployConfig, "certificateId"), + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + }) + return deployer, err + + default: + break + } + } + + case domain.DeployProviderTypeJDCloudALB, domain.DeployProviderTypeJDCloudCDN, domain.DeployProviderTypeJDCloudLive, domain.DeployProviderTypeJDCloudVOD: + { + access := domain.AccessConfigForJDCloud{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + switch options.Provider { + case domain.DeployProviderTypeJDCloudALB: + deployer, err := pJDCloudALB.NewDeployer(&pJDCloudALB.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + RegionId: maps.GetValueAsString(options.ProviderDeployConfig, "regionId"), + ResourceType: pJDCloudALB.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"), + ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"), + }) + return deployer, err + + case domain.DeployProviderTypeJDCloudCDN: + deployer, err := pJDCloudCDN.NewDeployer(&pJDCloudCDN.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + }) + return deployer, err + + case domain.DeployProviderTypeJDCloudLive: + deployer, err := pJDCloudLive.NewDeployer(&pJDCloudLive.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + }) + return deployer, err + + case domain.DeployProviderTypeJDCloudVOD: + deployer, err := pJDCloudVOD.NewDeployer(&pJDCloudVOD.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + }) + return deployer, err default: break @@ -313,77 +480,93 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, case domain.DeployProviderTypeLocal: { - deployer, err := providerLocal.NewWithLogger(&providerLocal.LocalDeployerConfig{ - ShellEnv: providerLocal.ShellEnvType(maps.GetValueAsString(options.ProviderDeployConfig, "shellEnv")), + deployer, err := pLocal.NewDeployer(&pLocal.DeployerConfig{ + ShellEnv: pLocal.ShellEnvType(maps.GetValueAsString(options.ProviderDeployConfig, "shellEnv")), PreCommand: maps.GetValueAsString(options.ProviderDeployConfig, "preCommand"), PostCommand: maps.GetValueAsString(options.ProviderDeployConfig, "postCommand"), - OutputFormat: providerLocal.OutputFormatType(maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "format", string(providerLocal.OUTPUT_FORMAT_PEM))), + OutputFormat: pLocal.OutputFormatType(maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "format", string(pLocal.OUTPUT_FORMAT_PEM))), OutputCertPath: maps.GetValueAsString(options.ProviderDeployConfig, "certPath"), OutputKeyPath: maps.GetValueAsString(options.ProviderDeployConfig, "keyPath"), PfxPassword: maps.GetValueAsString(options.ProviderDeployConfig, "pfxPassword"), JksAlias: maps.GetValueAsString(options.ProviderDeployConfig, "jksAlias"), JksKeypass: maps.GetValueAsString(options.ProviderDeployConfig, "jksKeypass"), JksStorepass: maps.GetValueAsString(options.ProviderDeployConfig, "jksStorepass"), - }, logger) - return deployer, logger, err + }) + return deployer, err } case domain.DeployProviderTypeKubernetesSecret: { access := domain.AccessConfigForKubernetes{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - deployer, err := providerK8sSecret.NewWithLogger(&providerK8sSecret.K8sSecretDeployerConfig{ + deployer, err := pK8sSecret.NewDeployer(&pK8sSecret.DeployerConfig{ KubeConfig: access.KubeConfig, Namespace: maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "namespace", "default"), SecretName: maps.GetValueAsString(options.ProviderDeployConfig, "secretName"), SecretType: maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "secretType", "kubernetes.io/tls"), SecretDataKeyForCrt: maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "secretDataKeyForCrt", "tls.crt"), SecretDataKeyForKey: maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "secretDataKeyForKey", "tls.key"), - }, logger) - return deployer, logger, err + }) + return deployer, err } case domain.DeployProviderTypeQiniuCDN, domain.DeployProviderTypeQiniuPili: { access := domain.AccessConfigForQiniu{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } switch options.Provider { case domain.DeployProviderTypeQiniuCDN: - deployer, err := providerQiniuCDN.NewWithLogger(&providerQiniuCDN.QiniuCDNDeployerConfig{ + deployer, err := pQiniuCDN.NewDeployer(&pQiniuCDN.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeQiniuPili: - deployer, err := providerQiniuPili.NewWithLogger(&providerQiniuPili.QiniuPiliDeployerConfig{ + deployer, err := pQiniuPili.NewDeployer(&pQiniuPili.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, Hub: maps.GetValueAsString(options.ProviderDeployConfig, "hub"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err default: break } } + case domain.DeployProviderTypeSafeLine: + { + access := domain.AccessConfigForSafeLine{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pSafeLine.NewDeployer(&pSafeLine.DeployerConfig{ + ApiUrl: access.ApiUrl, + ApiToken: access.ApiToken, + ResourceType: pSafeLine.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + CertificateId: maps.GetValueAsInt32(options.ProviderDeployConfig, "certificateId"), + }) + return deployer, err + } + case domain.DeployProviderTypeSSH: { access := domain.AccessConfigForSSH{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - deployer, err := providerSSH.NewWithLogger(&providerSSH.SshDeployerConfig{ + deployer, err := pSSH.NewDeployer(&pSSH.DeployerConfig{ SshHost: access.Host, SshPort: access.Port, SshUsername: access.Username, @@ -393,89 +576,108 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, UseSCP: maps.GetValueAsBool(options.ProviderDeployConfig, "useSCP"), PreCommand: maps.GetValueAsString(options.ProviderDeployConfig, "preCommand"), PostCommand: maps.GetValueAsString(options.ProviderDeployConfig, "postCommand"), - OutputFormat: providerSSH.OutputFormatType(maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "format", string(providerSSH.OUTPUT_FORMAT_PEM))), + OutputFormat: pSSH.OutputFormatType(maps.GetValueOrDefaultAsString(options.ProviderDeployConfig, "format", string(pSSH.OUTPUT_FORMAT_PEM))), OutputCertPath: maps.GetValueAsString(options.ProviderDeployConfig, "certPath"), OutputKeyPath: maps.GetValueAsString(options.ProviderDeployConfig, "keyPath"), PfxPassword: maps.GetValueAsString(options.ProviderDeployConfig, "pfxPassword"), JksAlias: maps.GetValueAsString(options.ProviderDeployConfig, "jksAlias"), JksKeypass: maps.GetValueAsString(options.ProviderDeployConfig, "jksKeypass"), JksStorepass: maps.GetValueAsString(options.ProviderDeployConfig, "jksStorepass"), - }, logger) - return deployer, logger, err + }) + return deployer, err } - case domain.DeployProviderTypeTencentCloudCDN, domain.DeployProviderTypeTencentCloudCLB, domain.DeployProviderTypeTencentCloudCOS, domain.DeployProviderTypeTencentCloudCSS, domain.DeployProviderTypeTencentCloudECDN, domain.DeployProviderTypeTencentCloudEO, domain.DeployProviderTypeTencentCloudSSLDeploy: + case domain.DeployProviderTypeTencentCloudCDN, domain.DeployProviderTypeTencentCloudCLB, domain.DeployProviderTypeTencentCloudCOS, domain.DeployProviderTypeTencentCloudCSS, domain.DeployProviderTypeTencentCloudECDN, domain.DeployProviderTypeTencentCloudEO, domain.DeployProviderTypeTencentCloudSSLDeploy, domain.DeployProviderTypeTencentCloudVOD, domain.DeployProviderTypeTencentCloudWAF: { access := domain.AccessConfigForTencentCloud{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } switch options.Provider { case domain.DeployProviderTypeTencentCloudCDN: - deployer, err := providerTencentCloudCDN.NewWithLogger(&providerTencentCloudCDN.TencentCloudCDNDeployerConfig{ + deployer, err := pTencentCloudCDN.NewDeployer(&pTencentCloudCDN.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeTencentCloudCLB: - deployer, err := providerTencentCloudCLB.NewWithLogger(&providerTencentCloudCLB.TencentCloudCLBDeployerConfig{ + deployer, err := pTencentCloudCLB.NewDeployer(&pTencentCloudCLB.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), - ResourceType: providerTencentCloudCLB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + ResourceType: pTencentCloudCLB.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"), ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeTencentCloudCOS: - deployer, err := providerTencentCloudCOS.NewWithLogger(&providerTencentCloudCOS.TencentCloudCOSDeployerConfig{ + deployer, err := pTencentCloudCOS.NewDeployer(&pTencentCloudCOS.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), Bucket: maps.GetValueAsString(options.ProviderDeployConfig, "bucket"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeTencentCloudCSS: - deployer, err := providerTencentCloudCSS.NewWithLogger(&providerTencentCloudCSS.TencentCloudCSSDeployerConfig{ + deployer, err := pTencentCloudCSS.NewDeployer(&pTencentCloudCSS.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeTencentCloudECDN: - deployer, err := providerTencentCloudECDN.NewWithLogger(&providerTencentCloudECDN.TencentCloudECDNDeployerConfig{ + deployer, err := pTencentCloudECDN.NewDeployer(&pTencentCloudECDN.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeTencentCloudEO: - deployer, err := providerTencentCloudEO.NewWithLogger(&providerTencentCloudEO.TencentCloudEODeployerConfig{ + deployer, err := pTencentCloudEO.NewDeployer(&pTencentCloudEO.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, ZoneId: maps.GetValueAsString(options.ProviderDeployConfig, "zoneId"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeTencentCloudSSLDeploy: - deployer, err := providerTencentCloudSSLDeploy.NewWithLogger(&providerTencentCloudSSLDeploy.TencentCloudSSLDeployDeployerConfig{ + deployer, err := pTencentCloudSSLDeploy.NewDeployer(&pTencentCloudSSLDeploy.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), ResourceType: maps.GetValueAsString(options.ProviderDeployConfig, "resourceType"), ResourceIds: slices.Filter(strings.Split(maps.GetValueAsString(options.ProviderDeployConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), - }, logger) - return deployer, logger, err + }) + return deployer, err + + case domain.DeployProviderTypeTencentCloudVOD: + deployer, err := pTencentCloudVOD.NewDeployer(&pTencentCloudVOD.DeployerConfig{ + SecretId: access.SecretId, + SecretKey: access.SecretKey, + SubAppId: maps.GetValueAsInt64(options.ProviderDeployConfig, "subAppId"), + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + }) + return deployer, err + + case domain.DeployProviderTypeTencentCloudWAF: + deployer, err := pTencentCloudWAF.NewDeployer(&pTencentCloudWAF.DeployerConfig{ + SecretId: access.SecretId, + SecretKey: access.SecretKey, + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + DomainId: maps.GetValueAsString(options.ProviderDeployConfig, "domainId"), + InstanceId: maps.GetValueAsString(options.ProviderDeployConfig, "instanceId"), + }) + return deployer, err default: break @@ -486,86 +688,96 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, { access := domain.AccessConfigForUCloud{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } switch options.Provider { case domain.DeployProviderTypeUCloudUCDN: - deployer, err := providerUCloudUCDN.NewWithLogger(&providerUCloudUCDN.UCloudUCDNDeployerConfig{ + deployer, err := pUCloudUCDN.NewDeployer(&pUCloudUCDN.DeployerConfig{ PrivateKey: access.PrivateKey, PublicKey: access.PublicKey, ProjectId: access.ProjectId, DomainId: maps.GetValueAsString(options.ProviderDeployConfig, "domainId"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeUCloudUS3: - deployer, err := providerUCloudUS3.NewWithLogger(&providerUCloudUS3.UCloudUS3DeployerConfig{ + deployer, err := pUCloudUS3.NewDeployer(&pUCloudUS3.DeployerConfig{ PrivateKey: access.PrivateKey, PublicKey: access.PublicKey, ProjectId: access.ProjectId, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), Bucket: maps.GetValueAsString(options.ProviderDeployConfig, "bucket"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err default: break } } - case domain.DeployProviderTypeVolcEngineCDN, domain.DeployProviderTypeVolcEngineCLB, domain.DeployProviderTypeVolcEngineDCDN, domain.DeployProviderTypeVolcEngineLive, domain.DeployProviderTypeVolcEngineTOS: + case domain.DeployProviderTypeVolcEngineCDN, domain.DeployProviderTypeVolcEngineCLB, domain.DeployProviderTypeVolcEngineDCDN, domain.DeployProviderTypeVolcEngineImageX, domain.DeployProviderTypeVolcEngineLive, domain.DeployProviderTypeVolcEngineTOS: { access := domain.AccessConfigForVolcEngine{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } switch options.Provider { case domain.DeployProviderTypeVolcEngineCDN: - deployer, err := providerVolcEngineCDN.NewWithLogger(&providerVolcEngineCDN.VolcEngineCDNDeployerConfig{ + deployer, err := pVolcEngineCDN.NewDeployer(&pVolcEngineCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeVolcEngineCLB: - deployer, err := providerVolcEngineCLB.NewWithLogger(&providerVolcEngineCLB.VolcEngineCLBDeployerConfig{ + deployer, err := pVolcEngineCLB.NewDeployer(&pVolcEngineCLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), - ResourceType: providerVolcEngineCLB.DeployResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + ResourceType: pVolcEngineCLB.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeVolcEngineDCDN: - deployer, err := providerVolcEngineDCDN.NewWithLogger(&providerVolcEngineDCDN.VolcEngineDCDNDeployerConfig{ + deployer, err := pVolcEngineDCDN.NewDeployer(&pVolcEngineDCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err + + case domain.DeployProviderTypeVolcEngineImageX: + deployer, err := pVolcEngineImageX.NewDeployer(&pVolcEngineImageX.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.SecretAccessKey, + Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), + ServiceId: maps.GetValueAsString(options.ProviderDeployConfig, "serviceId"), + Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), + }) + return deployer, err case domain.DeployProviderTypeVolcEngineLive: - deployer, err := providerVolcEngineLive.NewWithLogger(&providerVolcEngineLive.VolcEngineLiveDeployerConfig{ + deployer, err := pVolcEngineLive.NewDeployer(&pVolcEngineLive.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err case domain.DeployProviderTypeVolcEngineTOS: - deployer, err := providerVolcEngineTOS.NewWithLogger(&providerVolcEngineTOS.VolcEngineTOSDeployerConfig{ + deployer, err := pVolcEngineTOS.NewDeployer(&pVolcEngineTOS.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, Region: maps.GetValueAsString(options.ProviderDeployConfig, "region"), Bucket: maps.GetValueAsString(options.ProviderDeployConfig, "bucket"), Domain: maps.GetValueAsString(options.ProviderDeployConfig, "domain"), - }, logger) - return deployer, logger, err + }) + return deployer, err default: break @@ -576,16 +788,16 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, { access := domain.AccessConfigForWebhook{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, nil, fmt.Errorf("failed to populate provider access config: %w", err) + return nil, fmt.Errorf("failed to populate provider access config: %w", err) } - deployer, err := providerWebhook.NewWithLogger(&providerWebhook.WebhookDeployerConfig{ + deployer, err := pWebhook.NewDeployer(&pWebhook.DeployerConfig{ WebhookUrl: access.Url, WebhookData: maps.GetValueAsString(options.ProviderDeployConfig, "webhookData"), - }, logger) - return deployer, logger, err + }) + return deployer, err } } - return nil, nil, fmt.Errorf("unsupported deployer provider: %s", string(options.Provider)) + return nil, fmt.Errorf("unsupported deployer provider: %s", string(options.Provider)) } diff --git a/internal/domain/access.go b/internal/domain/access.go index e9df61d2..25df3210 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -53,6 +53,10 @@ type AccessConfigForBaiduCloud struct { SecretAccessKey string `json:"secretAccessKey"` } +type AccessConfigForBaishan struct { + ApiToken string `json:"apiToken"` +} + type AccessConfigForBaotaPanel struct { ApiUrl string `json:"apiUrl"` ApiKey string `json:"apiKey"` @@ -63,6 +67,16 @@ type AccessConfigForBytePlus struct { SecretKey string `json:"secretKey"` } +type AccessConfigForCacheFly struct { + ApiToken string `json:"apiToken"` +} + +type AccessConfigForCdnfly struct { + ApiUrl string `json:"apiUrl"` + ApiKey string `json:"apiKey"` + ApiSecret string `json:"apiSecret"` +} + type AccessConfigForCloudflare struct { DnsApiToken string `json:"dnsApiToken"` } @@ -72,6 +86,16 @@ type AccessConfigForClouDNS struct { AuthPassword string `json:"authPassword"` } +type AccessConfigForCMCCCloud struct { + AccessKeyId string `json:"accessKeyId"` + AccessKeySecret string `json:"accessKeySecret"` +} + +type AccessConfigForDNSLA struct { + ApiId string `json:"apiId"` + ApiSecret string `json:"apiSecret"` +} + type AccessConfigForDogeCloud struct { AccessKey string `json:"accessKey"` SecretKey string `json:"secretKey"` @@ -82,6 +106,10 @@ type AccessConfigForEdgio struct { ClientSecret string `json:"clientSecret"` } +type AccessConfigForGcore struct { + ApiToken string `json:"apiToken"` +} + type AccessConfigForGname struct { AppId string `json:"appId"` AppKey string `json:"appKey"` @@ -97,12 +125,22 @@ type AccessConfigForHuaweiCloud struct { SecretAccessKey string `json:"secretAccessKey"` } -type AccessConfigForLocal struct{} +type AccessConfigForJDCloud struct { + AccessKeyId string `json:"accessKeyId"` + AccessKeySecret string `json:"accessKeySecret"` +} type AccessConfigForKubernetes struct { KubeConfig string `json:"kubeConfig,omitempty"` } +type AccessConfigForLocal struct{} + +type AccessConfigForNamecheap struct { + Username string `json:"username"` + ApiKey string `json:"apiKey"` +} + type AccessConfigForNameDotCom struct { Username string `json:"username"` ApiToken string `json:"apiToken"` @@ -130,6 +168,11 @@ type AccessConfigForRainYun struct { ApiKey string `json:"apiKey"` } +type AccessConfigForSafeLine struct { + ApiUrl string `json:"apiUrl"` + ApiToken string `json:"apiToken"` +} + type AccessConfigForSSH struct { Host string `json:"host"` Port int32 `json:"port"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 350b3926..950abc08 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -16,33 +16,37 @@ const ( AccessProviderTypeAWS = AccessProviderType("aws") AccessProviderTypeAzure = AccessProviderType("azure") AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud") - AccessProviderTypeBaishan = AccessProviderType("baishan") // 白山云(预留) + AccessProviderTypeBaishan = AccessProviderType("baishan") AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel") AccessProviderTypeBytePlus = AccessProviderType("byteplus") - AccessProviderTypeCacheFly = AccessProviderType("cachefly") // CacheFly(预留) - AccessProviderTypeCdnfly = AccessProviderType("cdnfly") // Cdnly(预留) + AccessProviderTypeCacheFly = AccessProviderType("cachefly") + AccessProviderTypeCdnfly = AccessProviderType("cdnfly") AccessProviderTypeCloudflare = AccessProviderType("cloudflare") AccessProviderTypeClouDNS = AccessProviderType("cloudns") - AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud") // 移动云(预留) + AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud") AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留) AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留) + AccessProviderTypeDNSLA = AccessProviderType("dnsla") AccessProviderTypeDogeCloud = AccessProviderType("dogecloud") AccessProviderTypeEdgio = AccessProviderType("edgio") AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留) AccessProviderTypeGname = AccessProviderType("gname") - AccessProviderTypeGcore = AccessProviderType("gcore") // Gcore(预留) + AccessProviderTypeGcore = AccessProviderType("gcore") AccessProviderTypeGoDaddy = AccessProviderType("godaddy") AccessProviderTypeGoEdge = AccessProviderType("goedge") // GoEdge(预留) AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") + AccessProviderTypeJDCloud = AccessProviderType("jdcloud") AccessProviderTypeKubernetes = AccessProviderType("k8s") AccessProviderTypeLocal = AccessProviderType("local") + AccessProviderTypeNamecheap = AccessProviderType("namecheap") AccessProviderTypeNameDotCom = AccessProviderType("namedotcom") AccessProviderTypeNameSilo = AccessProviderType("namesilo") AccessProviderTypeNS1 = AccessProviderType("ns1") AccessProviderTypePowerDNS = AccessProviderType("powerdns") AccessProviderTypeQiniu = AccessProviderType("qiniu") + AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留) AccessProviderTypeRainYun = AccessProviderType("rainyun") - AccessProviderTypeSafeLine = AccessProviderType("safeline") // 雷池(预留) + AccessProviderTypeSafeLine = AccessProviderType("safeline") AccessProviderTypeSSH = AccessProviderType("ssh") AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud") AccessProviderTypeUCloud = AccessProviderType("ucloud") @@ -66,13 +70,22 @@ const ( ApplyDNSProviderTypeAliyunDNS = ApplyDNSProviderType("aliyun-dns") ApplyDNSProviderTypeAWS = ApplyDNSProviderType("aws") // 兼容旧值,等同于 [ApplyDNSProviderTypeAWSRoute53] ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType("aws-route53") + ApplyDNSProviderTypeAzure = ApplyDNSProviderType("azure") // 兼容旧值,等同于 [ApplyDNSProviderTypeAzure] ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns") + ApplyDNSProviderTypeBaiduCloud = ApplyDNSProviderType("baiducloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeBaiduCloudDNS] + ApplyDNSProviderTypeBaiduCloudDNS = ApplyDNSProviderType("baiducloud-dns") ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare") ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns") + ApplyDNSProviderTypeCMCCCloud = ApplyDNSProviderType("cmcccloud") + ApplyDNSProviderTypeDNSLA = ApplyDNSProviderType("dnsla") + ApplyDNSProviderTypeGcore = ApplyDNSProviderType("gcore") ApplyDNSProviderTypeGname = ApplyDNSProviderType("gname") ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy") ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS] ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns") + ApplyDNSProviderTypeJDCloud = ApplyDNSProviderType("jdcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeJDCloudDNS] + ApplyDNSProviderTypeJDCloudDNS = ApplyDNSProviderType("jdcloud-dns") + ApplyDNSProviderTypeNamecheap = ApplyDNSProviderType("namecheap") ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom") ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo") ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1") @@ -104,19 +117,31 @@ const ( DeployProviderTypeAliyunLive = DeployProviderType("aliyun-live") DeployProviderTypeAliyunNLB = DeployProviderType("aliyun-nlb") DeployProviderTypeAliyunOSS = DeployProviderType("aliyun-oss") + DeployProviderTypeAliyunVOD = DeployProviderType("aliyun-vod") DeployProviderTypeAliyunWAF = DeployProviderType("aliyun-waf") DeployProviderTypeAWSCloudFront = DeployProviderType("aws-cloudfront") DeployProviderTypeBaiduCloudCDN = DeployProviderType("baiducloud-cdn") + DeployProviderTypeBaishanCDN = DeployProviderType("baishan-cdn") + DeployProviderTypeBaotaPanelConsole = DeployProviderType("baotapanel-console") DeployProviderTypeBaotaPanelSite = DeployProviderType("baotapanel-site") DeployProviderTypeBytePlusCDN = DeployProviderType("byteplus-cdn") + DeployProviderTypeCacheFly = DeployProviderType("cachefly") + DeployProviderTypeCdnfly = DeployProviderType("cdnfly") DeployProviderTypeDogeCloudCDN = DeployProviderType("dogecloud-cdn") DeployProviderTypeEdgioApplications = DeployProviderType("edgio-applications") + DeployProviderTypeGcoreCDN = DeployProviderType("gcore-cdn") DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn") DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb") + DeployProviderTypeHuaweiCloudWAF = DeployProviderType("huaweicloud-waf") + DeployProviderTypeJDCloudALB = DeployProviderType("jdcloud-alb") + DeployProviderTypeJDCloudCDN = DeployProviderType("jdcloud-cdn") + DeployProviderTypeJDCloudLive = DeployProviderType("jdcloud-live") + DeployProviderTypeJDCloudVOD = DeployProviderType("jdcloud-vod") DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret") DeployProviderTypeLocal = DeployProviderType("local") DeployProviderTypeQiniuCDN = DeployProviderType("qiniu-cdn") DeployProviderTypeQiniuPili = DeployProviderType("qiniu-pili") + DeployProviderTypeSafeLine = DeployProviderType("safeline") DeployProviderTypeSSH = DeployProviderType("ssh") DeployProviderTypeTencentCloudCDN = DeployProviderType("tencentcloud-cdn") DeployProviderTypeTencentCloudCLB = DeployProviderType("tencentcloud-clb") @@ -125,11 +150,14 @@ const ( DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn") DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo") DeployProviderTypeTencentCloudSSLDeploy = DeployProviderType("tencentcloud-ssldeploy") + DeployProviderTypeTencentCloudVOD = DeployProviderType("tencentcloud-vod") + DeployProviderTypeTencentCloudWAF = DeployProviderType("tencentcloud-waf") DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn") DeployProviderTypeUCloudUS3 = DeployProviderType("ucloud-us3") DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn") DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb") DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn") + DeployProviderTypeVolcEngineImageX = DeployProviderType("volcengine-imagex") DeployProviderTypeVolcEngineLive = DeployProviderType("volcengine-live") DeployProviderTypeVolcEngineTOS = DeployProviderType("volcengine-tos") DeployProviderTypeWebhook = DeployProviderType("webhook") diff --git a/internal/notify/providers.go b/internal/notify/providers.go index 6483a7a0..6e18a84c 100644 --- a/internal/notify/providers.go +++ b/internal/notify/providers.go @@ -5,14 +5,14 @@ import ( "github.com/usual2970/certimate/internal/domain" "github.com/usual2970/certimate/internal/pkg/core/notifier" - providerBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark" - providerDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk" - providerEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email" - providerLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark" - providerServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan" - providerTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram" - providerWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook" - providerWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom" + pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark" + pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk" + pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email" + pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark" + pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan" + pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram" + pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook" + pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom" "github.com/usual2970/certimate/internal/pkg/utils/maps" ) @@ -23,19 +23,19 @@ func createNotifier(channel domain.NotifyChannelType, channelConfig map[string]a */ switch channel { case domain.NotifyChannelTypeBark: - return providerBark.New(&providerBark.BarkNotifierConfig{ + return pBark.NewNotifier(&pBark.NotifierConfig{ DeviceKey: maps.GetValueAsString(channelConfig, "deviceKey"), ServerUrl: maps.GetValueAsString(channelConfig, "serverUrl"), }) case domain.NotifyChannelTypeDingTalk: - return providerDingTalk.New(&providerDingTalk.DingTalkNotifierConfig{ + return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{ AccessToken: maps.GetValueAsString(channelConfig, "accessToken"), Secret: maps.GetValueAsString(channelConfig, "secret"), }) case domain.NotifyChannelTypeEmail: - return providerEmail.New(&providerEmail.EmailNotifierConfig{ + return pEmail.NewNotifier(&pEmail.NotifierConfig{ SmtpHost: maps.GetValueAsString(channelConfig, "smtpHost"), SmtpPort: maps.GetValueAsInt32(channelConfig, "smtpPort"), SmtpTLS: maps.GetValueOrDefaultAsBool(channelConfig, "smtpTLS", true), @@ -46,28 +46,28 @@ func createNotifier(channel domain.NotifyChannelType, channelConfig map[string]a }) case domain.NotifyChannelTypeLark: - return providerLark.New(&providerLark.LarkNotifierConfig{ + return pLark.NewNotifier(&pLark.NotifierConfig{ WebhookUrl: maps.GetValueAsString(channelConfig, "webhookUrl"), }) case domain.NotifyChannelTypeServerChan: - return providerServerChan.New(&providerServerChan.ServerChanNotifierConfig{ + return pServerChan.NewNotifier(&pServerChan.NotifierConfig{ Url: maps.GetValueAsString(channelConfig, "url"), }) case domain.NotifyChannelTypeTelegram: - return providerTelegram.New(&providerTelegram.TelegramNotifierConfig{ + return pTelegram.NewNotifier(&pTelegram.NotifierConfig{ ApiToken: maps.GetValueAsString(channelConfig, "apiToken"), ChatId: maps.GetValueAsInt64(channelConfig, "chatId"), }) case domain.NotifyChannelTypeWebhook: - return providerWebhook.New(&providerWebhook.WebhookNotifierConfig{ + return pWebhook.NewNotifier(&pWebhook.NotifierConfig{ Url: maps.GetValueAsString(channelConfig, "url"), }) case domain.NotifyChannelTypeWeCom: - return providerWeCom.New(&providerWeCom.WeComNotifierConfig{ + return pWeCom.NewNotifier(&pWeCom.NotifierConfig{ WebhookUrl: maps.GetValueAsString(channelConfig, "webhookUrl"), }) } diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq/acmehttpreq.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq/acmehttpreq.go index a25c13bb..ab2b11a6 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq/acmehttpreq.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq/acmehttpreq.go @@ -1,7 +1,6 @@ package acmehttpreq import ( - "errors" "net/url" "time" @@ -9,7 +8,7 @@ import ( "github.com/go-acme/lego/v4/providers/dns/httpreq" ) -type ACMEHttpReqApplicantConfig struct { +type ChallengeProviderConfig struct { Endpoint string `json:"endpoint"` Mode string `json:"mode"` Username string `json:"username"` @@ -17,9 +16,9 @@ type ACMEHttpReqApplicantConfig struct { DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` } -func NewChallengeProvider(config *ACMEHttpReqApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } endpoint, _ := url.Parse(config.Endpoint) diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun/aliyun.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun/aliyun.go index 6f6d2d89..8f5cc56b 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun/aliyun.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun/aliyun.go @@ -1,23 +1,22 @@ package aliyun import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/alidns" ) -type AliyunApplicantConfig struct { +type ChallengeProviderConfig struct { AccessKeyId string `json:"accessKeyId"` AccessKeySecret string `json:"accessKeySecret"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *AliyunApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := alidns.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53/aws-route53.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53/aws-route53.go index 6799885f..be1cfecf 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53/aws-route53.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53/aws-route53.go @@ -1,14 +1,13 @@ package awsroute53 import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/route53" ) -type AWSRoute53ApplicantConfig struct { +type ChallengeProviderConfig struct { AccessKeyId string `json:"accessKeyId"` SecretAccessKey string `json:"secretAccessKey"` Region string `json:"region"` @@ -17,9 +16,9 @@ type AWSRoute53ApplicantConfig struct { DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *AWSRoute53ApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := route53.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns/azure-dns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns/azure-dns.go index 7cfce4a7..bf36f3fb 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns/azure-dns.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns/azure-dns.go @@ -1,7 +1,6 @@ package azuredns import ( - "errors" "fmt" "strings" "time" @@ -11,7 +10,7 @@ import ( "github.com/go-acme/lego/v4/providers/dns/azuredns" ) -type AzureDNSApplicantConfig struct { +type ChallengeProviderConfig struct { TenantId string `json:"tenantId"` ClientId string `json:"clientId"` ClientSecret string `json:"clientSecret"` @@ -20,9 +19,9 @@ type AzureDNSApplicantConfig struct { DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *AzureDNSApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := azuredns.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/baiducloud.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/baiducloud.go new file mode 100644 index 00000000..ac63665e --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/baiducloud.go @@ -0,0 +1,39 @@ +package baiducloud + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + + internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal" +) + +type ChallengeProviderConfig struct { + AccessKeyId string `json:"accessKeyId"` + SecretAccessKey string `json:"secretAccessKey"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := internal.NewDefaultConfig() + providerConfig.AccessKeyID = config.AccessKeyId + providerConfig.SecretAccessKey = config.SecretAccessKey + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = config.DnsTTL + } + + provider, err := internal.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go new file mode 100644 index 00000000..0d3f9c66 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go @@ -0,0 +1,204 @@ +package lego_baiducloud + +import ( + "errors" + "fmt" + "strings" + "time" + + bceDns "github.com/baidubce/bce-sdk-go/services/dns" + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + "github.com/google/uuid" +) + +const ( + envNamespace = "BAIDUCLOUD_" + + EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID" + EnvSecretAccessKey = envNamespace + "SECRET_ACCESS_KEY" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" + EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" +) + +var _ challenge.ProviderTimeout = (*DNSProvider)(nil) + +type Config struct { + AccessKeyID string + SecretAccessKey string + + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int32 + HTTPTimeout time.Duration +} + +type DNSProvider struct { + client *bceDns.Client + config *Config +} + +func NewDefaultConfig() *Config { + return &Config{ + TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), + } +} + +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAccessKeyID, EnvSecretAccessKey) + if err != nil { + return nil, fmt.Errorf("baiducloud: %w", err) + } + + config := NewDefaultConfig() + config.AccessKeyID = values[EnvAccessKeyID] + config.SecretAccessKey = values[EnvSecretAccessKey] + + return NewDNSProviderConfig(config) +} + +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("baiducloud: the configuration of the DNS provider is nil") + } + + client, err := bceDns.NewClient(config.AccessKeyID, config.SecretAccessKey, "") + if err != nil { + return nil, err + } else { + if client.Config != nil { + client.Config.ConnectionTimeoutInMillis = int(config.HTTPTimeout.Milliseconds()) + } + } + + return &DNSProvider{ + client: client, + config: config, + }, nil +} + +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("baiducloud: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) + if err != nil { + return fmt.Errorf("baiducloud: %w", err) + } + + if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil { + return fmt.Errorf("baiducloud: %w", err) + } + + return nil +} + +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("baiducloud: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) + if err != nil { + return fmt.Errorf("baiducloud: %w", err) + } + + if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil { + return fmt.Errorf("baiducloud: %w", err) + } + + return nil +} + +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*bceDns.Record, error) { + pageMarker := "" + pageSize := 1000 + for { + request := &bceDns.ListRecordRequest{} + request.Rr = subDomain + request.Marker = pageMarker + request.MaxKeys = pageSize + + response, err := d.client.ListRecord(zoneName, request) + if err != nil { + return nil, err + } + + for _, record := range response.Records { + if record.Type == "TXT" && record.Rr == subDomain { + return &record, nil + } + } + + if len(response.Records) < pageSize { + break + } + + pageMarker = response.NextMarker + } + + return nil, nil +} + +func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error { + record, err := d.getDNSRecord(zoneName, subDomain) + if err != nil { + return err + } + + if record == nil { + request := &bceDns.CreateRecordRequest{ + Type: "TXT", + Rr: subDomain, + Value: value, + Ttl: &d.config.TTL, + } + err := d.client.CreateRecord(zoneName, request, d.generateClientToken()) + return err + } else { + request := &bceDns.UpdateRecordRequest{ + Type: "TXT", + Rr: subDomain, + Value: value, + Ttl: &d.config.TTL, + } + err := d.client.UpdateRecord(zoneName, record.Id, request, d.generateClientToken()) + return err + } +} + +func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error { + record, err := d.getDNSRecord(zoneName, subDomain) + if err != nil { + return err + } + + if record == nil { + return nil + } else { + err = d.client.DeleteRecord(zoneName, record.Id, d.generateClientToken()) + return err + } +} + +func (d *DNSProvider) generateClientToken() string { + return strings.ReplaceAll(uuid.New().String(), "-", "") +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare/cloudflare.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare/cloudflare.go index cea32e94..9782b39b 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare/cloudflare.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare/cloudflare.go @@ -1,22 +1,21 @@ package cloudflare import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/cloudflare" ) -type CloudflareApplicantConfig struct { +type ChallengeProviderConfig struct { DnsApiToken string `json:"dnsApiToken"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *CloudflareApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := cloudflare.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns/cloudns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns/cloudns.go index 09aac6df..dc351bd3 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns/cloudns.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns/cloudns.go @@ -1,23 +1,22 @@ package cloudns import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/cloudns" ) -type ClouDNSApplicantConfig struct { +type ChallengeProviderConfig struct { AuthId string `json:"authId"` AuthPassword string `json:"authPassword"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *ClouDNSApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := cloudns.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/cmcccloud.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/cmcccloud.go new file mode 100644 index 00000000..ba0721fd --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/cmcccloud.go @@ -0,0 +1,40 @@ +package cmcccloud + +import ( + "errors" + "time" + + "github.com/go-acme/lego/v4/challenge" + + "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/internal" +) + +type ChallengeProviderConfig struct { + AccessKeyId string `json:"accessKeyId"` + AccessKeySecret string `json:"accessKeySecret"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + return nil, errors.New("config is nil") + } + + providerConfig := internal.NewDefaultConfig() + providerConfig.AccessKey = config.AccessKeyId + providerConfig.SecretKey = config.AccessKeySecret + if config.DnsTTL != 0 { + providerConfig.TTL = config.DnsTTL + } + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + + provider, err := internal.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/internal/lego.go new file mode 100644 index 00000000..92ef6dfe --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud/internal/lego.go @@ -0,0 +1,221 @@ +package internal + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + "gitlab.ecloud.com/ecloud/ecloudsdkclouddns" + "gitlab.ecloud.com/ecloud/ecloudsdkclouddns/model" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" +) + +const ( + envNamespace = "CMCCCLOUD_" + + EnvAccessKey = envNamespace + "ACCESS_KEY" + EnvSecretKey = envNamespace + "SECRET_KEY" + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" + EnvReadTimeOut = envNamespace + "READ_TIMEOUT" + EnvConnectTimeout = envNamespace + "CONNECT_TIMEOUT" +) + +var _ challenge.ProviderTimeout = (*DNSProvider)(nil) + +type Config struct { + AccessKey string + SecretKey string + ReadTimeOut int + ConnectTimeout int + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int32 +} + +type DNSProvider struct { + client *ecloudsdkclouddns.Client + config *Config +} + +func NewDefaultConfig() *Config { + return &Config{ + ReadTimeOut: env.GetOrDefaultInt(EnvReadTimeOut, 30), + ConnectTimeout: env.GetOrDefaultInt(EnvConnectTimeout, 30), + TTL: int32(env.GetOrDefaultInt(EnvTTL, 600)), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + } +} + +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAccessKey, EnvSecretKey) + if err != nil { + return nil, fmt.Errorf("cmccecloud: %w", err) + } + + cfg := NewDefaultConfig() + cfg.AccessKey = values[EnvAccessKey] + cfg.SecretKey = values[EnvSecretKey] + + return NewDNSProviderConfig(cfg) +} + +func NewDNSProviderConfig(cfg *Config) (*DNSProvider, error) { + if cfg == nil { + return nil, errors.New("cmccecloud: the configuration of the DNS provider is nil") + } + + client := ecloudsdkclouddns.NewClient(&config.Config{ + AccessKey: cfg.AccessKey, + SecretKey: cfg.SecretKey, + // 资源池常量见: https://ecloud.10086.cn/op-help-center/doc/article/54462 + // 默认全局 + PoolId: "CIDC-CORE-00", + ReadTimeOut: cfg.ReadTimeOut, + ConnectTimeout: cfg.ConnectTimeout, + }) + + return &DNSProvider{ + client: client, + config: cfg, + }, nil +} + +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("cmccecloud: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName) + if err != nil { + return fmt.Errorf("cmccecloud: %w", err) + } + + readDomain := strings.Trim(zoneName, ".") + record, err := d.getDomainRecord(readDomain, subDomain) + if err != nil { + return err + } + if record == nil { + // add new record + resp, err := d.client.CreateRecordOpenapi(&model.CreateRecordOpenapiRequest{ + CreateRecordOpenapiBody: &model.CreateRecordOpenapiBody{ + LineId: "0", // 默认线路 + Rr: subDomain, + DomainName: readDomain, + Description: "from certimate", + Type: model.CreateRecordOpenapiBodyTypeEnumTxt, + Value: info.Value, + Ttl: &d.config.TTL, + }, + }) + if err != nil { + return fmt.Errorf("lego: %w", err) + } + if resp.State != model.CreateRecordOpenapiResponseStateEnumOk { + return fmt.Errorf("lego: create record failed, response state: %s, message: %s, code: %s", resp.State, resp.ErrorMessage, resp.ErrorCode) + } + return nil + } else { + // update record + resp, err := d.client.ModifyRecordOpenapi(&model.ModifyRecordOpenapiRequest{ + ModifyRecordOpenapiBody: &model.ModifyRecordOpenapiBody{ + RecordId: record.RecordId, + Rr: subDomain, + DomainName: readDomain, + Description: "from certmate", + LineId: "0", + Type: model.ModifyRecordOpenapiBodyTypeEnumTxt, + Value: info.Value, + Ttl: &d.config.TTL, + }, + }) + if err != nil { + return fmt.Errorf("lego: %w", err) + } + if resp.State != model.ModifyRecordOpenapiResponseStateEnumOk { + return fmt.Errorf("lego: create record failed, response state: %s", resp.State) + } + return nil + } +} + +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + challengeInfo := dns01.GetChallengeInfo(domain, keyAuth) + zoneName, err := dns01.FindZoneByFqdn(challengeInfo.FQDN) + if err != nil { + return fmt.Errorf("cmccecloud: %w", err) + } + subDomain, err := dns01.ExtractSubDomain(challengeInfo.FQDN, zoneName) + if err != nil { + return fmt.Errorf("cmccecloud: %w", err) + } + readDomain := strings.Trim(zoneName, ".") + record, err := d.getDomainRecord(readDomain, subDomain) + if err != nil { + return err + } + if record == nil { + return nil + } + resp, err := d.client.DeleteRecordOpenapi(&model.DeleteRecordOpenapiRequest{ + DeleteRecordOpenapiBody: &model.DeleteRecordOpenapiBody{ + RecordIdList: []string{record.RecordId}, + }, + }) + if err != nil { + return fmt.Errorf("lego: %w", err) + } + if resp.State != model.DeleteRecordOpenapiResponseStateEnumOk { + return fmt.Errorf("lego: delete record failed, response state: %s", resp.State) + } + return nil +} + +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +func (d *DNSProvider) getDomainRecord(domain string, rr string) (*model.ListRecordOpenapiResponseData, error) { + pageSize := int32(50) + page := int32(1) + for { + resp, err := d.client.ListRecordOpenapi(&model.ListRecordOpenapiRequest{ + ListRecordOpenapiBody: &model.ListRecordOpenapiBody{ + DomainName: domain, + }, + ListRecordOpenapiQuery: &model.ListRecordOpenapiQuery{ + PageSize: &pageSize, + Page: &page, + }, + }) + if err != nil { + return nil, err + } + if resp.State != model.ListRecordOpenapiResponseStateEnumOk { + respStr, _ := json.Marshal(resp) + return nil, fmt.Errorf("request error. %s", string(respStr)) + } + if resp.Body.Data != nil { + for _, item := range *resp.Body.Data { + if item.Rr == rr { + return &item, nil + } + } + } + if resp.Body.TotalPages == nil || page >= *resp.Body.TotalPages { + return nil, nil + } + page++ + } +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/dnsla.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/dnsla.go new file mode 100644 index 00000000..5b0bd977 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/dnsla.go @@ -0,0 +1,39 @@ +package dnsla + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + + internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/internal" +) + +type ChallengeProviderConfig struct { + ApiId string `json:"apiId"` + ApiSecret string `json:"apiSecret"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := internal.NewDefaultConfig() + providerConfig.APIId = config.ApiId + providerConfig.APISecret = config.ApiSecret + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := internal.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/internal/lego.go new file mode 100644 index 00000000..1b9603bd --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla/internal/lego.go @@ -0,0 +1,240 @@ +package lego_dnsla + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + + dnslasdk "github.com/usual2970/certimate/internal/pkg/vendors/dnsla-sdk" +) + +const ( + envNamespace = "DNSLA_" + + EnvAPIId = envNamespace + "API_ID" + EnvAPISecret = envNamespace + "API_KEY" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" + EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" +) + +var _ challenge.ProviderTimeout = (*DNSProvider)(nil) + +type Config struct { + APIId string + APISecret string + + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPTimeout time.Duration +} + +type DNSProvider struct { + client *dnslasdk.Client + config *Config +} + +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt(EnvTTL, 300), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), + } +} + +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAPIId, EnvAPISecret) + if err != nil { + return nil, fmt.Errorf("dnsla: %w", err) + } + + config := NewDefaultConfig() + config.APIId = values[EnvAPIId] + config.APISecret = values[EnvAPISecret] + + return NewDNSProviderConfig(config) +} + +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("dnsla: the configuration of the DNS provider is nil") + } + + client := dnslasdk.NewClient(config.APIId, config.APISecret). + WithTimeout(config.HTTPTimeout) + + return &DNSProvider{ + client: client, + config: config, + }, nil +} + +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("dnsla: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) + if err != nil { + return fmt.Errorf("dnsla: %w", err) + } + + if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil { + return fmt.Errorf("dnsla: %w", err) + } + + return nil +} + +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("dnsla: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) + if err != nil { + return fmt.Errorf("dnsla: %w", err) + } + + if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil { + return fmt.Errorf("dnsla: %w", err) + } + + return nil +} + +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +func (d *DNSProvider) getDNSZone(zoneName string) (*dnslasdk.DomainInfo, error) { + pageIndex := 1 + pageSize := 100 + for { + request := &dnslasdk.ListDomainsRequest{ + PageIndex: int32(pageIndex), + PageSize: int32(pageSize), + } + response, err := d.client.ListDomains(request) + if err != nil { + return nil, err + } + + if response.Data != nil { + for _, item := range response.Data.Results { + if strings.TrimRight(item.Domain, ".") == zoneName || strings.TrimRight(item.DisplayDomain, ".") == zoneName { + return item, nil + } + } + } + + if response.Data == nil || len(response.Data.Results) < pageSize { + break + } + + pageIndex++ + } + + return nil, fmt.Errorf("dnsla: zone %s not found", zoneName) +} + +func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*dnslasdk.DomainInfo, *dnslasdk.RecordInfo, error) { + zone, err := d.getDNSZone(zoneName) + if err != nil { + return nil, nil, err + } + + pageIndex := 1 + pageSize := 100 + for { + request := &dnslasdk.ListRecordsRequest{ + DomainId: zone.Id, + Host: &subDomain, + PageIndex: int32(pageIndex), + PageSize: int32(pageSize), + } + response, err := d.client.ListRecords(request) + if err != nil { + return zone, nil, err + } + + if response.Data != nil { + for _, record := range response.Data.Results { + if record.Type == 16 && (record.Host == subDomain || record.DisplayHost == subDomain) { + return zone, record, nil + } + } + } + + if response.Data == nil || len(response.Data.Results) < pageSize { + break + } + + pageIndex++ + } + + return zone, nil, nil +} + +func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error { + zone, record, err := d.getDNSZoneAndRecord(zoneName, subDomain) + if err != nil { + return err + } + + if record == nil { + request := &dnslasdk.CreateRecordRequest{ + DomainId: zone.Id, + Type: 16, + Host: subDomain, + Data: value, + Ttl: int32(d.config.TTL), + } + _, err := d.client.CreateRecord(request) + return err + } else { + reqType := int32(16) + reqTtl := int32(d.config.TTL) + request := &dnslasdk.UpdateRecordRequest{ + Id: record.Id, + Type: &reqType, + Host: &subDomain, + Data: &value, + Ttl: &reqTtl, + } + _, err := d.client.UpdateRecord(request) + return err + } +} + +func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error { + _, record, err := d.getDNSZoneAndRecord(zoneName, subDomain) + if err != nil { + return err + } + + if record == nil { + return nil + } else { + request := &dnslasdk.DeleteRecordRequest{ + Id: record.Id, + } + _, err = d.client.DeleteRecord(request) + return err + } +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore/gcore.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore/gcore.go new file mode 100644 index 00000000..ac9f7e61 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore/gcore.go @@ -0,0 +1,36 @@ +package gcore + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/gcore" +) + +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 := gcore.NewDefaultConfig() + providerConfig.APIToken = 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 := gcore.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/gname.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/gname.go index 90cec017..42057149 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/gname.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/gname.go @@ -1,7 +1,6 @@ package gname import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" @@ -9,16 +8,16 @@ import ( internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal" ) -type GnameApplicantConfig struct { +type ChallengeProviderConfig struct { AppId string `json:"appId"` AppKey string `json:"appKey"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *GnameApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := internal.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go index 03dc633f..3d0f2e54 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname/internal/lego.go @@ -37,13 +37,13 @@ type Config struct { } type DNSProvider struct { - client *gnamesdk.GnameClient + client *gnamesdk.Client config *Config } func NewDefaultConfig() *Config { return &Config{ - TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL), + TTL: env.GetOrDefaultInt(EnvTTL, 300), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), @@ -68,7 +68,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, errors.New("gname: the configuration of the DNS provider is nil") } - client := gnamesdk.NewGnameClient(config.AppID, config.AppKey). + client := gnamesdk.NewClient(config.AppID, config.AppKey). WithTimeout(config.HTTPTimeout) return &DNSProvider{ @@ -80,17 +80,17 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { func (d *DNSProvider) Present(domain, token, keyAuth string) error { info := dns01.GetChallengeInfo(domain, keyAuth) - zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) if err != nil { return fmt.Errorf("gname: %w", err) } - subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName) + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) if err != nil { return fmt.Errorf("gname: %w", err) } - if err := d.addOrUpdateDNSRecord(domain, subDomain, info.Value); err != nil { + if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil { return fmt.Errorf("gname: %w", err) } @@ -98,10 +98,19 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value := dns01.GetRecord(domain, keyAuth) - subDomain := dns01.UnFqdn(fqdn) + info := dns01.GetChallengeInfo(domain, keyAuth) - if err := d.removeDNSRecord(domain, subDomain, value); err != nil { + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("gname: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) + if err != nil { + return fmt.Errorf("gname: %w", err) + } + + if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil { return fmt.Errorf("gname: %w", err) } @@ -112,12 +121,12 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } -func (d *DNSProvider) getDNSRecord(domain, subDomain string) (*gnamesdk.ResolutionRecord, error) { +func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*gnamesdk.ResolutionRecord, error) { page := 1 pageSize := 20 for { request := &gnamesdk.ListDomainResolutionRequest{} - request.ZoneName = domain + request.ZoneName = zoneName request.Page = &page request.PageSize = &pageSize @@ -145,15 +154,15 @@ func (d *DNSProvider) getDNSRecord(domain, subDomain string) (*gnamesdk.Resoluti return nil, nil } -func (d *DNSProvider) addOrUpdateDNSRecord(domain, subDomain, value string) error { - record, err := d.getDNSRecord(domain, subDomain) +func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error { + record, err := d.getDNSRecord(zoneName, subDomain) if err != nil { return err } if record == nil { request := &gnamesdk.AddDomainResolutionRequest{ - ZoneName: domain, + ZoneName: zoneName, RecordType: "TXT", RecordName: subDomain, RecordValue: value, @@ -164,7 +173,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(domain, subDomain, value string) erro } else { request := &gnamesdk.ModifyDomainResolutionRequest{ ID: record.ID, - ZoneName: domain, + ZoneName: zoneName, RecordType: "TXT", RecordName: subDomain, RecordValue: value, @@ -175,8 +184,8 @@ func (d *DNSProvider) addOrUpdateDNSRecord(domain, subDomain, value string) erro } } -func (d *DNSProvider) removeDNSRecord(domain, subDomain, value string) error { - record, err := d.getDNSRecord(domain, subDomain) +func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error { + record, err := d.getDNSRecord(zoneName, subDomain) if err != nil { return err } @@ -186,7 +195,7 @@ func (d *DNSProvider) removeDNSRecord(domain, subDomain, value string) error { } request := &gnamesdk.DeleteDomainResolutionRequest{ - ZoneName: domain, + ZoneName: zoneName, RecordID: record.ID, } _, err = d.client.DeleteDomainResolution(request) diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy/godaddy.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy/godaddy.go index 3a44616b..957c9185 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy/godaddy.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy/godaddy.go @@ -1,23 +1,22 @@ package godaddy import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/godaddy" ) -type GoDaddyApplicantConfig struct { +type ChallengeProviderConfig struct { ApiKey string `json:"apiKey"` ApiSecret string `json:"apiSecret"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *GoDaddyApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := godaddy.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud/huaweicloud.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud/huaweicloud.go index cbdf2928..08a629fc 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud/huaweicloud.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud/huaweicloud.go @@ -1,14 +1,13 @@ package huaweicloud import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" hwc "github.com/go-acme/lego/v4/providers/dns/huaweicloud" ) -type HuaweiCloudApplicantConfig struct { +type ChallengeProviderConfig struct { AccessKeyId string `json:"accessKeyId"` SecretAccessKey string `json:"secretAccessKey"` Region string `json:"region"` @@ -16,9 +15,9 @@ type HuaweiCloudApplicantConfig struct { DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *HuaweiCloudApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } region := config.Region diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go new file mode 100644 index 00000000..68d81f7e --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go @@ -0,0 +1,238 @@ +package lego_jdcloud + +import ( + "errors" + "fmt" + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core" + jdDnsApi "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/apis" + jdDnsClient "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/client" + jdDnsModel "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/models" +) + +const ( + envNamespace = "JDCLOUD_" + + EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID" + EnvAccessKeySecret = envNamespace + "ACCESS_KEY_SECRET" + EnvRegionId = envNamespace + "REGION_ID" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" + EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" +) + +var _ challenge.ProviderTimeout = (*DNSProvider)(nil) + +type Config struct { + AccessKeyID string + AccessKeySecret string + RegionId string + + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int32 + HTTPTimeout time.Duration +} + +type DNSProvider struct { + client *jdDnsClient.DomainserviceClient + config *Config +} + +func NewDefaultConfig() *Config { + return &Config{ + TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), + } +} + +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAccessKeyID, EnvAccessKeySecret) + if err != nil { + return nil, fmt.Errorf("jdcloud: %w", err) + } + + config := NewDefaultConfig() + config.AccessKeyID = values[EnvAccessKeyID] + config.AccessKeySecret = values[EnvAccessKeySecret] + config.RegionId = values[EnvRegionId] + + return NewDNSProviderConfig(config) +} + +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("jdcloud: the configuration of the DNS provider is nil") + } + + clientCredentials := jdCore.NewCredentials(config.AccessKeyID, config.AccessKeySecret) + client := jdDnsClient.NewDomainserviceClient(clientCredentials) + clientConfig := &client.Config + clientConfig.SetTimeout(config.HTTPTimeout) + client.SetConfig(clientConfig) + client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn)) + + return &DNSProvider{ + client: client, + config: config, + }, nil +} + +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("jdcloud: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) + if err != nil { + return fmt.Errorf("jdcloud: %w", err) + } + + if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil { + return fmt.Errorf("jdcloud: %w", err) + } + + return nil +} + +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("jdcloud: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) + if err != nil { + return fmt.Errorf("jdcloud: %w", err) + } + + if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil { + return fmt.Errorf("jdcloud: %w", err) + } + + return nil +} + +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +func (d *DNSProvider) getDNSZone(zoneName string) (*jdDnsModel.DomainInfo, error) { + pageNumber := 1 + pageSize := 10 + for { + request := jdDnsApi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize) + request.SetDomainName(zoneName) + + response, err := d.client.DescribeDomains(request) + if err != nil { + return nil, err + } + + for _, item := range response.Result.DataList { + if item.DomainName == zoneName { + return &item, nil + } + } + + if len(response.Result.DataList) < pageSize { + break + } + + pageNumber++ + } + + return nil, fmt.Errorf("jdcloud: zone %s not found", zoneName) +} + +func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jdDnsModel.DomainInfo, *jdDnsModel.RRInfo, error) { + zone, err := d.getDNSZone(zoneName) + if err != nil { + return nil, nil, err + } + + pageNumber := 1 + pageSize := 10 + for { + request := jdDnsApi.NewDescribeResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id)) + request.SetSearch(subDomain) + request.SetPageNumber(pageNumber) + request.SetPageSize(pageSize) + + response, err := d.client.DescribeResourceRecord(request) + if err != nil { + return zone, nil, err + } + + for _, record := range response.Result.DataList { + if record.Type == "TXT" && record.HostRecord == subDomain { + return zone, &record, nil + } + } + + if len(response.Result.DataList) < pageSize { + break + } + + pageNumber++ + } + + return zone, nil, nil +} + +func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error { + zone, record, err := d.getDNSZoneAndRecord(zoneName, subDomain) + if err != nil { + return err + } + + if record == nil { + request := jdDnsApi.NewCreateResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), &jdDnsModel.AddRR{ + Type: "TXT", + HostRecord: subDomain, + HostValue: value, + Ttl: int(d.config.TTL), + ViewValue: -1, + }) + _, err := d.client.CreateResourceRecord(request) + return err + } else { + request := jdDnsApi.NewModifyResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id), &jdDnsModel.UpdateRR{ + Type: "TXT", + HostRecord: subDomain, + HostValue: value, + Ttl: int(d.config.TTL), + ViewValue: -1, + }) + _, err := d.client.ModifyResourceRecord(request) + return err + } +} + +func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error { + zone, record, err := d.getDNSZoneAndRecord(zoneName, subDomain) + if err != nil { + return err + } + + if record == nil { + return nil + } else { + request := jdDnsApi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id)) + _, err = d.client.DeleteResourceRecord(request) + return err + } +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/jdcloud.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/jdcloud.go new file mode 100644 index 00000000..5729d932 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/jdcloud.go @@ -0,0 +1,47 @@ +package jdcloud + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + + internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal" +) + +type ChallengeProviderConfig struct { + AccessKeyId string `json:"accessKeyId"` + AccessKeySecret string `json:"accessKeySecret"` + RegionId string `json:"regionId"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + regionId := config.RegionId + if regionId == "" { + // 京东云的 SDK 要求必须传一个区域,实际上 DNS-01 流程里用不到,但不传会报错 + regionId = "cn-north-1" + } + + providerConfig := internal.NewDefaultConfig() + providerConfig.AccessKeyID = config.AccessKeyId + providerConfig.AccessKeySecret = config.AccessKeySecret + providerConfig.RegionId = regionId + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = config.DnsTTL + } + + provider, err := internal.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/namecheap/namecheap.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/namecheap/namecheap.go new file mode 100644 index 00000000..9bf2f3c3 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/namecheap/namecheap.go @@ -0,0 +1,38 @@ +package namedotcom + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/namecheap" +) + +type ChallengeProviderConfig struct { + Username string `json:"username"` + ApiKey string `json:"apiKey"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := namecheap.NewDefaultConfig() + providerConfig.APIUser = config.Username + providerConfig.APIKey = config.ApiKey + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := namecheap.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom/namedotcom.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom/namedotcom.go index c51b1a9b..daff3612 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom/namedotcom.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom/namedotcom.go @@ -1,23 +1,22 @@ package namedotcom import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/namedotcom" ) -type NameDotComApplicantConfig struct { +type ChallengeProviderConfig struct { Username string `json:"username"` ApiToken string `json:"apiToken"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *NameDotComApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := namedotcom.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo/namesilo.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo/namesilo.go index 0f1bf8ea..5656136b 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo/namesilo.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo/namesilo.go @@ -1,22 +1,21 @@ package namesilo import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/namesilo" ) -type NameSiloApplicantConfig struct { +type ChallengeProviderConfig struct { ApiKey string `json:"apiKey"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *NameSiloApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := namesilo.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1/ns1.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1/ns1.go index efa40076..1682e0c2 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1/ns1.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1/ns1.go @@ -1,22 +1,21 @@ package ns1 import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/ns1" ) -type NS1ApplicantConfig struct { +type ChallengeProviderConfig struct { ApiKey string `json:"apiKey"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *NS1ApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := ns1.NewDefaultConfig() 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 d5fa616c..3dc86d66 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go @@ -1,7 +1,6 @@ package namesilo import ( - "errors" "net/url" "time" @@ -9,16 +8,16 @@ import ( "github.com/go-acme/lego/v4/providers/dns/pdns" ) -type PowerDNSApplicantConfig struct { +type ChallengeProviderConfig struct { ApiUrl string `json:"apiUrl"` ApiKey string `json:"apiKey"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *PowerDNSApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } host, _ := url.Parse(config.ApiUrl) diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun/rainyun.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun/rainyun.go index d250a279..2deda0f1 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun/rainyun.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun/rainyun.go @@ -1,22 +1,21 @@ package rainyun import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/rainyun" ) -type RainYunApplicantConfig struct { +type ChallengeProviderConfig struct { ApiKey string `json:"apiKey"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *RainYunApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := rainyun.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud/tencentcloud.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud/tencentcloud.go index f7e6bf7b..8ef3760c 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud/tencentcloud.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud/tencentcloud.go @@ -1,23 +1,22 @@ package tencentcloud import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/tencentcloud" ) -type TencentCloudApplicantConfig struct { +type ChallengeProviderConfig struct { SecretId string `json:"secretId"` SecretKey string `json:"secretKey"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *TencentCloudApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := tencentcloud.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine/volcengine.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine/volcengine.go index a8f11ff9..e0a1ae91 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine/volcengine.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine/volcengine.go @@ -1,23 +1,22 @@ package volcengine import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/volcengine" ) -type VolcEngineApplicantConfig struct { +type ChallengeProviderConfig struct { AccessKeyId string `json:"accessKeyId"` SecretAccessKey string `json:"secretAccessKey"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *VolcEngineApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := volcengine.NewDefaultConfig() diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn/westcn.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn/westcn.go index f20b5d21..f79c5a8c 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn/westcn.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn/westcn.go @@ -1,23 +1,22 @@ package westcn import ( - "errors" "time" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/providers/dns/westcn" ) -type WestcnApplicantConfig struct { +type ChallengeProviderConfig struct { Username string `json:"username"` ApiPassword string `json:"apiPassword"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` DnsTTL int32 `json:"dnsTTL,omitempty"` } -func NewChallengeProvider(config *WestcnApplicantConfig) (challenge.Provider, error) { +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } providerConfig := westcn.NewDefaultConfig() diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go index 9878b6f4..39600c7b 100644 --- a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go +++ b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go @@ -18,10 +18,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" ) -type AliyunALBDeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -29,43 +29,35 @@ type AliyunALBDeployerConfig struct { // 阿里云地域。 Region string `json:"region"` // 部署资源类型。 - ResourceType DeployResourceType `json:"resourceType"` + ResourceType ResourceType `json:"resourceType"` // 负载均衡实例 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_LOADBALANCER] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER] 时必填。 LoadbalancerId string `json:"loadbalancerId,omitempty"` // 负载均衡监听 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_LISTENER] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。 ListenerId string `json:"listenerId,omitempty"` // SNI 域名(支持泛域名)。 - // 部署资源类型为 [DEPLOY_RESOURCE_LOADBALANCER]、[DEPLOY_RESOURCE_LISTENER] 时选填。 + // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时选填。 Domain string `json:"domain,omitempty"` } -type AliyunALBDeployer struct { - config *AliyunALBDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClients *wSdkClients sslUploader uploader.Uploader } -var _ deployer.Deployer = (*AliyunALBDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) type wSdkClients struct { alb *aliyunAlb.Client cas *aliyunCas.Client } -func New(config *AliyunALBDeployerConfig) (*AliyunALBDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunALBDeployerConfig, logger logger.Logger) (*AliyunALBDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -78,15 +70,20 @@ func NewWithLogger(config *AliyunALBDeployerConfig, logger logger.Logger) (*Aliy return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &AliyunALBDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClients: clients, sslUploader: uploader, }, nil } -func (d *AliyunALBDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 CAS upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { @@ -97,12 +94,12 @@ func (d *AliyunALBDeployer) Deploy(ctx context.Context, certPem string, privkeyP // 根据部署资源类型决定部署方式 switch d.config.ResourceType { - case DEPLOY_RESOURCE_LOADBALANCER: + case RESOURCE_TYPE_LOADBALANCER: if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil { return nil, err } - case DEPLOY_RESOURCE_LISTENER: + case RESOURCE_TYPE_LISTENER: if err := d.deployToListener(ctx, upres.CertId); err != nil { return nil, err } @@ -114,7 +111,7 @@ func (d *AliyunALBDeployer) Deploy(ctx context.Context, certPem string, privkeyP return &deployer.DeployResult{}, nil } -func (d *AliyunALBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -213,7 +210,7 @@ func (d *AliyunALBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertI return nil } -func (d *AliyunALBDeployer) deployToListener(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error { if d.config.ListenerId == "" { return errors.New("config `listenerId` is required") } @@ -226,7 +223,7 @@ func (d *AliyunALBDeployer) deployToListener(ctx context.Context, cloudCertId st return nil } -func (d *AliyunALBDeployer) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error { +func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error { // 查询监听的属性 // REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getlistenerattribute getListenerAttributeReq := &aliyunAlb.GetListenerAttributeRequest{ @@ -257,7 +254,7 @@ func (d *AliyunALBDeployer) updateListenerCertificate(ctx context.Context, cloud d.logger.Logt("已更新 ALB 监听配置", updateListenerAttributeResp) } else { - // 指定 SNI,需部署到扩展域名(支持泛域名) + // 指定 SNI,需部署到扩展域名 // 查询监听证书列表 // REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates @@ -445,7 +442,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up } } - uploader, err := uploaderp.New(&uploaderp.AliyunCASUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: accessKeyId, AccessKeySecret: accessKeySecret, Region: casRegion, diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go index 15e6aff2..b5ae776a 100644 --- a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go @@ -63,11 +63,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.AliyunALBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LOADBALANCER, + ResourceType: provider.RESOURCE_TYPE_LOADBALANCER, LoadbalancerId: fLoadbalancerId, Domain: fDomain, }) @@ -98,11 +98,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("LISTENERID: %v", fListenerId), }, "\n")) - deployer, err := provider.New(&provider.AliyunALBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LISTENER, + ResourceType: provider.RESOURCE_TYPE_LISTENER, ListenerId: fListenerId, Domain: fDomain, }) diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/consts.go b/internal/pkg/core/deployer/providers/aliyun-alb/consts.go new file mode 100644 index 00000000..a2d1aacc --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-alb/consts.go @@ -0,0 +1,10 @@ +package aliyunalb + +type ResourceType string + +const ( + // 资源类型:部署到指定负载均衡器。 + RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer") + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = ResourceType("listener") +) diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/defines.go b/internal/pkg/core/deployer/providers/aliyun-alb/defines.go deleted file mode 100644 index 927e990a..00000000 --- a/internal/pkg/core/deployer/providers/aliyun-alb/defines.go +++ /dev/null @@ -1,10 +0,0 @@ -package aliyunalb - -type DeployResourceType string - -const ( - // 资源类型:部署到指定负载均衡器。 - DEPLOY_RESOURCE_LOADBALANCER = DeployResourceType("loadbalancer") - // 资源类型:部署到指定监听器。 - DEPLOY_RESOURCE_LISTENER = DeployResourceType("listener") -) diff --git a/internal/pkg/core/deployer/providers/aliyun-cas-deploy/aliyun_cas_deploy.go b/internal/pkg/core/deployer/providers/aliyun-cas-deploy/aliyun_cas_deploy.go index 31dc66b8..1cc36c6f 100644 --- a/internal/pkg/core/deployer/providers/aliyun-cas-deploy/aliyun_cas_deploy.go +++ b/internal/pkg/core/deployer/providers/aliyun-cas-deploy/aliyun_cas_deploy.go @@ -15,10 +15,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" ) -type AliyunCASDeployDeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -32,26 +32,18 @@ type AliyunCASDeployDeployerConfig struct { ContactIds []string `json:"contactIds"` } -type AliyunCASDeployDeployer struct { - config *AliyunCASDeployDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *aliyunCas.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*AliyunCASDeployDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AliyunCASDeployDeployerConfig) (*AliyunCASDeployDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunCASDeployDeployerConfig, logger logger.Logger) (*AliyunCASDeployDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -64,15 +56,20 @@ func NewWithLogger(config *AliyunCASDeployDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &AliyunCASDeployDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *AliyunCASDeployDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if len(d.config.ResourceIds) == 0 { return nil, errors.New("config `resourceIds` is required") } @@ -178,7 +175,7 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunCas.Cl } func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Uploader, error) { - uploader, err := uploaderp.New(&uploaderp.AliyunCASUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: accessKeyId, AccessKeySecret: accessKeySecret, Region: region, diff --git a/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn.go b/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn.go index d2de5684..b0edd415 100644 --- a/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn.go +++ b/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "strings" "time" @@ -16,7 +15,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/logger" ) -type AliyunCDNDeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -25,25 +24,17 @@ type AliyunCDNDeployerConfig struct { Domain string `json:"domain"` } -type AliyunCDNDeployer struct { - config *AliyunCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *aliyunCdn.Client } -var _ deployer.Deployer = (*AliyunCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AliyunCDNDeployerConfig) (*AliyunCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunCDNDeployerConfig, logger logger.Logger) (*AliyunCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) @@ -51,14 +42,19 @@ func NewWithLogger(config *AliyunCDNDeployerConfig, logger logger.Logger) (*Aliy return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &AliyunCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, }, nil } -func (d *AliyunCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // "*.example.com" → ".example.com",适配阿里云 CDN 要求的泛域名格式 domain := strings.TrimPrefix(d.config.Domain, "*") diff --git a/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn_test.go b/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn_test.go index da4d1d74..1f92947f 100644 --- a/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-cdn/aliyun_cdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.AliyunCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go index 4bd91611..21a1e471 100644 --- a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go +++ b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go @@ -13,10 +13,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb" ) -type AliyunCLBDeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -24,38 +24,30 @@ type AliyunCLBDeployerConfig struct { // 阿里云地域。 Region string `json:"region"` // 部署资源类型。 - ResourceType DeployResourceType `json:"resourceType"` + ResourceType ResourceType `json:"resourceType"` // 负载均衡实例 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_LOADBALANCER]、[DEPLOY_RESOURCE_LISTENER] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时必填。 LoadbalancerId string `json:"loadbalancerId,omitempty"` // 负载均衡监听端口。 - // 部署资源类型为 [DEPLOY_RESOURCE_LISTENER] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。 ListenerPort int32 `json:"listenerPort,omitempty"` // SNI 域名(支持泛域名)。 - // 部署资源类型为 [DEPLOY_RESOURCE_LOADBALANCER]、[DEPLOY_RESOURCE_LISTENER] 时选填。 + // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时选填。 Domain string `json:"domain,omitempty"` } -type AliyunCLBDeployer struct { - config *AliyunCLBDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *aliyunSlb.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*AliyunCLBDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AliyunCLBDeployerConfig) (*AliyunCLBDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunCLBDeployerConfig, logger logger.Logger) (*AliyunCLBDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -63,7 +55,7 @@ func NewWithLogger(config *AliyunCLBDeployerConfig, logger logger.Logger) (*Aliy return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.AliyunSLBUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: config.AccessKeyId, AccessKeySecret: config.AccessKeySecret, Region: config.Region, @@ -72,15 +64,20 @@ func NewWithLogger(config *AliyunCLBDeployerConfig, logger logger.Logger) (*Aliy return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &AliyunCLBDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *AliyunCLBDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 SLB upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { @@ -91,12 +88,12 @@ func (d *AliyunCLBDeployer) Deploy(ctx context.Context, certPem string, privkeyP // 根据部署资源类型决定部署方式 switch d.config.ResourceType { - case DEPLOY_RESOURCE_LOADBALANCER: + case RESOURCE_TYPE_LOADBALANCER: if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil { return nil, err } - case DEPLOY_RESOURCE_LISTENER: + case RESOURCE_TYPE_LISTENER: if err := d.deployToListener(ctx, upres.CertId); err != nil { return nil, err } @@ -108,7 +105,7 @@ func (d *AliyunCLBDeployer) Deploy(ctx context.Context, certPem string, privkeyP return &deployer.DeployResult{}, nil } -func (d *AliyunCLBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -179,7 +176,7 @@ func (d *AliyunCLBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertI return nil } -func (d *AliyunCLBDeployer) deployToListener(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -195,7 +192,7 @@ func (d *AliyunCLBDeployer) deployToListener(ctx context.Context, cloudCertId st return nil } -func (d *AliyunCLBDeployer) updateListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudListenerPort int32, cloudCertId string) error { +func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudListenerPort int32, cloudCertId string) error { // 查询监听配置 // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerhttpslistenerattribute describeLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.DescribeLoadBalancerHTTPSListenerAttributeRequest{ @@ -214,8 +211,6 @@ func (d *AliyunCLBDeployer) updateListenerCertificate(ctx context.Context, cloud // 修改监听配置 // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setloadbalancerhttpslistenerattribute - // - // 注意修改监听配置要放在修改扩展域名之后 setLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.SetLoadBalancerHTTPSListenerAttributeRequest{ RegionId: tea.String(d.config.Region), LoadBalancerId: tea.String(cloudLoadbalancerId), @@ -229,7 +224,7 @@ func (d *AliyunCLBDeployer) updateListenerCertificate(ctx context.Context, cloud d.logger.Logt("已更新 CLB HTTPS 监听配置", setLoadBalancerHTTPSListenerAttributeResp) } else { - // 指定 SNI,需部署到扩展域名(支持泛域名) + // 指定 SNI,需部署到扩展域名 // 查询扩展域名 // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describedomainextensions diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go index 0b27c5f8..a845bf86 100644 --- a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go @@ -63,11 +63,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.AliyunCLBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LOADBALANCER, + ResourceType: provider.RESOURCE_TYPE_LOADBALANCER, LoadbalancerId: fLoadbalancerId, Domain: fDomain, }) @@ -99,11 +99,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("LISTENERPORT: %v", fListenerPort), }, "\n")) - deployer, err := provider.New(&provider.AliyunCLBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LISTENER, + ResourceType: provider.RESOURCE_TYPE_LISTENER, LoadbalancerId: fLoadbalancerId, ListenerPort: int32(fListenerPort), Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/consts.go b/internal/pkg/core/deployer/providers/aliyun-clb/consts.go new file mode 100644 index 00000000..ad4fb070 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-clb/consts.go @@ -0,0 +1,10 @@ +package aliyunclb + +type ResourceType string + +const ( + // 资源类型:部署到指定负载均衡器。 + RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer") + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = ResourceType("listener") +) diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/defines.go b/internal/pkg/core/deployer/providers/aliyun-clb/defines.go deleted file mode 100644 index 4a02ab20..00000000 --- a/internal/pkg/core/deployer/providers/aliyun-clb/defines.go +++ /dev/null @@ -1,10 +0,0 @@ -package aliyunclb - -type DeployResourceType string - -const ( - // 资源类型:部署到指定负载均衡器。 - DEPLOY_RESOURCE_LOADBALANCER = DeployResourceType("loadbalancer") - // 资源类型:部署到指定监听器。 - DEPLOY_RESOURCE_LISTENER = DeployResourceType("listener") -) diff --git a/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn.go b/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn.go index 65c9a375..bfa28e7b 100644 --- a/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn.go +++ b/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "strings" "time" @@ -16,7 +15,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/logger" ) -type AliyunDCDNDeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -25,25 +24,17 @@ type AliyunDCDNDeployerConfig struct { Domain string `json:"domain"` } -type AliyunDCDNDeployer struct { - config *AliyunDCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *aliyunDcdn.Client } -var _ deployer.Deployer = (*AliyunDCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AliyunDCDNDeployerConfig) (*AliyunDCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunDCDNDeployerConfig, logger logger.Logger) (*AliyunDCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) @@ -51,14 +42,19 @@ func NewWithLogger(config *AliyunDCDNDeployerConfig, logger logger.Logger) (*Ali return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &AliyunDCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, }, nil } -func (d *AliyunDCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // "*.example.com" → ".example.com",适配阿里云 DCDN 要求的泛域名格式 domain := strings.TrimPrefix(d.config.Domain, "*") diff --git a/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn_test.go b/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn_test.go index ffbfcd1f..04ca4c48 100644 --- a/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-dcdn/aliyun_dcdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.AliyunDCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa.go b/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa.go index a09f8bbf..1ccbdae5 100644 --- a/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa.go +++ b/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa.go @@ -15,10 +15,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" ) -type AliyunESADeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -29,26 +29,18 @@ type AliyunESADeployerConfig struct { SiteId int64 `json:"siteId"` } -type AliyunESADeployer struct { - config *AliyunESADeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *aliyunEsa.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*AliyunESADeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AliyunESADeployerConfig) (*AliyunESADeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunESADeployerConfig, logger logger.Logger) (*AliyunESADeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -61,15 +53,20 @@ func NewWithLogger(config *AliyunESADeployerConfig, logger logger.Logger) (*Aliy return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &AliyunESADeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *AliyunESADeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if d.config.SiteId == 0 { return nil, errors.New("config `siteId` is required") } @@ -129,7 +126,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up } } - uploader, err := uploaderp.New(&uploaderp.AliyunCASUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: accessKeyId, AccessKeySecret: accessKeySecret, Region: casRegion, diff --git a/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa_test.go b/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa_test.go index 980dc9a0..c2d81aa3 100644 --- a/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-esa/aliyun_esa_test.go @@ -56,7 +56,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("SITEID: %v", fSiteId), }, "\n")) - deployer, err := provider.New(&provider.AliyunESADeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, diff --git a/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live.go b/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live.go index 2e0c46af..5735da79 100644 --- a/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live.go +++ b/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "strings" "time" @@ -16,7 +15,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/logger" ) -type AliyunLiveDeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -27,25 +26,17 @@ type AliyunLiveDeployerConfig struct { Domain string `json:"domain"` } -type AliyunLiveDeployer struct { - config *AliyunLiveDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *aliyunLive.Client } -var _ deployer.Deployer = (*AliyunLiveDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AliyunLiveDeployerConfig) (*AliyunLiveDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunLiveDeployerConfig, logger logger.Logger) (*AliyunLiveDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -53,14 +44,19 @@ func NewWithLogger(config *AliyunLiveDeployerConfig, logger logger.Logger) (*Ali return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &AliyunLiveDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, }, nil } -func (d *AliyunLiveDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // "*.example.com" → ".example.com",适配阿里云 Live 要求的泛域名格式 domain := strings.TrimPrefix(d.config.Domain, "*") diff --git a/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live_test.go b/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live_test.go index d66f647a..fcf01147 100644 --- a/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-live/aliyun_live_test.go @@ -16,6 +16,7 @@ var ( fInputKeyPath string fAccessKeyId string fAccessKeySecret string + fRegion string fDomain string ) @@ -26,6 +27,7 @@ func init() { flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fRegion, argsPrefix+"REGION", "", "") flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") } @@ -37,6 +39,7 @@ Shell command to run this test: --CERTIMATE_DEPLOYER_ALIYUNLIVE_INPUTKEYPATH="/path/to/your-input-key.pem" \ --CERTIMATE_DEPLOYER_ALIYUNLIVE_ACCESSKEYID="your-access-key-id" \ --CERTIMATE_DEPLOYER_ALIYUNLIVE_ACCESSKEYSECRET="your-access-key-secret" \ + --CERTIMATE_DEPLOYER_ALIYUNLIVE_REGION="cn-hangzhou" \ --CERTIMATE_DEPLOYER_ALIYUNLIVE_DOMAIN="example.com" */ func TestDeploy(t *testing.T) { @@ -49,12 +52,14 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("REGION: %v", fRegion), fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.AliyunLiveDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, + Region: fRegion, Domain: fDomain, }) if err != nil { diff --git a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go index 6deee907..0f1f1bca 100644 --- a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go +++ b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go @@ -14,10 +14,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" ) -type AliyunNLBDeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -25,35 +25,27 @@ type AliyunNLBDeployerConfig struct { // 阿里云地域。 Region string `json:"region"` // 部署资源类型。 - ResourceType DeployResourceType `json:"resourceType"` + ResourceType ResourceType `json:"resourceType"` // 负载均衡实例 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_LOADBALANCER] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER] 时必填。 LoadbalancerId string `json:"loadbalancerId,omitempty"` // 负载均衡监听 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_LISTENER] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。 ListenerId string `json:"listenerId,omitempty"` } -type AliyunNLBDeployer struct { - config *AliyunNLBDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *aliyunNlb.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*AliyunNLBDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AliyunNLBDeployerConfig) (*AliyunNLBDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunNLBDeployerConfig, logger logger.Logger) (*AliyunNLBDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -66,15 +58,20 @@ func NewWithLogger(config *AliyunNLBDeployerConfig, logger logger.Logger) (*Aliy return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &AliyunNLBDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *AliyunNLBDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 CAS upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { @@ -85,12 +82,12 @@ func (d *AliyunNLBDeployer) Deploy(ctx context.Context, certPem string, privkeyP // 根据部署资源类型决定部署方式 switch d.config.ResourceType { - case DEPLOY_RESOURCE_LOADBALANCER: + case RESOURCE_TYPE_LOADBALANCER: if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil { return nil, err } - case DEPLOY_RESOURCE_LISTENER: + case RESOURCE_TYPE_LISTENER: if err := d.deployToListener(ctx, upres.CertId); err != nil { return nil, err } @@ -102,7 +99,7 @@ func (d *AliyunNLBDeployer) Deploy(ctx context.Context, certPem string, privkeyP return &deployer.DeployResult{}, nil } -func (d *AliyunNLBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -171,7 +168,7 @@ func (d *AliyunNLBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertI return nil } -func (d *AliyunNLBDeployer) deployToListener(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error { if d.config.ListenerId == "" { return errors.New("config `listenerId` is required") } @@ -184,7 +181,7 @@ func (d *AliyunNLBDeployer) deployToListener(ctx context.Context, cloudCertId st return nil } -func (d *AliyunNLBDeployer) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error { +func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error { // 查询监听的属性 // REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getlistenerattribute getListenerAttributeReq := &aliyunNlb.GetListenerAttributeRequest{ @@ -248,7 +245,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up } } - uploader, err := uploaderp.New(&uploaderp.AliyunCASUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: accessKeyId, AccessKeySecret: accessKeySecret, Region: casRegion, diff --git a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb_test.go b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb_test.go index 45abd932..dfaf0916 100644 --- a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb_test.go @@ -59,11 +59,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId), }, "\n")) - deployer, err := provider.New(&provider.AliyunNLBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LOADBALANCER, + ResourceType: provider.RESOURCE_TYPE_LOADBALANCER, LoadbalancerId: fLoadbalancerId, }) if err != nil { @@ -94,11 +94,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("LISTENERID: %v", fListenerId), }, "\n")) - deployer, err := provider.New(&provider.AliyunNLBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LISTENER, + ResourceType: provider.RESOURCE_TYPE_LISTENER, ListenerId: fListenerId, }) if err != nil { diff --git a/internal/pkg/core/deployer/providers/aliyun-nlb/consts.go b/internal/pkg/core/deployer/providers/aliyun-nlb/consts.go new file mode 100644 index 00000000..1fa071df --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-nlb/consts.go @@ -0,0 +1,10 @@ +package aliyunnlb + +type ResourceType string + +const ( + // 资源类型:部署到指定负载均衡器。 + RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer") + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = ResourceType("listener") +) diff --git a/internal/pkg/core/deployer/providers/aliyun-nlb/defines.go b/internal/pkg/core/deployer/providers/aliyun-nlb/defines.go deleted file mode 100644 index 14e3c2b2..00000000 --- a/internal/pkg/core/deployer/providers/aliyun-nlb/defines.go +++ /dev/null @@ -1,10 +0,0 @@ -package aliyunnlb - -type DeployResourceType string - -const ( - // 资源类型:部署到指定负载均衡器。 - DEPLOY_RESOURCE_LOADBALANCER = DeployResourceType("loadbalancer") - // 资源类型:部署到指定监听器。 - DEPLOY_RESOURCE_LISTENER = DeployResourceType("listener") -) diff --git a/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go b/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go index 9caf0a3b..88adbf0b 100644 --- a/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go +++ b/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss.go @@ -12,7 +12,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/logger" ) -type AliyunOSSDeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -25,25 +25,17 @@ type AliyunOSSDeployerConfig struct { Domain string `json:"domain"` } -type AliyunOSSDeployer struct { - config *AliyunOSSDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *oss.Client } -var _ deployer.Deployer = (*AliyunOSSDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AliyunOSSDeployerConfig) (*AliyunOSSDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunOSSDeployerConfig, logger logger.Logger) (*AliyunOSSDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -51,14 +43,19 @@ func NewWithLogger(config *AliyunOSSDeployerConfig, logger logger.Logger) (*Aliy return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &AliyunOSSDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, }, nil } -func (d *AliyunOSSDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if d.config.Bucket == "" { return nil, errors.New("config `bucket` is required") } diff --git a/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss_test.go b/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss_test.go index e25f8156..7613a003 100644 --- a/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-oss/aliyun_oss_test.go @@ -60,7 +60,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.AliyunOSSDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, diff --git a/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod.go b/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod.go new file mode 100644 index 00000000..740fbb56 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod.go @@ -0,0 +1,95 @@ +package aliyunvod + +import ( + "context" + "fmt" + "time" + + aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client" + "github.com/alibabacloud-go/tea/tea" + aliyunVod "github.com/alibabacloud-go/vod-20170321/v4/client" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" +) + +type DeployerConfig struct { + // 阿里云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 阿里云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 阿里云地域。 + Region string `json:"region"` + // 点播加速域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *aliyunVod.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 设置域名证书 + // REF: https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-setvoddomainsslcertificate + setVodDomainSSLCertificateReq := &aliyunVod.SetVodDomainSSLCertificateRequest{ + DomainName: tea.String(d.config.Domain), + CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())), + CertType: tea.String("upload"), + SSLProtocol: tea.String("on"), + SSLPub: tea.String(certPem), + SSLPri: tea.String(privkeyPem), + } + setVodDomainSSLCertificateResp, err := d.sdkClient.SetVodDomainSSLCertificate(setVodDomainSSLCertificateReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.SetVodDomainSSLCertificate'") + } else { + d.logger.Logt("已设置域名证书", setVodDomainSSLCertificateResp) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunVod.Client, error) { + // 接入点一览 https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-endpoint + endpoint := fmt.Sprintf("vod.%s.aliyuncs.com", region) + + config := &aliyunOpen.Config{ + AccessKeyId: tea.String(accessKeyId), + AccessKeySecret: tea.String(accessKeySecret), + Endpoint: tea.String(endpoint), + } + + client, err := aliyunVod.NewClient(config) + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod_test.go b/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod_test.go new file mode 100644 index 00000000..552ddc0f --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-vod/aliyun_vod_test.go @@ -0,0 +1,80 @@ +package aliyunvod_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-vod" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fRegion string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNVOD_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fRegion, argsPrefix+"REGION", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./aliyun_vod_test.go -args \ + --CERTIMATE_DEPLOYER_ALIYUNVOD_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_ALIYUNVOD_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_ALIYUNVOD_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_ALIYUNVOD_ACCESSKEYSECRET="your-access-key-secret" \ + --CERTIMATE_DEPLOYER_ALIYUNVOD_REGION="cn-hangzhou" \ + --CERTIMATE_DEPLOYER_ALIYUNVOD_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("REGION: %v", fRegion), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Region: fRegion, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf.go b/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf.go index 1cc7aecd..5747d23e 100644 --- a/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf.go +++ b/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf.go @@ -14,10 +14,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" ) -type AliyunWAFDeployerConfig struct { +type DeployerConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -28,26 +28,18 @@ type AliyunWAFDeployerConfig struct { InstanceId string `json:"instanceId"` } -type AliyunWAFDeployer struct { - config *AliyunWAFDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *aliyunWaf.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*AliyunWAFDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AliyunWAFDeployerConfig) (*AliyunWAFDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AliyunWAFDeployerConfig, logger logger.Logger) (*AliyunWAFDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -60,15 +52,20 @@ func NewWithLogger(config *AliyunWAFDeployerConfig, logger logger.Logger) (*Aliy return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &AliyunWAFDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *AliyunWAFDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if d.config.InstanceId == "" { return nil, errors.New("config `instanceId` is required") } @@ -142,7 +139,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up } } - uploader, err := uploaderp.New(&uploaderp.AliyunCASUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: accessKeyId, AccessKeySecret: accessKeySecret, Region: casRegion, diff --git a/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf_test.go b/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf_test.go index 2498beca..06a76c63 100644 --- a/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-waf/aliyun_waf_test.go @@ -56,7 +56,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("INSTANCEID: %v", fInstanceId), }, "\n")) - deployer, err := provider.New(&provider.AliyunWAFDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, diff --git a/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go b/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go index 086dd415..2e8e09ee 100644 --- a/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go +++ b/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go @@ -14,10 +14,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-acm" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-acm" ) -type AWSCloudFrontDeployerConfig struct { +type DeployerConfig struct { // AWS AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // AWS SecretAccessKey。 @@ -28,26 +28,18 @@ type AWSCloudFrontDeployerConfig struct { DistributionId string `json:"distributionId"` } -type AWSCloudFrontDeployer struct { - config *AWSCloudFrontDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *awsCf.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*AWSCloudFrontDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *AWSCloudFrontDeployerConfig) (*AWSCloudFrontDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *AWSCloudFrontDeployerConfig, logger logger.Logger) (*AWSCloudFrontDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) @@ -55,7 +47,7 @@ func NewWithLogger(config *AWSCloudFrontDeployerConfig, logger logger.Logger) (* return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.AWSCertificateManagerUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: config.AccessKeyId, SecretAccessKey: config.SecretAccessKey, Region: config.Region, @@ -64,15 +56,20 @@ func NewWithLogger(config *AWSCloudFrontDeployerConfig, logger logger.Logger) (* return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &AWSCloudFrontDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *AWSCloudFrontDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if d.config.DistributionId == "" { return nil, errors.New("config `distribuitionId` is required") } diff --git a/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go b/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go index 89997b37..5b4c75db 100644 --- a/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go +++ b/internal/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go @@ -56,11 +56,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DISTRIBUTIONID: %v", fDistribuitionId), }, "\n")) - deployer, err := provider.New(&provider.AWSCloudFrontDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, SecretAccessKey: fSecretAccessKey, Region: fRegion, - DistribuitionId: fDistribuitionId, + DistributionId: fDistribuitionId, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn.go b/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn.go index ab932b42..df57ea1d 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn.go +++ b/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "time" @@ -14,7 +13,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/logger" ) -type BaiduCloudCDNDeployerConfig struct { +type DeployerConfig struct { // 百度智能云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 百度智能云 SecretAccessKey。 @@ -23,25 +22,17 @@ type BaiduCloudCDNDeployerConfig struct { Domain string `json:"domain"` } -type BaiduCloudCDNDeployer struct { - config *BaiduCloudCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *bceCdn.Client } -var _ deployer.Deployer = (*BaiduCloudCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *BaiduCloudCDNDeployerConfig) (*BaiduCloudCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *BaiduCloudCDNDeployerConfig, logger logger.Logger) (*BaiduCloudCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey) @@ -49,14 +40,19 @@ func NewWithLogger(config *BaiduCloudCDNDeployerConfig, logger logger.Logger) (* return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &BaiduCloudCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, }, nil } -func (d *BaiduCloudCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 修改域名证书 // REF: https://cloud.baidu.com/doc/CDN/s/qjzuz2hp8 putCertResp, err := d.sdkClient.PutCert( diff --git a/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn_test.go b/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn_test.go index 817600b6..ecb9a9d4 100644 --- a/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/baiducloud-cdn/baiducloud_cdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.BaiduCloudCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, SecretAccessKey: fSecretAccessKey, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go b/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go new file mode 100644 index 00000000..efb3353e --- /dev/null +++ b/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go @@ -0,0 +1,117 @@ +package baishancdn + +import ( + "context" + "errors" + "fmt" + "time" + + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + bssdk "github.com/usual2970/certimate/internal/pkg/vendors/baishan-sdk" +) + +type DeployerConfig struct { + // 白山云 API Token。 + ApiToken string `json:"apiToken"` + // 加速域名(支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *bssdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiToken) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + if d.config.Domain == "" { + return nil, errors.New("config `domain` is required") + } + + // 查询域名配置 + // REF: https://portal.baishancloud.com/track/document/api/1/1065 + getDomainConfigReq := &bssdk.GetDomainConfigRequest{ + Domains: d.config.Domain, + Config: "https", + } + getDomainConfigResp, err := d.sdkClient.GetDomainConfig(getDomainConfigReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.GetDomainConfig'") + } else if len(getDomainConfigResp.Data) == 0 { + return nil, errors.New("domain config not found") + } else { + d.logger.Logt("已查询到域名配置", getDomainConfigResp) + } + + // 新增证书 + // REF: https://portal.baishancloud.com/track/document/downloadPdf/1441 + createCertificateReq := &bssdk.CreateCertificateRequest{ + Certificate: certPem, + Key: privkeyPem, + Name: fmt.Sprintf("certimate_%d", time.Now().UnixMilli()), + } + createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.CreateCertificate'") + } else { + d.logger.Logt("已新增证书", createCertificateResp) + } + + // 设置域名配置 + // REF: https://portal.baishancloud.com/track/document/api/1/1045 + setDomainConfigReq := &bssdk.SetDomainConfigRequest{ + Domains: d.config.Domain, + Config: &bssdk.DomainConfig{ + Https: &bssdk.DomainConfigHttps{ + CertId: createCertificateResp.Data.CertId, + ForceHttps: getDomainConfigResp.Data[0].Config.Https.ForceHttps, + EnableHttp2: getDomainConfigResp.Data[0].Config.Https.EnableHttp2, + EnableOcsp: getDomainConfigResp.Data[0].Config.Https.EnableOcsp, + }, + }, + } + setDomainConfigResp, err := d.sdkClient.SetDomainConfig(setDomainConfigReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.SetDomainConfig'") + } else { + d.logger.Logt("已设置域名配置", setDomainConfigResp) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(apiToken string) (*bssdk.Client, error) { + if apiToken == "" { + return nil, errors.New("invalid baishan api token") + } + + client := bssdk.NewClient(apiToken) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go b/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go new file mode 100644 index 00000000..5534a232 --- /dev/null +++ b/internal/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go @@ -0,0 +1,70 @@ +package baishancdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baishan-cdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiToken string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_BAISHANCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./baishan_cdn_test.go -args \ + --CERTIMATE_DEPLOYER_BAISHANCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_BAISHANCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_BAISHANCDN_APITOKEN="your-api-token" \ + --CERTIMATE_DEPLOYER_BAISHANCDN_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("APITOKEN: %v", fApiToken), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiToken: fApiToken, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go new file mode 100644 index 00000000..6343acf7 --- /dev/null +++ b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go @@ -0,0 +1,93 @@ +package baotapanelconsole + +import ( + "context" + "errors" + "net/url" + + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + btsdk "github.com/usual2970/certimate/internal/pkg/vendors/btpanel-sdk" +) + +type DeployerConfig struct { + // 宝塔面板地址。 + ApiUrl string `json:"apiUrl"` + // 宝塔面板接口密钥。 + ApiKey string `json:"apiKey"` + // 是否自动重启。 + AutoRestart bool `json:"autoRestart"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *btsdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.ApiKey) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 设置面板 SSL 证书 + configSavePanelSSLReq := &btsdk.ConfigSavePanelSSLRequest{ + PrivateKey: privkeyPem, + Certificate: certPem, + } + configSavePanelSSLResp, err := d.sdkClient.ConfigSavePanelSSL(configSavePanelSSLReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.ConfigSavePanelSSL'") + } else { + d.logger.Logt("已设置面板 SSL 证书", configSavePanelSSLResp) + } + + if d.config.AutoRestart { + // 重启面板 + systemServiceAdminReq := &btsdk.SystemServiceAdminRequest{ + Name: "nginx", + Type: "restart", + } + _, err := d.sdkClient.SystemServiceAdmin(systemServiceAdminReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SystemServiceAdmin'") + } + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(apiUrl, apiKey string) (*btsdk.Client, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid baota api url") + } + + if apiKey == "" { + return nil, errors.New("invalid baota api key") + } + + client := btsdk.NewClient(apiUrl, apiKey) + return client, nil +} 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 new file mode 100644 index 00000000..06a2f096 --- /dev/null +++ b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go @@ -0,0 +1,70 @@ +package baotapanelconsole_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-console" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fApiKey string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") +} + +/* +Shell command to run this test: + + go test -v ./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_APIKEY="your-api-key" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("APIKEY: %v", fApiKey), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + ApiKey: fApiKey, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go index 336e2a5d..62cef9de 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go @@ -3,42 +3,41 @@ import ( "context" "errors" + "fmt" + "net/url" xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/utils/slices" btsdk "github.com/usual2970/certimate/internal/pkg/vendors/btpanel-sdk" ) -type BaotaPanelSiteDeployerConfig struct { +type DeployerConfig struct { // 宝塔面板地址。 ApiUrl string `json:"apiUrl"` // 宝塔面板接口密钥。 ApiKey string `json:"apiKey"` - // 站点名称 - SiteName string `json:"siteName"` + // 站点类型。 + SiteType string `json:"siteType"` + // 站点名称(单个)。 + SiteName string `json:"siteName,omitempty"` + // 站点名称(多个)。 + SiteNames []string `json:"siteNames,omitempty"` } -type BaotaPanelSiteDeployer struct { - config *BaotaPanelSiteDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger - sdkClient *btsdk.BaoTaPanelClient + sdkClient *btsdk.Client } -var _ deployer.Deployer = (*BaotaPanelSiteDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *BaotaPanelSiteDeployerConfig) (*BaotaPanelSiteDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *BaotaPanelSiteDeployerConfig, logger logger.Logger) (*BaotaPanelSiteDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.ApiUrl, config.ApiKey) @@ -46,36 +45,92 @@ func NewWithLogger(config *BaotaPanelSiteDeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &BaotaPanelSiteDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, }, nil } -func (d *BaotaPanelSiteDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { - if d.config.SiteName == "" { - return nil, errors.New("config `siteName` is required") - } +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} - // 设置站点 SSL 证书 - setSiteSSLReq := &btsdk.SetSiteSSLRequest{ - SiteName: d.config.SiteName, - Type: "1", - Key: privkeyPem, - Csr: certPem, - } - setSiteSSLResp, err := d.sdkClient.SetSiteSSL(setSiteSSLReq) - if err != nil { - return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SetSiteSSL'") - } +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + switch d.config.SiteType { + case "php": + { + if d.config.SiteName == "" { + return nil, errors.New("config `siteName` is required") + } - d.logger.Logt("已设置站点 SSL 证书", setSiteSSLResp) + // 设置站点 SSL 证书 + siteSetSSLReq := &btsdk.SiteSetSSLRequest{ + SiteName: d.config.SiteName, + Type: "0", + Certificate: certPem, + PrivateKey: privkeyPem, + } + siteSetSSLResp, err := d.sdkClient.SiteSetSSL(siteSetSSLReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SiteSetSSL'") + } else { + d.logger.Logt("已设置站点证书", siteSetSSLResp) + } + } + + case "other": + { + if len(d.config.SiteNames) == 0 { + return nil, errors.New("config `siteNames` is required") + } + + // 上传证书 + sslCertSaveCertReq := &btsdk.SSLCertSaveCertRequest{ + Certificate: certPem, + PrivateKey: privkeyPem, + } + sslCertSaveCertResp, err := d.sdkClient.SSLCertSaveCert(sslCertSaveCertReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SSLCertSaveCert'") + } else { + d.logger.Logt("已上传证书", sslCertSaveCertResp) + } + + // 设置站点证书 + sslSetBatchCertToSiteReq := &btsdk.SSLSetBatchCertToSiteRequest{ + BatchInfo: slices.Map(d.config.SiteNames, func(siteName string) *btsdk.SSLSetBatchCertToSiteRequestBatchInfo { + return &btsdk.SSLSetBatchCertToSiteRequestBatchInfo{ + SiteName: siteName, + SSLHash: sslCertSaveCertResp.SSLHash, + } + }), + } + sslSetBatchCertToSiteResp, err := d.sdkClient.SSLSetBatchCertToSite(sslSetBatchCertToSiteReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SSLSetBatchCertToSite'") + } else { + d.logger.Logt("已设置站点证书", sslSetBatchCertToSiteResp) + } + } + + default: + return nil, fmt.Errorf("unsupported site type: %s", d.config.SiteType) + } return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiKey string) (*btsdk.BaoTaPanelClient, error) { - client := btsdk.NewBaoTaPanelClient(apiUrl, apiKey) +func createSdkClient(apiUrl, apiKey string) (*btsdk.Client, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid baota api url") + } + + if apiKey == "" { + return nil, errors.New("invalid baota api key") + } + + client := btsdk.NewClient(apiUrl, apiKey) return client, nil } 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 486e8428..4c31b021 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 @@ -16,6 +16,7 @@ var ( fInputKeyPath string fApiUrl string fApiKey string + fSiteType string fSiteName string ) @@ -26,6 +27,7 @@ func init() { flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") + flag.StringVar(&fSiteType, argsPrefix+"SITETYPE", "", "") flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") } @@ -35,9 +37,10 @@ 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="your-baota-panel-url" \ - --CERTIMATE_DEPLOYER_BAOTAPANELSITE_APIKEY="your-baota-panel-key" \ - --CERTIMATE_DEPLOYER_BAOTAPANELSITE_SITENAME="your-baota-site-name" + --CERTIMATE_DEPLOYER_BAOTAPANELSITE_APIURL="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" */ func TestDeploy(t *testing.T) { flag.Parse() @@ -49,13 +52,16 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), fmt.Sprintf("APIURL: %v", fApiUrl), fmt.Sprintf("APIKEY: %v", fApiKey), + fmt.Sprintf("SITETYPE: %v", fSiteType), fmt.Sprintf("SITENAME: %v", fSiteName), }, "\n")) - deployer, err := provider.New(&provider.BaotaPanelSiteDeployerConfig{ - ApiUrl: fApiUrl, - ApiKey: fApiKey, - SiteName: fSiteName, + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + ApiKey: fApiKey, + SiteType: fSiteType, + SiteName: fSiteName, + SiteNames: []string{fSiteName}, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn.go b/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn.go index 2a695513..909a2e7d 100644 --- a/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn.go +++ b/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn.go @@ -12,10 +12,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/byteplus-cdn" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/byteplus-cdn" ) -type BytePlusCDNDeployerConfig struct { +type DeployerConfig struct { // BytePlus AccessKey。 AccessKey string `json:"accessKey"` // BytePlus SecretKey。 @@ -24,33 +24,25 @@ type BytePlusCDNDeployerConfig struct { Domain string `json:"domain"` } -type BytePlusCDNDeployer struct { - config *BytePlusCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *bpCdn.CDN sslUploader uploader.Uploader } -var _ deployer.Deployer = (*BytePlusCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *BytePlusCDNDeployerConfig) (*BytePlusCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *BytePlusCDNDeployerConfig, logger logger.Logger) (*BytePlusCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client := bpCdn.NewInstance() client.Client.SetAccessKey(config.AccessKey) client.Client.SetSecretKey(config.SecretKey) - uploader, err := uploaderp.New(&uploaderp.ByteplusCDNUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKey: config.AccessKey, SecretKey: config.SecretKey, }) @@ -58,15 +50,20 @@ func NewWithLogger(config *BytePlusCDNDeployerConfig, logger logger.Logger) (*By return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &BytePlusCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *BytePlusCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 CDN upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn_test.go b/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn_test.go index 9de97be6..ee09e8ee 100644 --- a/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn_test.go +++ b/internal/pkg/core/deployer/providers/byteplus-cdn/byteplus_cdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.BytePlusCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKey: fAccessKey, SecretKey: fSecretKey, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/cachefly/cachefly.go b/internal/pkg/core/deployer/providers/cachefly/cachefly.go new file mode 100644 index 00000000..8c6f129d --- /dev/null +++ b/internal/pkg/core/deployer/providers/cachefly/cachefly.go @@ -0,0 +1,72 @@ +package cachefly + +import ( + "context" + "errors" + + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + cfsdk "github.com/usual2970/certimate/internal/pkg/vendors/cachefly-sdk" +) + +type DeployerConfig struct { + // CacheFly API Token。 + ApiToken string `json:"apiToken"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *cfsdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiToken) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 上传证书 + createCertificateReq := &cfsdk.CreateCertificateRequest{ + Certificate: certPem, + CertificateKey: privkeyPem, + } + createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'cachefly.CreateCertificate'") + } else { + d.logger.Logt("已上传证书", createCertificateResp) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(apiToken string) (*cfsdk.Client, error) { + if apiToken == "" { + return nil, errors.New("invalid cachefly api token") + } + + client := cfsdk.NewClient(apiToken) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/cachefly/cachefly_test.go b/internal/pkg/core/deployer/providers/cachefly/cachefly_test.go new file mode 100644 index 00000000..1de6047e --- /dev/null +++ b/internal/pkg/core/deployer/providers/cachefly/cachefly_test.go @@ -0,0 +1,65 @@ +package cachefly_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cachefly" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiToken string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_CACHEFLY_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./cachefly_test.go -args \ + --CERTIMATE_DEPLOYER_CACHEFLY_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_CACHEFLY_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_CACHEFLY_APITOKEN="your-api-token" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APITOKEN: %v", fApiToken), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiToken: fApiToken, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go new file mode 100644 index 00000000..c98d9ae4 --- /dev/null +++ b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go @@ -0,0 +1,177 @@ +package cdnfly + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/url" + "time" + + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + cfsdk "github.com/usual2970/certimate/internal/pkg/vendors/cdnfly-sdk" +) + +type DeployerConfig struct { + // Cdnfly 地址。 + ApiUrl string `json:"apiUrl"` + // Cdnfly 用户端 API Key。 + ApiKey string `json:"apiKey"` + // Cdnfly 用户端 API Secret。 + ApiSecret string `json:"apiSecret"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` + // 网站 ID。 + // 部署资源类型为 [RESOURCE_TYPE_SITE] 时必填。 + SiteId string `json:"siteId,omitempty"` + // 证书 ID。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。 + CertificateId string `json:"certificateId,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *cfsdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.ApiSecret) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_SITE: + if err := d.deployToSite(ctx, certPem, privkeyPem); err != nil { + return nil, err + } + + case RESOURCE_TYPE_CERTIFICATE: + if err := d.deployToCertificate(ctx, certPem, privkeyPem); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToSite(ctx context.Context, certPem string, privkeyPem string) error { + if d.config.SiteId == "" { + return errors.New("config `siteId` is required") + } + + // 获取单个网站详情 + // REF: https://doc.cdnfly.cn/wangzhanguanli-v1-sites.html#%E8%8E%B7%E5%8F%96%E5%8D%95%E4%B8%AA%E7%BD%91%E7%AB%99%E8%AF%A6%E6%83%85 + getSiteReq := &cfsdk.GetSiteRequest{ + Id: d.config.SiteId, + } + getSiteResp, err := d.sdkClient.GetSite(getSiteReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.GetSite'") + } else { + d.logger.Logt("已获取网站详情", getSiteResp) + } + + // 添加单个证书 + // REF: https://doc.cdnfly.cn/wangzhanzhengshu-v1-certs.html#%E6%B7%BB%E5%8A%A0%E5%8D%95%E4%B8%AA%E6%88%96%E5%A4%9A%E4%B8%AA%E8%AF%81%E4%B9%A6-%E5%A4%9A%E4%B8%AA%E8%AF%81%E4%B9%A6%E6%97%B6%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E4%B8%BA%E6%95%B0%E7%BB%84 + createCertificateReq := &cfsdk.CreateCertificateRequest{ + Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), + Type: "custom", + Cert: certPem, + Key: privkeyPem, + } + createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.CreateCertificate'") + } else { + d.logger.Logt("已添加证书", createCertificateResp) + } + + // 修改单个网站 + // REF: https://doc.cdnfly.cn/wangzhanguanli-v1-sites.html#%E4%BF%AE%E6%94%B9%E5%8D%95%E4%B8%AA%E7%BD%91%E7%AB%99 + updateSiteHttpsListenMap := make(map[string]any) + _ = json.Unmarshal([]byte(getSiteResp.Data.HttpsListen), &updateSiteHttpsListenMap) + updateSiteHttpsListenMap["cert"] = createCertificateResp.Data + updateSiteHttpsListenData, _ := json.Marshal(updateSiteHttpsListenMap) + updateSiteHttpsListen := string(updateSiteHttpsListenData) + updateSiteReq := &cfsdk.UpdateSiteRequest{ + Id: d.config.SiteId, + HttpsListen: &updateSiteHttpsListen, + } + updateSiteResp, err := d.sdkClient.UpdateSite(updateSiteReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.UpdateSite'") + } else { + d.logger.Logt("已修改网站", updateSiteResp) + } + + return nil +} + +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem string, privkeyPem string) error { + if d.config.CertificateId == "" { + return errors.New("config `certificateId` is required") + } + + // 修改单个证书 + // REF: https://doc.cdnfly.cn/wangzhanzhengshu-v1-certs.html#%E4%BF%AE%E6%94%B9%E5%8D%95%E4%B8%AA%E8%AF%81%E4%B9%A6 + updateCertificateType := "custom" + updateCertificateReq := &cfsdk.UpdateCertificateRequest{ + Id: d.config.CertificateId, + Type: &updateCertificateType, + Cert: &certPem, + Key: &privkeyPem, + } + updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.UpdateCertificate'") + } else { + d.logger.Logt("已修改证书", updateCertificateResp) + } + + return nil +} + +func createSdkClient(apiUrl, apiKey, apiSecret string) (*cfsdk.Client, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid cachefly api url") + } + + if apiKey == "" { + return nil, errors.New("invalid cachefly api key") + } + + if apiSecret == "" { + return nil, errors.New("invalid cachefly api secret") + } + + client := cfsdk.NewClient(apiUrl, apiKey, apiSecret) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go b/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go new file mode 100644 index 00000000..fd17df9c --- /dev/null +++ b/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go @@ -0,0 +1,81 @@ +package cdnfly_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cdnfly" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fApiKey string + fApiSecret string + fCertificateId string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_CDNFLY_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") + flag.StringVar(&fApiSecret, argsPrefix+"APISECRET", "", "") + flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "") +} + +/* +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_APIKEY="your-api-key" \ + --CERTIMATE_DEPLOYER_CDNFLY_APISECRET="your-api-secret" \ + --CERTIMATE_DEPLOYER_CDNFLY_CERTIFICATEID="your-cert-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("APIKEY: %v", fApiKey), + fmt.Sprintf("APISECRET: %v", fApiSecret), + fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + ApiKey: fApiKey, + ApiSecret: fApiSecret, + ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, + CertificateId: fCertificateId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/cdnfly/consts.go b/internal/pkg/core/deployer/providers/cdnfly/consts.go new file mode 100644 index 00000000..3f6c6db0 --- /dev/null +++ b/internal/pkg/core/deployer/providers/cdnfly/consts.go @@ -0,0 +1,10 @@ +package cdnfly + +type ResourceType string + +const ( + // 资源类型:替换指定网站的证书。 + RESOURCE_TYPE_SITE = ResourceType("site") + // 资源类型:替换指定证书。 + RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate") +) diff --git a/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn.go b/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn.go index 283d9907..325a5319 100644 --- a/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn.go +++ b/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "strconv" xerrors "github.com/pkg/errors" @@ -10,11 +9,11 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/dogecloud" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/dogecloud" dogesdk "github.com/usual2970/certimate/internal/pkg/vendors/dogecloud-sdk" ) -type DogeCloudCDNDeployerConfig struct { +type DeployerConfig struct { // 多吉云 AccessKey。 AccessKey string `json:"accessKey"` // 多吉云 SecretKey。 @@ -23,31 +22,23 @@ type DogeCloudCDNDeployerConfig struct { Domain string `json:"domain"` } -type DogeCloudCDNDeployer struct { - config *DogeCloudCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *dogesdk.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*DogeCloudCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *DogeCloudCDNDeployerConfig) (*DogeCloudCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *DogeCloudCDNDeployerConfig, logger logger.Logger) (*DogeCloudCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client := dogesdk.NewClient(config.AccessKey, config.SecretKey) - uploader, err := uploaderp.New(&uploaderp.DogeCloudUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKey: config.AccessKey, SecretKey: config.SecretKey, }) @@ -55,15 +46,19 @@ func NewWithLogger(config *DogeCloudCDNDeployerConfig, logger logger.Logger) (*D return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &DogeCloudCDNDeployer{ - logger: logger, - config: config, + return &DeployerProvider{ + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *DogeCloudCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 CDN upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn_test.go b/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn_test.go index 8d066484..f9c1e7c9 100644 --- a/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/dogecloud-cdn/dogecloud_cdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.DogeCloudCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKey: fAccessKey, SecretKey: fSecretKey, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go index 5a6d4bf2..890cfdf3 100644 --- a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go +++ b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go @@ -2,7 +2,6 @@ import ( "context" - "errors" xerrors "github.com/pkg/errors" @@ -13,7 +12,7 @@ import ( edgsdkDtos "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7/dtos" ) -type EdgioApplicationsDeployerConfig struct { +type DeployerConfig struct { // Edgio ClientId。 ClientId string `json:"clientId"` // Edgio ClientSecret。 @@ -22,25 +21,17 @@ type EdgioApplicationsDeployerConfig struct { EnvironmentId string `json:"environmentId"` } -type EdgioApplicationsDeployer struct { - config *EdgioApplicationsDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *edgsdk.EdgioClient } -var _ deployer.Deployer = (*EdgioApplicationsDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *EdgioApplicationsDeployerConfig) (*EdgioApplicationsDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *EdgioApplicationsDeployerConfig, logger logger.Logger) (*EdgioApplicationsDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.ClientId, config.ClientSecret) @@ -48,14 +39,19 @@ func NewWithLogger(config *EdgioApplicationsDeployerConfig, logger logger.Logger return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &EdgioApplicationsDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, }, nil } -func (d *EdgioApplicationsDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 提取 Edgio 所需的服务端证书和中间证书内容 privateCertPem, intermediateCertPem, err := certs.ExtractCertificatesFromPEM(certPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go index 04f7a4cb..bd05b16c 100644 --- a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go +++ b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("ENVIRONMENTID: %v", fEnvironmentId), }, "\n")) - deployer, err := provider.New(&provider.EdgioApplicationsDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ ClientId: fClientId, ClientSecret: fClientSecret, EnvironmentId: fEnvironmentId, diff --git a/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn.go b/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn.go new file mode 100644 index 00000000..e1598a5d --- /dev/null +++ b/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn.go @@ -0,0 +1,124 @@ +package gcorecdn + +import ( + "context" + "errors" + "strconv" + + gprovider "github.com/G-Core/gcorelabscdn-go/gcore/provider" + gresources "github.com/G-Core/gcorelabscdn-go/resources" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/gcore-cdn" + gcoresdk "github.com/usual2970/certimate/internal/pkg/vendors/gcore-sdk/common" +) + +type DeployerConfig struct { + // Gcore API Token。 + ApiToken string `json:"apiToken"` + // CDN 资源 ID。 + ResourceId int64 `json:"resourceId"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *gresources.Service + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiToken) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + ApiToken: config.ApiToken, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + if d.config.ResourceId == 0 { + return nil, errors.New("config `resourceId` is required") + } + + // 上传证书到 CDN + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", upres) + } + + // 获取 CDN 资源详情 + // REF: https://api.gcore.com/docs/cdn#tag/CDN-resources/paths/~1cdn~1resources~1%7Bresource_id%7D/get + getResourceResp, err := d.sdkClient.Get(context.TODO(), d.config.ResourceId) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'resources.Get'") + } else { + d.logger.Logt("已获取 CDN 资源详情", getResourceResp) + } + + // 更新 CDN 资源详情 + // REF: https://api.gcore.com/docs/cdn#tag/CDN-resources/operation/change_cdn_resource + updateResourceCertId, _ := strconv.ParseInt(upres.CertId, 10, 64) + updateResourceReq := &gresources.UpdateRequest{ + Description: getResourceResp.Description, + Active: getResourceResp.Active, + OriginGroup: int(getResourceResp.OriginGroup), + OriginProtocol: getResourceResp.OriginProtocol, + SecondaryHostnames: getResourceResp.SecondaryHostnames, + SSlEnabled: true, + SSLData: int(updateResourceCertId), + ProxySSLEnabled: getResourceResp.ProxySSLEnabled, + ProxySSLCA: &getResourceResp.ProxySSLCA, + ProxySSLData: &getResourceResp.ProxySSLData, + Options: getResourceResp.Options, + } + updateResourceResp, err := d.sdkClient.Update(context.TODO(), d.config.ResourceId, updateResourceReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'resources.Update'") + } else { + d.logger.Logt("已更新 CDN 资源详情", updateResourceResp) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(apiToken string) (*gresources.Service, error) { + if apiToken == "" { + return nil, errors.New("invalid gcore api token") + } + + requester := gprovider.NewClient( + gcoresdk.BASE_URL, + gprovider.WithSigner(gcoresdk.NewAuthRequestSigner(apiToken)), + ) + service := gresources.NewService(requester) + return service, nil +} diff --git a/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn_test.go b/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn_test.go new file mode 100644 index 00000000..34d4a9d6 --- /dev/null +++ b/internal/pkg/core/deployer/providers/gcore-cdn/gcore_cdn_test.go @@ -0,0 +1,70 @@ +package gcorecdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/gcore-cdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiToken string + fResourceId int64 +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_GCORECDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") + flag.Int64Var(&fResourceId, argsPrefix+"RESOURCEID", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./gcore_cdn_test.go -args \ + --CERTIMATE_DEPLOYER_GCORECDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_GCORECDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_GCORECDN_APITOKEN="your-api-token" \ + --CERTIMATE_DEPLOYER_GCORECDN_RESOURCEID="your-cdn-resource-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APITOKEN: %v", fApiToken), + fmt.Sprintf("RESOURCEID: %v", fResourceId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiToken: fApiToken, + ResourceId: fResourceId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn.go b/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn.go index 31782af3..4a40fbc1 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global" hcCdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2" @@ -13,11 +12,11 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-scm" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-scm" hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" ) -type HuaweiCloudCDNDeployerConfig struct { +type DeployerConfig struct { // 华为云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 华为云 SecretAccessKey。 @@ -28,26 +27,18 @@ type HuaweiCloudCDNDeployerConfig struct { Domain string `json:"domain"` } -type HuaweiCloudCDNDeployer struct { - config *HuaweiCloudCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *hcCdn.CdnClient sslUploader uploader.Uploader } -var _ deployer.Deployer = (*HuaweiCloudCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *HuaweiCloudCDNDeployerConfig) (*HuaweiCloudCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *HuaweiCloudCDNDeployerConfig, logger logger.Logger) (*HuaweiCloudCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient( @@ -59,7 +50,7 @@ func NewWithLogger(config *HuaweiCloudCDNDeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.HuaweiCloudSCMUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: config.AccessKeyId, SecretAccessKey: config.SecretAccessKey, }) @@ -67,15 +58,20 @@ func NewWithLogger(config *HuaweiCloudCDNDeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &HuaweiCloudCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *HuaweiCloudCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 SCM upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn_test.go b/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn_test.go index d31b2a68..aaf8d6c7 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-cdn/huaweicloud_cdn_test.go @@ -56,7 +56,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.HuaweiCloudCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, SecretAccessKey: fSecretAccessKey, Region: fRegion, diff --git a/internal/pkg/core/deployer/providers/huaweicloud-elb/consts.go b/internal/pkg/core/deployer/providers/huaweicloud-elb/consts.go new file mode 100644 index 00000000..b097a958 --- /dev/null +++ b/internal/pkg/core/deployer/providers/huaweicloud-elb/consts.go @@ -0,0 +1,12 @@ +package huaweicloudelb + +type ResourceType string + +const ( + // 资源类型:替换指定证书。 + RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate") + // 资源类型:部署到指定负载均衡器。 + RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer") + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = ResourceType("listener") +) diff --git a/internal/pkg/core/deployer/providers/huaweicloud-elb/defines.go b/internal/pkg/core/deployer/providers/huaweicloud-elb/defines.go deleted file mode 100644 index 093ab829..00000000 --- a/internal/pkg/core/deployer/providers/huaweicloud-elb/defines.go +++ /dev/null @@ -1,12 +0,0 @@ -package huaweicloudelb - -type DeployResourceType string - -const ( - // 资源类型:替换指定证书。 - DEPLOY_RESOURCE_CERTIFICATE = DeployResourceType("certificate") - // 资源类型:部署到指定负载均衡器。 - DEPLOY_RESOURCE_LOADBALANCER = DeployResourceType("loadbalancer") - // 资源类型:部署到指定监听器。 - DEPLOY_RESOURCE_LISTENER = DeployResourceType("listener") -) diff --git a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go index 3fa17a70..17bc178b 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go @@ -19,11 +19,11 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-elb" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-elb" hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" ) -type HuaweiCloudELBDeployerConfig struct { +type DeployerConfig struct { // 华为云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 华为云 SecretAccessKey。 @@ -31,38 +31,30 @@ type HuaweiCloudELBDeployerConfig struct { // 华为云区域。 Region string `json:"region"` // 部署资源类型。 - ResourceType DeployResourceType `json:"resourceType"` + ResourceType ResourceType `json:"resourceType"` // 证书 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_CERTIFICATE] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。 CertificateId string `json:"certificateId,omitempty"` // 负载均衡器 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_LOADBALANCER] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER] 时必填。 LoadbalancerId string `json:"loadbalancerId,omitempty"` // 负载均衡监听 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_LISTENER] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。 ListenerId string `json:"listenerId,omitempty"` } -type HuaweiCloudELBDeployer struct { - config *HuaweiCloudELBDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *hcElb.ElbClient sslUploader uploader.Uploader } -var _ deployer.Deployer = (*HuaweiCloudELBDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *HuaweiCloudELBDeployerConfig) (*HuaweiCloudELBDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *HuaweiCloudELBDeployerConfig, logger logger.Logger) (*HuaweiCloudELBDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) @@ -70,7 +62,7 @@ func NewWithLogger(config *HuaweiCloudELBDeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.HuaweiCloudELBUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: config.AccessKeyId, SecretAccessKey: config.SecretAccessKey, Region: config.Region, @@ -79,15 +71,20 @@ func NewWithLogger(config *HuaweiCloudELBDeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &HuaweiCloudELBDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *HuaweiCloudELBDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 SCM upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { @@ -98,17 +95,17 @@ func (d *HuaweiCloudELBDeployer) Deploy(ctx context.Context, certPem string, pri // 根据部署资源类型决定部署方式 switch d.config.ResourceType { - case DEPLOY_RESOURCE_CERTIFICATE: + case RESOURCE_TYPE_CERTIFICATE: if err := d.deployToCertificate(ctx, certPem, privkeyPem); err != nil { return nil, err } - case DEPLOY_RESOURCE_LOADBALANCER: + case RESOURCE_TYPE_LOADBALANCER: if err := d.deployToLoadbalancer(ctx, certPem, privkeyPem); err != nil { return nil, err } - case DEPLOY_RESOURCE_LISTENER: + case RESOURCE_TYPE_LISTENER: if err := d.deployToListener(ctx, certPem, privkeyPem); err != nil { return nil, err } @@ -120,7 +117,7 @@ func (d *HuaweiCloudELBDeployer) Deploy(ctx context.Context, certPem string, pri return &deployer.DeployResult{}, nil } -func (d *HuaweiCloudELBDeployer) deployToCertificate(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem string, privkeyPem string) error { if d.config.CertificateId == "" { return errors.New("config `certificateId` is required") } @@ -146,7 +143,7 @@ func (d *HuaweiCloudELBDeployer) deployToCertificate(ctx context.Context, certPe return nil } -func (d *HuaweiCloudELBDeployer) deployToLoadbalancer(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem string, privkeyPem string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -223,7 +220,7 @@ func (d *HuaweiCloudELBDeployer) deployToLoadbalancer(ctx context.Context, certP return nil } -func (d *HuaweiCloudELBDeployer) deployToListener(ctx context.Context, certPem string, privkeyPem string) error { +func (d *DeployerProvider) deployToListener(ctx context.Context, certPem string, privkeyPem string) error { if d.config.ListenerId == "" { return errors.New("config `listenerId` is required") } @@ -244,7 +241,7 @@ func (d *HuaweiCloudELBDeployer) deployToListener(ctx context.Context, certPem s return nil } -func (d *HuaweiCloudELBDeployer) modifyListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error { +func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error { // 查询监听器详情 // REF: https://support.huaweicloud.com/api-elb/ShowListener.html showListenerReq := &hcElbModel.ShowListenerRequest{ diff --git a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb_test.go b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb_test.go index 32582800..4325ba40 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb_test.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb_test.go @@ -62,11 +62,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), }, "\n")) - deployer, err := provider.New(&provider.HuaweiCloudELBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, SecretAccessKey: fSecretAccessKey, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_CERTIFICATE, + ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, CertificateId: fCertificateId, }) if err != nil { @@ -96,11 +96,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId), }, "\n")) - deployer, err := provider.New(&provider.HuaweiCloudELBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, SecretAccessKey: fSecretAccessKey, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LOADBALANCER, + ResourceType: provider.RESOURCE_TYPE_LOADBALANCER, LoadbalancerId: fLoadbalancerId, }) if err != nil { @@ -130,11 +130,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("LISTENERID: %v", fListenerId), }, "\n")) - deployer, err := provider.New(&provider.HuaweiCloudELBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, SecretAccessKey: fSecretAccessKey, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LISTENER, + ResourceType: provider.RESOURCE_TYPE_LISTENER, ListenerId: fListenerId, }) if err != nil { diff --git a/internal/pkg/core/deployer/providers/huaweicloud-waf/consts.go b/internal/pkg/core/deployer/providers/huaweicloud-waf/consts.go new file mode 100644 index 00000000..fc574c32 --- /dev/null +++ b/internal/pkg/core/deployer/providers/huaweicloud-waf/consts.go @@ -0,0 +1,12 @@ +package huaweicloudwaf + +type ResourceType string + +const ( + // 资源类型:替换指定证书。 + RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate") + // 资源类型:部署到云模式防护网站。 + RESOURCE_TYPE_CLOUDSERVER = ResourceType("cloudserver") + // 资源类型:部署到独享模式防护网站。 + RESOURCE_TYPE_PREMIUMHOST = ResourceType("premiumhost") +) diff --git a/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf.go b/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf.go new file mode 100644 index 00000000..2342edfa --- /dev/null +++ b/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf.go @@ -0,0 +1,355 @@ +package huaweicloudwaf + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic" + "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global" + hcIam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3" + hcIamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model" + hcIamRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region" + hcWaf "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1" + hcWafModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/model" + hcWafRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/region" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-waf" + hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" +) + +type DeployerConfig struct { + // 华为云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 华为云 SecretAccessKey。 + SecretAccessKey string `json:"secretAccessKey"` + // 华为云区域。 + Region string `json:"region"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` + // 证书 ID。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。 + CertificateId string `json:"certificateId,omitempty"` + // 防护域名(支持泛域名)。 + // 部署资源类型为 [RESOURCE_TYPE_CLOUDSERVER]、[RESOURCE_TYPE_PREMIUMHOST] 时必填。 + Domain string `json:"domain,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *hcWaf.WafClient + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: config.AccessKeyId, + SecretAccessKey: config.SecretAccessKey, + Region: config.Region, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 上传证书到 WAF + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", upres) + } + + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_CERTIFICATE: + if err := d.deployToCertificate(ctx, certPem, privkeyPem); err != nil { + return nil, err + } + + case RESOURCE_TYPE_CLOUDSERVER: + if err := d.deployToCloudServer(ctx, certPem, privkeyPem); err != nil { + return nil, err + } + + case RESOURCE_TYPE_PREMIUMHOST: + if err := d.deployToPremiumHost(ctx, certPem, privkeyPem); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem string, privkeyPem string) error { + if d.config.CertificateId == "" { + return errors.New("config `certificateId` is required") + } + + // 查询证书 + // REF: https://support.huaweicloud.com/api-waf/ShowCertificate.html + showCertificateReq := &hcWafModel.ShowCertificateRequest{ + CertificateId: d.config.CertificateId, + } + showCertificateResp, err := d.sdkClient.ShowCertificate(showCertificateReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'waf.ShowCertificate'") + } else { + d.logger.Logt("已获取 WAF 证书", showCertificateResp) + } + + // 更新证书 + // REF: https://support.huaweicloud.com/api-waf/UpdateCertificate.html + updateCertificateReq := &hcWafModel.UpdateCertificateRequest{ + CertificateId: d.config.CertificateId, + Body: &hcWafModel.UpdateCertificateRequestBody{ + Name: *showCertificateResp.Name, + Content: hwsdk.StringPtr(certPem), + Key: hwsdk.StringPtr(privkeyPem), + }, + } + updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'waf.UpdateCertificate'") + } else { + d.logger.Logt("已更新 WAF 证书", updateCertificateResp) + } + + return nil +} + +func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPem string, privkeyPem string) error { + if d.config.Domain == "" { + return errors.New("config `domain` is required") + } + + // 上传证书到 WAF + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", upres) + } + + // 遍历查询云模式防护域名列表,获取防护域名 ID + // REF: https://support.huaweicloud.com/api-waf/ListHost.html + hostId := "" + listHostPage := int32(1) + listHostPageSize := int32(100) + for { + listHostReq := &hcWafModel.ListHostRequest{ + Hostname: hwsdk.StringPtr(strings.TrimPrefix(d.config.Domain, "*")), + Page: hwsdk.Int32Ptr(listHostPage), + Pagesize: hwsdk.Int32Ptr(listHostPageSize), + } + listHostResp, err := d.sdkClient.ListHost(listHostReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'waf.ListHost'") + } + + if listHostResp.Items != nil { + for _, hostItem := range *listHostResp.Items { + if strings.TrimPrefix(d.config.Domain, "*") == *hostItem.Hostname { + hostId = *hostItem.Id + break + } + } + } + + if listHostResp.Items == nil || len(*listHostResp.Items) < int(listHostPageSize) { + break + } else { + listHostPage++ + } + } + if hostId == "" { + return errors.New("host not found") + } + + // 更新云模式防护域名的配置 + // REF: https://support.huaweicloud.com/api-waf/UpdateHost.html + updateHostReq := &hcWafModel.UpdateHostRequest{ + InstanceId: hostId, + Body: &hcWafModel.UpdateHostRequestBody{ + Certificateid: hwsdk.StringPtr(upres.CertId), + Certificatename: hwsdk.StringPtr(upres.CertName), + }, + } + updateHostResp, err := d.sdkClient.UpdateHost(updateHostReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'waf.UpdateHost'") + } else { + d.logger.Logt("已更新云模式防护域名的配置", updateHostResp) + } + + return nil +} + +func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPem string, privkeyPem string) error { + if d.config.Domain == "" { + return errors.New("config `domain` is required") + } + + // 上传证书到 WAF + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", upres) + } + + // 遍历查询独享模式域名列表,获取防护域名 ID + // REF: https://support.huaweicloud.com/api-waf/ListPremiumHost.html + hostId := "" + listPremiumHostPage := int32(1) + listPremiumHostPageSize := int32(100) + for { + listPremiumHostReq := &hcWafModel.ListPremiumHostRequest{ + Hostname: hwsdk.StringPtr(strings.TrimPrefix(d.config.Domain, "*")), + Page: hwsdk.StringPtr(fmt.Sprintf("%d", listPremiumHostPage)), + Pagesize: hwsdk.StringPtr(fmt.Sprintf("%d", listPremiumHostPageSize)), + } + listPremiumHostResp, err := d.sdkClient.ListPremiumHost(listPremiumHostReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'waf.ListPremiumHost'") + } + + if listPremiumHostResp.Items != nil { + for _, hostItem := range *listPremiumHostResp.Items { + if strings.TrimPrefix(d.config.Domain, "*") == *hostItem.Hostname { + hostId = *hostItem.Id + break + } + } + } + + if listPremiumHostResp.Items == nil || len(*listPremiumHostResp.Items) < int(listPremiumHostPageSize) { + break + } else { + listPremiumHostPage++ + } + } + if hostId == "" { + return errors.New("host not found") + } + + // 修改独享模式域名配置 + // REF: https://support.huaweicloud.com/api-waf/UpdatePremiumHost.html + updatePremiumHostReq := &hcWafModel.UpdatePremiumHostRequest{ + HostId: hostId, + Body: &hcWafModel.UpdatePremiumHostRequestBody{ + Certificateid: hwsdk.StringPtr(upres.CertId), + Certificatename: hwsdk.StringPtr(upres.CertName), + }, + } + updatePremiumHostResp, err := d.sdkClient.UpdatePremiumHost(updatePremiumHostReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'waf.UpdatePremiumHost'") + } else { + d.logger.Logt("已修改独享模式域名配置", updatePremiumHostResp) + } + + return nil +} + +func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcWaf.WafClient, error) { + projectId, err := getSdkProjectId(accessKeyId, secretAccessKey, region) + if err != nil { + return nil, err + } + + auth, err := basic.NewCredentialsBuilder(). + WithAk(accessKeyId). + WithSk(secretAccessKey). + WithProjectId(projectId). + SafeBuild() + if err != nil { + return nil, err + } + + hcRegion, err := hcWafRegion.SafeValueOf(region) + if err != nil { + return nil, err + } + + hcClient, err := hcWaf.WafClientBuilder(). + WithRegion(hcRegion). + WithCredential(auth). + SafeBuild() + if err != nil { + return nil, err + } + + client := hcWaf.NewWafClient(hcClient) + return client, nil +} + +func getSdkProjectId(accessKeyId, secretAccessKey, region string) (string, error) { + auth, err := global.NewCredentialsBuilder(). + WithAk(accessKeyId). + WithSk(secretAccessKey). + SafeBuild() + if err != nil { + return "", err + } + + hcRegion, err := hcIamRegion.SafeValueOf(region) + if err != nil { + return "", err + } + + hcClient, err := hcIam.IamClientBuilder(). + WithRegion(hcRegion). + WithCredential(auth). + SafeBuild() + if err != nil { + return "", err + } + + client := hcIam.NewIamClient(hcClient) + + request := &hcIamModel.KeystoneListProjectsRequest{ + Name: ®ion, + } + response, err := client.KeystoneListProjects(request) + if err != nil { + return "", err + } else if response.Projects == nil || len(*response.Projects) == 0 { + return "", errors.New("no project found") + } + + return (*response.Projects)[0].Id, nil +} diff --git a/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf_test.go b/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf_test.go new file mode 100644 index 00000000..83142325 --- /dev/null +++ b/internal/pkg/core/deployer/providers/huaweicloud-waf/huaweicloud_waf_test.go @@ -0,0 +1,84 @@ +package huaweicloudwaf_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-waf" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fSecretAccessKey string + fRegion string + fResourceType string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_HUAWEICLOUDWAF_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "") + flag.StringVar(&fRegion, argsPrefix+"REGION", "", "") + flag.StringVar(&fResourceType, argsPrefix+"RESOURCETYPE", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./huaweicloud_waf_test.go -args \ + --CERTIMATE_DEPLOYER_HUAWEICLOUDWAF_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_HUAWEICLOUDWAF_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_HUAWEICLOUDWAF_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_HUAWEICLOUDWAF_SECRETACCESSKEY="your-secret-access-key" \ + --CERTIMATE_DEPLOYER_HUAWEICLOUDWAF_REGION="cn-north-1" \ + --CERTIMATE_DEPLOYER_HUAWEICLOUDWAF_RESOURCETYPE="premium" \ + --CERTIMATE_DEPLOYER_HUAWEICLOUDWAF_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("SECRETACCESSKEY: %v", fSecretAccessKey), + fmt.Sprintf("REGION: %v", fRegion), + fmt.Sprintf("RESOURCETYPE: %v", fResourceType), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + SecretAccessKey: fSecretAccessKey, + Region: fRegion, + ResourceType: provider.ResourceType(fResourceType), + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go b/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go new file mode 100644 index 00000000..13525c20 --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go @@ -0,0 +1,10 @@ +package jdcloudalb + +type ResourceType string + +const ( + // 资源类型:部署到指定负载均衡器。 + RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer") + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = ResourceType("listener") +) diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go new file mode 100644 index 00000000..41625b64 --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go @@ -0,0 +1,252 @@ +package jdcloudalb + +import ( + "context" + "errors" + "fmt" + "strings" + + jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core" + jdCommon "github.com/jdcloud-api/jdcloud-sdk-go/services/common/models" + jdLbApi "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/apis" + jdLbClient "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/client" + jdLbModel "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/models" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/jdcloud-ssl" + "github.com/usual2970/certimate/internal/pkg/utils/slices" +) + +type DeployerConfig struct { + // 京东云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 京东云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 京东云地域 ID。 + RegionId string `json:"regionId"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` + // 负载均衡器 ID。 + // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER] 时必填。 + LoadbalancerId string `json:"loadbalancerId,omitempty"` + // 监听器 ID。 + // 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。 + ListenerId string `json:"listenerId,omitempty"` + // SNI 域名(支持泛域名)。 + // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时选填。 + Domain string `json:"domain,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *jdLbClient.LbClient + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: config.AccessKeyId, + AccessKeySecret: config.AccessKeySecret, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 上传证书到 SSL + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", upres) + } + + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_LOADBALANCER: + if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil { + return nil, err + } + + case RESOURCE_TYPE_LISTENER: + if err := d.deployToListener(ctx, upres.CertId); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { + if d.config.LoadbalancerId == "" { + return errors.New("config `loadbalancerId` is required") + } + + // 查询负载均衡器详情 + // REF: https://docs.jdcloud.com/cn/load-balancer/api/describeloadbalancer + describeLoadBalancerReq := jdLbApi.NewDescribeLoadBalancerRequest(d.config.RegionId, d.config.LoadbalancerId) + describeLoadBalancerResp, err := d.sdkClient.DescribeLoadBalancer(describeLoadBalancerReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeLoadBalancer'") + } else { + d.logger.Logt("已查询到负载均衡器详情", describeLoadBalancerResp) + } + + // 查询监听器列表 + // REF: https://docs.jdcloud.com/cn/load-balancer/api/describelisteners + listenerIds := make([]string, 0) + describeListenersPageNumber := 1 + describeListenersPageSize := 100 + for { + describeListenersReq := jdLbApi.NewDescribeListenersRequest(d.config.RegionId) + describeListenersReq.SetFilters([]jdCommon.Filter{{Name: "loadBalancerId", Values: []string{d.config.LoadbalancerId}}}) + describeListenersReq.SetPageSize(describeListenersPageNumber) + describeListenersReq.SetPageSize(describeListenersPageSize) + describeListenersResp, err := d.sdkClient.DescribeListeners(describeListenersReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListeners'") + } + + for _, listener := range describeListenersResp.Result.Listeners { + if strings.EqualFold(listener.Protocol, "https") || strings.EqualFold(listener.Protocol, "tls") { + listenerIds = append(listenerIds, listener.ListenerId) + } + } + + if len(describeListenersResp.Result.Listeners) < int(describeListenersPageSize) { + break + } else { + describeListenersPageNumber++ + } + } + + // 遍历更新监听器证书 + if len(listenerIds) == 0 { + return errors.New("listener not found") + } else { + d.logger.Logt("已查询到负载均衡器下的全部 HTTPS/TLS 监听器", listenerIds) + + var errs []error + + for _, listenerId := range listenerIds { + if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { + errs = append(errs, err) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + } + + return nil +} + +func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error { + if d.config.ListenerId == "" { + return errors.New("config `listenerId` is required") + } + + // 更新监听器证书 + if err := d.updateListenerCertificate(ctx, d.config.ListenerId, cloudCertId); err != nil { + return err + } + + return nil +} + +func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error { + // 查询监听器详情 + // REF: https://docs.jdcloud.com/cn/load-balancer/api/describelistener + describeListenerReq := jdLbApi.NewDescribeListenerRequest(d.config.RegionId, cloudListenerId) + describeListenerResp, err := d.sdkClient.DescribeListener(describeListenerReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListener'") + } else { + d.logger.Logt("已查询到监听器详情", describeListenerResp) + } + + if d.config.Domain == "" { + // 未指定 SNI,只需部署到监听器 + + // 修改监听器信息 + // REF: https://docs.jdcloud.com/cn/load-balancer/api/updatelistener + updateListenerReq := jdLbApi.NewUpdateListenerRequest(d.config.RegionId, cloudListenerId) + updateListenerReq.SetCertificateSpecs([]jdLbModel.CertificateSpec{{CertificateId: cloudCertId}}) + updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListener'") + } else { + d.logger.Logt("已修改监听器信息", updateListenerResp) + } + } else { + // 指定 SNI,需部署到扩展证书 + + extCertSpecs := slices.Filter(describeListenerResp.Result.Listener.ExtensionCertificateSpecs, func(extCertSpec jdLbModel.ExtensionCertificateSpec) bool { + return extCertSpec.Domain == d.config.Domain + }) + if len(extCertSpecs) == 0 { + return errors.New("extension certificate spec not found") + } + + // 批量修改扩展证书 + // REF: https://docs.jdcloud.com/cn/load-balancer/api/updatelistenercertificates + updateListenerCertificatesReq := jdLbApi.NewUpdateListenerCertificatesRequest( + d.config.RegionId, + cloudListenerId, + slices.Map(extCertSpecs, func(extCertSpec jdLbModel.ExtensionCertificateSpec) jdLbModel.ExtCertificateUpdateSpec { + return jdLbModel.ExtCertificateUpdateSpec{ + CertificateBindId: extCertSpec.CertificateBindId, + CertificateId: &cloudCertId, + Domain: &extCertSpec.Domain, + } + }), + ) + updateListenerCertificatesResp, err := d.sdkClient.UpdateListenerCertificates(updateListenerCertificatesReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListenerCertificates'") + } else { + d.logger.Logt("已批量修改扩展证书", updateListenerCertificatesResp) + } + } + + return nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*jdLbClient.LbClient, error) { + clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret) + client := jdLbClient.NewLbClient(clientCredentials) + client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn)) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go new file mode 100644 index 00000000..9c9cc9cc --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go @@ -0,0 +1,118 @@ +package jdcloudalb_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-alb" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fRegionId string + fLoadbalancerId string + fListenerId string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDALB_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fRegionId, argsPrefix+"REGIONID", "", "") + flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "") + flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "") +} + +/* +Shell command to run this test: + + go test -v ./jdcloud_alb_test.go -args \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_ACCESSKEYSECRET="your-secret-access-key" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_REGION_ID="cn-north-1" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_LOADBALANCERID="your-alb-loadbalancer-id" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_LISTENERID="your-alb-listener-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy_ToLoadbalancer", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("REGIONID: %v", fRegionId), + fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + RegionId: fRegionId, + ResourceType: provider.RESOURCE_TYPE_LOADBALANCER, + LoadbalancerId: fLoadbalancerId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) + + t.Run("Deploy_ToListener", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("REGIONID: %v", fRegionId), + fmt.Sprintf("LISTENERID: %v", fListenerId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + RegionId: fRegionId, + ResourceType: provider.RESOURCE_TYPE_LISTENER, + ListenerId: fListenerId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn.go b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn.go new file mode 100644 index 00000000..d5bdae4f --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn.go @@ -0,0 +1,109 @@ +package jdcloudcdn + +import ( + "context" + + jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core" + jdCdnApi "github.com/jdcloud-api/jdcloud-sdk-go/services/cdn/apis" + jdCdnClient "github.com/jdcloud-api/jdcloud-sdk-go/services/cdn/client" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/jdcloud-ssl" +) + +type DeployerConfig struct { + // 京东云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 京东云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 加速域名(支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *jdCdnClient.CdnClient + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: config.AccessKeyId, + AccessKeySecret: config.AccessKeySecret, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 查询域名配置信息 + // REF: https://docs.jdcloud.com/cn/cdn/api/querydomainconfig + queryDomainConfigReq := jdCdnApi.NewQueryDomainConfigRequest(d.config.Domain) + queryDomainConfigResp, err := d.sdkClient.QueryDomainConfig(queryDomainConfigReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.QueryDomainConfig'") + } else { + d.logger.Logt("已查询到域名配置信息", queryDomainConfigResp) + } + + // 上传证书到 SSL + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", upres) + } + + // 设置通讯协议 + // REF: https://docs.jdcloud.com/cn/cdn/api/sethttptype + setHttpTypeReq := jdCdnApi.NewSetHttpTypeRequest(d.config.Domain) + setHttpTypeReq.SetHttpType("https") + setHttpTypeReq.SetCertificate(certPem) + setHttpTypeReq.SetRsaKey(privkeyPem) + setHttpTypeReq.SetCertFrom("ssl") + setHttpTypeReq.SetSslCertId(upres.CertId) + setHttpTypeReq.SetJumpType(queryDomainConfigResp.Result.HttpsJumpType) + setHttpTypeResp, err := d.sdkClient.SetHttpType(setHttpTypeReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.SetHttpType'") + } else { + d.logger.Logt("已设置通讯协议", setHttpTypeResp) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*jdCdnClient.CdnClient, error) { + clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret) + client := jdCdnClient.NewCdnClient(clientCredentials) + client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn)) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go new file mode 100644 index 00000000..2d2f7ed0 --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go @@ -0,0 +1,75 @@ +package jdcloudcdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-cdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./jdcloud_cdn_test.go -args \ + --CERTIMATE_DEPLOYER_JDCLOUDCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDCDN_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_JDCLOUDCDN_ACCESSKEYSECRET="your-secret-access-key" \ + --CERTIMATE_DEPLOYER_JDCLOUDCDN_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go new file mode 100644 index 00000000..7998fb16 --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live.go @@ -0,0 +1,75 @@ +package jdcloudlive + +import ( + "context" + + jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core" + jdLiveApi "github.com/jdcloud-api/jdcloud-sdk-go/services/live/apis" + jdLiveClient "github.com/jdcloud-api/jdcloud-sdk-go/services/live/client" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" +) + +type DeployerConfig struct { + // 京东云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 京东云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 直播播放域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *jdLiveClient.LiveClient +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 设置直播证书 + // REF: https://docs.jdcloud.com/cn/live-video/api/setlivedomaincertificate + setLiveDomainCertificateReq := jdLiveApi.NewSetLiveDomainCertificateRequest(d.config.Domain, "on") + setLiveDomainCertificateReq.SetCert(certPem) + setLiveDomainCertificateReq.SetKey(privkeyPem) + setLiveDomainCertificateResp, err := d.sdkClient.SetLiveDomainCertificate(setLiveDomainCertificateReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.SetLiveDomainCertificate'") + } else { + d.logger.Logt("已设置直播证书", setLiveDomainCertificateResp) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*jdLiveClient.LiveClient, error) { + clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret) + client := jdLiveClient.NewLiveClient(clientCredentials) + client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn)) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go new file mode 100644 index 00000000..076202b5 --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go @@ -0,0 +1,75 @@ +package jdcloudlive_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-live" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDLIVE_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./jdcloud_live_test.go -args \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_ACCESSKEYSECRET="your-secret-access-key" \ + --CERTIMATE_DEPLOYER_JDCLOUDLIVE_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod.go b/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod.go new file mode 100644 index 00000000..b83fd430 --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod.go @@ -0,0 +1,123 @@ +package jdcloudvod + +import ( + "context" + "fmt" + "strconv" + "time" + + jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core" + jdVodApi "github.com/jdcloud-api/jdcloud-sdk-go/services/vod/apis" + jdVodClient "github.com/jdcloud-api/jdcloud-sdk-go/services/vod/client" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" +) + +type DeployerConfig struct { + // 京东云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 京东云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 点播加速域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *jdVodClient.VodClient +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 查询域名列表 + // REF: https://docs.jdcloud.com/cn/video-on-demand/api/listdomains + var domainId int + listDomainsPageNumber := 1 + listDomainsPageSize := 100 + for { + listDomainsReq := jdVodApi.NewListDomainsRequest() + listDomainsReq.SetPageNumber(1) + listDomainsReq.SetPageSize(100) + listDomainsResp, err := d.sdkClient.ListDomains(listDomainsReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.ListDomains'") + } + + for _, domain := range listDomainsResp.Result.Content { + if domain.Name == d.config.Domain { + domainId, _ = strconv.Atoi(domain.Id) + break + } + } + + if len(listDomainsResp.Result.Content) < listDomainsPageSize { + break + } else { + listDomainsPageNumber++ + } + } + if domainId == 0 { + return nil, xerrors.New("domain not found") + } + + // 查询域名 SSL 配置 + // REF: https://docs.jdcloud.com/cn/video-on-demand/api/gethttpssl + getHttpSslReq := jdVodApi.NewGetHttpSslRequest(domainId) + getHttpSslResp, err := d.sdkClient.GetHttpSsl(getHttpSslReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.GetHttpSsl'") + } else { + d.logger.Logt("已查询到域名 SSL 配置", getHttpSslResp) + } + + // 设置域名 SSL 配置 + // REF: https://docs.jdcloud.com/cn/video-on-demand/api/sethttpssl + setHttpSslReq := jdVodApi.NewSetHttpSslRequest(domainId) + setHttpSslReq.SetTitle(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())) + setHttpSslReq.SetSslCert(certPem) + setHttpSslReq.SetSslKey(privkeyPem) + setHttpSslReq.SetSource("default") + setHttpSslReq.SetJumpType(getHttpSslResp.Result.JumpType) + setHttpSslReq.SetEnabled(true) + setHttpSslResp, err := d.sdkClient.SetHttpSsl(setHttpSslReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.SetHttpSsl'") + } else { + d.logger.Logt("已设置域名 SSL 配置", setHttpSslResp) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*jdVodClient.VodClient, error) { + clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret) + client := jdVodClient.NewVodClient(clientCredentials) + client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn)) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod_test.go b/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod_test.go new file mode 100644 index 00000000..6046982b --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-vod/jdcloud_vod_test.go @@ -0,0 +1,75 @@ +package jdcloudvod_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-vod" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDVOD_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./jdcloud_vod_test.go -args \ + --CERTIMATE_DEPLOYER_JDCLOUDVOD_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDVOD_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDVOD_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_JDCLOUDVOD_ACCESSKEYSECRET="your-secret-access-key" \ + --CERTIMATE_DEPLOYER_JDCLOUDVOD_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go b/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go index 2b7ab8fd..c97e4386 100644 --- a/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go +++ b/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go @@ -17,7 +17,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/certs" ) -type K8sSecretDeployerConfig struct { +type DeployerConfig struct { // kubeconfig 文件内容。 KubeConfig string `json:"kubeConfig,omitempty"` // Kubernetes 命名空间。 @@ -32,33 +32,30 @@ type K8sSecretDeployerConfig struct { SecretDataKeyForKey string `json:"secretDataKeyForKey,omitempty"` } -type K8sSecretDeployer struct { - config *K8sSecretDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger } -var _ deployer.Deployer = (*K8sSecretDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *K8sSecretDeployerConfig) (*K8sSecretDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *K8sSecretDeployerConfig, logger logger.Logger) (*K8sSecretDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - if logger == nil { - return nil, errors.New("logger is nil") - } - - return &K8sSecretDeployer{ - logger: logger, + return &DeployerProvider{ + logger: logger.NewNilLogger(), config: config, }, nil } -func (d *K8sSecretDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if d.config.Namespace == "" { return nil, errors.New("config `namespace` is required") } diff --git a/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go b/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go index 913099ec..75f15502 100644 --- a/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go +++ b/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go @@ -56,7 +56,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("SECRETDATAKEYFORKEY: %v", fSecretDataKeyForKey), }, "\n")) - deployer, err := provider.New(&provider.K8sSecretDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ Namespace: fNamespace, SecretName: fSecretName, SecretDataKeyForCrt: fSecretDataKeyForCrt, diff --git a/internal/pkg/core/deployer/providers/local/local.go b/internal/pkg/core/deployer/providers/local/local.go index e254caf9..7952f63c 100644 --- a/internal/pkg/core/deployer/providers/local/local.go +++ b/internal/pkg/core/deployer/providers/local/local.go @@ -3,7 +3,6 @@ package local import ( "bytes" "context" - "errors" "fmt" "os/exec" "runtime" @@ -16,7 +15,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/files" ) -type LocalDeployerConfig struct { +type DeployerConfig struct { // Shell 执行环境。 // 零值时默认根据操作系统决定。 ShellEnv ShellEnvType `json:"shellEnv,omitempty"` @@ -44,33 +43,30 @@ type LocalDeployerConfig struct { JksStorepass string `json:"jksStorepass,omitempty"` } -type LocalDeployer struct { - config *LocalDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger } -var _ deployer.Deployer = (*LocalDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *LocalDeployerConfig) (*LocalDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *LocalDeployerConfig, logger logger.Logger) (*LocalDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - if logger == nil { - return nil, errors.New("logger is nil") - } - - return &LocalDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), }, nil } -func (d *LocalDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 执行前置命令 if d.config.PreCommand != "" { stdout, stderr, err := execCommand(d.config.ShellEnv, d.config.PreCommand) diff --git a/internal/pkg/core/deployer/providers/local/local_test.go b/internal/pkg/core/deployer/providers/local/local_test.go index 723ddf58..e86ba7e0 100644 --- a/internal/pkg/core/deployer/providers/local/local_test.go +++ b/internal/pkg/core/deployer/providers/local/local_test.go @@ -72,7 +72,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("POSTCOMMAND: %v", fPostCommand), }, "\n")) - deployer, err := provider.New(&provider.LocalDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ OutputFormat: provider.OUTPUT_FORMAT_PEM, OutputCertPath: fOutputCertPath + ".pem", OutputKeyPath: fOutputKeyPath + ".pem", @@ -123,7 +123,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("PFXPASSWORD: %v", fPfxPassword), }, "\n")) - deployer, err := provider.New(&provider.LocalDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ OutputFormat: provider.OUTPUT_FORMAT_PFX, OutputCertPath: fOutputCertPath + ".pfx", PfxPassword: fPfxPassword, @@ -164,7 +164,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("JKSSTOREPASS: %v", fJksStorepass), }, "\n")) - deployer, err := provider.New(&provider.LocalDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ OutputFormat: provider.OUTPUT_FORMAT_JKS, OutputCertPath: fOutputCertPath + ".jks", JksAlias: fJksAlias, diff --git a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go index 8fac6459..cdb62b8c 100644 --- a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go +++ b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "strings" xerrors "github.com/pkg/errors" @@ -11,11 +10,11 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/qiniu-sslcert" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/qiniu-sslcert" qiniusdk "github.com/usual2970/certimate/internal/pkg/vendors/qiniu-sdk" ) -type QiniuCDNDeployerConfig struct { +type DeployerConfig struct { // 七牛云 AccessKey。 AccessKey string `json:"accessKey"` // 七牛云 SecretKey。 @@ -24,31 +23,23 @@ type QiniuCDNDeployerConfig struct { Domain string `json:"domain"` } -type QiniuCDNDeployer struct { - config *QiniuCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *qiniusdk.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*QiniuCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *QiniuCDNDeployerConfig) (*QiniuCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *QiniuCDNDeployerConfig, logger logger.Logger) (*QiniuCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client := qiniusdk.NewClient(auth.New(config.AccessKey, config.SecretKey)) - uploader, err := uploaderp.New(&uploaderp.QiniuSSLCertUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKey: config.AccessKey, SecretKey: config.SecretKey, }) @@ -56,15 +47,20 @@ func NewWithLogger(config *QiniuCDNDeployerConfig, logger logger.Logger) (*Qiniu return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &QiniuCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *QiniuCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 CDN upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go index 85ca8316..eff0ab48 100644 --- a/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go +++ b/internal/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.QiniuCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKey: fAccessKey, SecretKey: fSecretKey, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go b/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go index 4530071c..b9121694 100644 --- a/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go +++ b/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go @@ -2,7 +2,6 @@ import ( "context" - "errors" xerrors "github.com/pkg/errors" "github.com/qiniu/go-sdk/v7/pili" @@ -10,10 +9,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/qiniu-sslcert" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/qiniu-sslcert" ) -type QiniuPiliDeployerConfig struct { +type DeployerConfig struct { // 七牛云 AccessKey。 AccessKey string `json:"accessKey"` // 七牛云 SecretKey。 @@ -24,31 +23,23 @@ type QiniuPiliDeployerConfig struct { Domain string `json:"domain"` } -type QiniuPiliDeployer struct { - config *QiniuPiliDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *pili.Manager sslUploader uploader.Uploader } -var _ deployer.Deployer = (*QiniuPiliDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *QiniuPiliDeployerConfig) (*QiniuPiliDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *QiniuPiliDeployerConfig, logger logger.Logger) (*QiniuPiliDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } manager := pili.NewManager(pili.ManagerConfig{AccessKey: config.AccessKey, SecretKey: config.SecretKey}) - uploader, err := uploaderp.New(&uploaderp.QiniuSSLCertUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKey: config.AccessKey, SecretKey: config.SecretKey, }) @@ -56,15 +47,20 @@ func NewWithLogger(config *QiniuPiliDeployerConfig, logger logger.Logger) (*Qini return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &QiniuPiliDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: manager, sslUploader: uploader, }, nil } -func (d *QiniuPiliDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 CDN upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go b/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go index 06ef47e4..1d307dda 100644 --- a/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go +++ b/internal/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go @@ -56,7 +56,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.QiniuPiliDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKey: fAccessKey, SecretKey: fSecretKey, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/safeline/consts.go b/internal/pkg/core/deployer/providers/safeline/consts.go new file mode 100644 index 00000000..a19e3866 --- /dev/null +++ b/internal/pkg/core/deployer/providers/safeline/consts.go @@ -0,0 +1,8 @@ +package safeline + +type ResourceType string + +const ( + // 资源类型:替换指定证书。 + RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate") +) diff --git a/internal/pkg/core/deployer/providers/safeline/safeline.go b/internal/pkg/core/deployer/providers/safeline/safeline.go new file mode 100644 index 00000000..3b4006c4 --- /dev/null +++ b/internal/pkg/core/deployer/providers/safeline/safeline.go @@ -0,0 +1,108 @@ +package safeline + +import ( + "context" + "errors" + "fmt" + "net/url" + + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + safelinesdk "github.com/usual2970/certimate/internal/pkg/vendors/safeline-sdk" +) + +type DeployerConfig struct { + // 雷池 URL。 + ApiUrl string `json:"apiUrl"` + // 雷池 API Token。 + ApiToken string `json:"apiToken"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` + // 证书 ID。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。 + CertificateId int32 `json:"certificateId,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *safelinesdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.ApiToken) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk clients") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_CERTIFICATE: + if err := d.deployToCertificate(ctx, certPem, privkeyPem); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem string, privkeyPem string) error { + if d.config.CertificateId == 0 { + return errors.New("config `certificateId` is required") + } + + // 更新证书 + updateCertificateReq := &safelinesdk.UpdateCertificateRequest{ + Id: d.config.CertificateId, + Type: 2, + Manual: &safelinesdk.UpdateCertificateRequestBodyManul{ + Crt: certPem, + Key: privkeyPem, + }, + } + updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'safeline.UpdateCertificate'") + } else { + d.logger.Logt("已更新证书", updateCertificateResp) + } + + return nil +} + +func createSdkClient(apiUrl, apiToken string) (*safelinesdk.Client, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid safeline api url") + } + + if apiToken == "" { + return nil, errors.New("invalid safeline api token") + } + + client := safelinesdk.NewClient(apiUrl, apiToken) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/safeline/safeline_test.go b/internal/pkg/core/deployer/providers/safeline/safeline_test.go new file mode 100644 index 00000000..0d7f2223 --- /dev/null +++ b/internal/pkg/core/deployer/providers/safeline/safeline_test.go @@ -0,0 +1,76 @@ +package safeline_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/safeline" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fApiToken string + fCertificateId int +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_SAFELINE_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") + flag.IntVar(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") +} + +/* +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_APITOKEN="your-api-token" \ + --CERTIMATE_DEPLOYER_SAFELINE_CERTIFICATEID="your-cerficiate-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("APITOKEN: %v", fApiToken), + fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + ApiToken: fApiToken, + ResourceType: provider.ResourceType("certificate"), + CertificateId: fCertificateId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/ssh/ssh.go b/internal/pkg/core/deployer/providers/ssh/ssh.go index 20ea348a..a1617327 100644 --- a/internal/pkg/core/deployer/providers/ssh/ssh.go +++ b/internal/pkg/core/deployer/providers/ssh/ssh.go @@ -3,7 +3,6 @@ package ssh import ( "bytes" "context" - "errors" "fmt" "os" "path/filepath" @@ -18,7 +17,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/certs" ) -type SshDeployerConfig struct { +type DeployerConfig struct { // SSH 主机。 // 零值时默认为 "localhost"。 SshHost string `json:"sshHost,omitempty"` @@ -59,33 +58,30 @@ type SshDeployerConfig struct { JksStorepass string `json:"jksStorepass,omitempty"` } -type SshDeployer struct { - config *SshDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger } -var _ deployer.Deployer = (*SshDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *SshDeployerConfig) (*SshDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *SshDeployerConfig, logger logger.Logger) (*SshDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - if logger == nil { - return nil, errors.New("logger is nil") - } - - return &SshDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), }, nil } -func (d *SshDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 连接 client, err := createSshClient( d.config.SshHost, diff --git a/internal/pkg/core/deployer/providers/ssh/ssh_test.go b/internal/pkg/core/deployer/providers/ssh/ssh_test.go index 4221378e..8312d680 100644 --- a/internal/pkg/core/deployer/providers/ssh/ssh_test.go +++ b/internal/pkg/core/deployer/providers/ssh/ssh_test.go @@ -64,7 +64,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("OUTPUTKEYPATH: %v", fOutputKeyPath), }, "\n")) - deployer, err := provider.New(&provider.SshDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SshHost: fSshHost, SshPort: int32(fSshPort), SshUsername: fSshUsername, diff --git a/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go b/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go index 61418845..1784354d 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "strings" xerrors "github.com/pkg/errors" @@ -15,10 +14,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" ) -type TencentCloudCDNDeployerConfig struct { +type DeployerConfig struct { // 腾讯云 SecretId。 SecretId string `json:"secretId"` // 腾讯云 SecretKey。 @@ -27,31 +26,23 @@ type TencentCloudCDNDeployerConfig struct { Domain string `json:"domain"` } -type TencentCloudCDNDeployer struct { - config *TencentCloudCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClients *wSdkClients sslUploader uploader.Uploader } -var _ deployer.Deployer = (*TencentCloudCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) type wSdkClients struct { ssl *tcSsl.Client cdn *tcCdn.Client } -func New(config *TencentCloudCDNDeployerConfig) (*TencentCloudCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *TencentCloudCDNDeployerConfig, logger logger.Logger) (*TencentCloudCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } clients, err := createSdkClients(config.SecretId, config.SecretKey) @@ -59,7 +50,7 @@ func NewWithLogger(config *TencentCloudCDNDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create sdk clients") } - uploader, err := uploaderp.New(&uploaderp.TencentCloudSSLUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ SecretId: config.SecretId, SecretKey: config.SecretKey, }) @@ -67,15 +58,20 @@ func NewWithLogger(config *TencentCloudCDNDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &TencentCloudCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClients: clients, sslUploader: uploader, }, nil } -func (d *TencentCloudCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 SSL upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { @@ -135,7 +131,7 @@ func (d *TencentCloudCDNDeployer) Deploy(ctx context.Context, certPem string, pr return &deployer.DeployResult{}, nil } -func (d *TencentCloudCDNDeployer) getDomainsByCertificateId(cloudCertId string) ([]string, error) { +func (d *DeployerProvider) getDomainsByCertificateId(cloudCertId string) ([]string, error) { // 获取证书中的可用域名 // REF: https://cloud.tencent.com/document/product/228/42491 describeCertDomainsReq := tcCdn.NewDescribeCertDomainsRequest() @@ -156,7 +152,7 @@ func (d *TencentCloudCDNDeployer) getDomainsByCertificateId(cloudCertId string) return domains, nil } -func (d *TencentCloudCDNDeployer) getDeployedDomainsByCertificateId(cloudCertId string) ([]string, error) { +func (d *DeployerProvider) getDeployedDomainsByCertificateId(cloudCertId string) ([]string, error) { // 根据证书查询关联 CDN 域名 // REF: https://cloud.tencent.com/document/product/400/62674 describeDeployedResourcesReq := tcSsl.NewDescribeDeployedResourcesRequest() diff --git a/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn_test.go b/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn_test.go index 088251b1..0361e17b 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-cdn/tencentcloud_cdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.TencentCloudCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SecretId: fSecretId, SecretKey: fSecretKey, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/tencentcloud-clb/consts.go b/internal/pkg/core/deployer/providers/tencentcloud-clb/consts.go new file mode 100644 index 00000000..7d72d5d3 --- /dev/null +++ b/internal/pkg/core/deployer/providers/tencentcloud-clb/consts.go @@ -0,0 +1,14 @@ +package tencentcloudclb + +type ResourceType string + +const ( + // 资源类型:通过 SSL 服务部署到云资源实例。 + RESOURCE_TYPE_VIA_SSLDEPLOY = ResourceType("ssl-deploy") + // 资源类型:部署到指定负载均衡器。 + RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer") + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = ResourceType("listener") + // 资源类型:部署到指定转发规则域名。 + RESOURCE_TYPE_RULEDOMAIN = ResourceType("ruledomain") +) diff --git a/internal/pkg/core/deployer/providers/tencentcloud-clb/defines.go b/internal/pkg/core/deployer/providers/tencentcloud-clb/defines.go deleted file mode 100644 index 7e7eace9..00000000 --- a/internal/pkg/core/deployer/providers/tencentcloud-clb/defines.go +++ /dev/null @@ -1,14 +0,0 @@ -package tencentcloudclb - -type DeployResourceType string - -const ( - // 资源类型:通过 SSL 服务部署到云资源实例。 - DEPLOY_RESOURCE_VIA_SSLDEPLOY = DeployResourceType("ssl-deploy") - // 资源类型:部署到指定负载均衡器。 - DEPLOY_RESOURCE_LOADBALANCER = DeployResourceType("loadbalancer") - // 资源类型:部署到指定监听器。 - DEPLOY_RESOURCE_LISTENER = DeployResourceType("listener") - // 资源类型:部署到指定转发规则域名。 - DEPLOY_RESOURCE_RULEDOMAIN = DeployResourceType("ruledomain") -) diff --git a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go index e6982817..521f3e34 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go @@ -14,10 +14,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" ) -type TencentCloudCLBDeployerConfig struct { +type DeployerConfig struct { // 腾讯云 SecretId。 SecretId string `json:"secretId"` // 腾讯云 SecretKey。 @@ -25,43 +25,35 @@ type TencentCloudCLBDeployerConfig struct { // 腾讯云地域。 Region string `json:"region"` // 部署资源类型。 - ResourceType DeployResourceType `json:"resourceType"` + ResourceType ResourceType `json:"resourceType"` // 负载均衡器 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_SSLDEPLOY]、[DEPLOY_RESOURCE_LOADBALANCER]、[DEPLOY_RESOURCE_RULEDOMAIN] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_SSLDEPLOY]、[RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_RULEDOMAIN] 时必填。 LoadbalancerId string `json:"loadbalancerId,omitempty"` // 负载均衡监听 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_SSLDEPLOY]、[DEPLOY_RESOURCE_LOADBALANCER]、[DEPLOY_RESOURCE_LISTENER]、[DEPLOY_RESOURCE_RULEDOMAIN] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_SSLDEPLOY]、[RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER]、[RESOURCE_TYPE_RULEDOMAIN] 时必填。 ListenerId string `json:"listenerId,omitempty"` // SNI 域名或七层转发规则域名(支持泛域名)。 - // 部署资源类型为 [DEPLOY_RESOURCE_SSLDEPLOY] 时选填;部署资源类型为 [DEPLOY_RESOURCE_RULEDOMAIN] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_SSLDEPLOY] 时选填;部署资源类型为 [RESOURCE_TYPE_RULEDOMAIN] 时必填。 Domain string `json:"domain,omitempty"` } -type TencentCloudCLBDeployer struct { - config *TencentCloudCLBDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClients *wSdkClients sslUploader uploader.Uploader } -var _ deployer.Deployer = (*TencentCloudCLBDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) type wSdkClients struct { ssl *tcSsl.Client clb *tcClb.Client } -func New(config *TencentCloudCLBDeployerConfig) (*TencentCloudCLBDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *TencentCloudCLBDeployerConfig, logger logger.Logger) (*TencentCloudCLBDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } clients, err := createSdkClients(config.SecretId, config.SecretKey, config.Region) @@ -69,7 +61,7 @@ func NewWithLogger(config *TencentCloudCLBDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create sdk clients") } - uploader, err := uploaderp.New(&uploaderp.TencentCloudSSLUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ SecretId: config.SecretId, SecretKey: config.SecretKey, }) @@ -77,15 +69,20 @@ func NewWithLogger(config *TencentCloudCLBDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &TencentCloudCLBDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClients: clients, sslUploader: uploader, }, nil } -func (d *TencentCloudCLBDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 SSL upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { @@ -96,22 +93,22 @@ func (d *TencentCloudCLBDeployer) Deploy(ctx context.Context, certPem string, pr // 根据部署资源类型决定部署方式 switch d.config.ResourceType { - case DEPLOY_RESOURCE_VIA_SSLDEPLOY: + case RESOURCE_TYPE_VIA_SSLDEPLOY: if err := d.deployViaSslService(ctx, upres.CertId); err != nil { return nil, err } - case DEPLOY_RESOURCE_LOADBALANCER: + case RESOURCE_TYPE_LOADBALANCER: if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil { return nil, err } - case DEPLOY_RESOURCE_LISTENER: + case RESOURCE_TYPE_LISTENER: if err := d.deployToListener(ctx, upres.CertId); err != nil { return nil, err } - case DEPLOY_RESOURCE_RULEDOMAIN: + case RESOURCE_TYPE_RULEDOMAIN: if err := d.deployToRuleDomain(ctx, upres.CertId); err != nil { return nil, err } @@ -123,7 +120,7 @@ func (d *TencentCloudCLBDeployer) Deploy(ctx context.Context, certPem string, pr return &deployer.DeployResult{}, nil } -func (d *TencentCloudCLBDeployer) deployViaSslService(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployViaSslService(ctx context.Context, cloudCertId string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -141,7 +138,7 @@ func (d *TencentCloudCLBDeployer) deployViaSslService(ctx context.Context, cloud // 未指定 SNI,只需部署到监听器 deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s", d.config.LoadbalancerId, d.config.ListenerId)}) } else { - // 指定 SNI,需部署到域名(支持泛域名) + // 指定 SNI,需部署到域名 deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s|%s", d.config.LoadbalancerId, d.config.ListenerId, d.config.Domain)}) } deployCertificateInstanceResp, err := d.sdkClients.ssl.DeployCertificateInstance(deployCertificateInstanceReq) @@ -154,7 +151,7 @@ func (d *TencentCloudCLBDeployer) deployViaSslService(ctx context.Context, cloud return nil } -func (d *TencentCloudCLBDeployer) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -201,7 +198,7 @@ func (d *TencentCloudCLBDeployer) deployToLoadbalancer(ctx context.Context, clou return nil } -func (d *TencentCloudCLBDeployer) deployToListener(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -217,7 +214,7 @@ func (d *TencentCloudCLBDeployer) deployToListener(ctx context.Context, cloudCer return nil } -func (d *TencentCloudCLBDeployer) deployToRuleDomain(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToRuleDomain(ctx context.Context, cloudCertId string) error { if d.config.LoadbalancerId == "" { return errors.New("config `loadbalancerId` is required") } @@ -248,7 +245,7 @@ func (d *TencentCloudCLBDeployer) deployToRuleDomain(ctx context.Context, cloudC return nil } -func (d *TencentCloudCLBDeployer) modifyListenerCertificate(ctx context.Context, cloudLoadbalancerId, cloudListenerId, cloudCertId string) error { +func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudLoadbalancerId, cloudListenerId, cloudCertId string) error { // 查询监听器列表 // REF: https://cloud.tencent.com/document/api/214/30686 describeListenersReq := tcClb.NewDescribeListenersRequest() diff --git a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb_test.go b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb_test.go index 74a1e23e..0aeb1e7c 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb_test.go @@ -64,11 +64,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.TencentCloudCLBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SecretId: fSecretId, SecretKey: fSecretKey, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_VIA_SSLDEPLOY, + ResourceType: provider.RESOURCE_TYPE_VIA_SSLDEPLOY, LoadbalancerId: fLoadbalancerId, ListenerId: fListenerId, Domain: fDomain, @@ -100,11 +100,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId), }, "\n")) - deployer, err := provider.New(&provider.TencentCloudCLBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SecretId: fSecretId, SecretKey: fSecretKey, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LOADBALANCER, + ResourceType: provider.RESOURCE_TYPE_LOADBALANCER, LoadbalancerId: fLoadbalancerId, }) if err != nil { @@ -135,11 +135,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("LISTENERID: %v", fListenerId), }, "\n")) - deployer, err := provider.New(&provider.TencentCloudCLBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SecretId: fSecretId, SecretKey: fSecretKey, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LISTENER, + ResourceType: provider.RESOURCE_TYPE_LISTENER, LoadbalancerId: fLoadbalancerId, ListenerId: fListenerId, }) @@ -172,11 +172,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.TencentCloudCLBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SecretId: fSecretId, SecretKey: fSecretKey, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_RULEDOMAIN, + ResourceType: provider.RESOURCE_TYPE_RULEDOMAIN, LoadbalancerId: fLoadbalancerId, ListenerId: fListenerId, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go b/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go index c2af84fb..d3b3f881 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos.go @@ -1,4 +1,4 @@ -package tencentcloudcdn +package tencentcloudcos import ( "context" @@ -13,10 +13,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" ) -type TencentCloudCOSDeployerConfig struct { +type DeployerConfig struct { // 腾讯云 SecretId。 SecretId string `json:"secretId"` // 腾讯云 SecretKey。 @@ -29,26 +29,18 @@ type TencentCloudCOSDeployerConfig struct { Domain string `json:"domain"` } -type TencentCloudCOSDeployer struct { - config *TencentCloudCOSDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *tcSsl.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*TencentCloudCOSDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *TencentCloudCOSDeployerConfig) (*TencentCloudCOSDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *TencentCloudCOSDeployerConfig, logger logger.Logger) (*TencentCloudCOSDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.SecretId, config.SecretKey, config.Region) @@ -56,7 +48,7 @@ func NewWithLogger(config *TencentCloudCOSDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create sdk clients") } - uploader, err := uploaderp.New(&uploaderp.TencentCloudSSLUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ SecretId: config.SecretId, SecretKey: config.SecretKey, }) @@ -64,15 +56,20 @@ func NewWithLogger(config *TencentCloudCOSDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &TencentCloudCOSDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *TencentCloudCOSDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if d.config.Bucket == "" { return nil, errors.New("config `bucket` is required") } diff --git a/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos_test.go b/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos_test.go index d58e4f50..ab29a893 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-cos/tencentcloud_cos_test.go @@ -1,4 +1,4 @@ -package tencentcloudcdn_test +package tencentcloudcos_test import ( "context" @@ -60,7 +60,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.TencentCloudCOSDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SecretId: fSecretId, SecretKey: fSecretKey, Region: fRegion, diff --git a/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css.go b/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css.go index 31057017..ad5dbed5 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css.go @@ -2,7 +2,6 @@ import ( "context" - "errors" xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" @@ -12,10 +11,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" ) -type TencentCloudCSSDeployerConfig struct { +type DeployerConfig struct { // 腾讯云 SecretId。 SecretId string `json:"secretId"` // 腾讯云 SecretKey。 @@ -24,26 +23,18 @@ type TencentCloudCSSDeployerConfig struct { Domain string `json:"domain"` } -type TencentCloudCSSDeployer struct { - config *TencentCloudCSSDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *tcLive.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*TencentCloudCSSDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *TencentCloudCSSDeployerConfig) (*TencentCloudCSSDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *TencentCloudCSSDeployerConfig, logger logger.Logger) (*TencentCloudCSSDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.SecretId, config.SecretKey) @@ -51,7 +42,7 @@ func NewWithLogger(config *TencentCloudCSSDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.TencentCloudSSLUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ SecretId: config.SecretId, SecretKey: config.SecretKey, }) @@ -59,15 +50,20 @@ func NewWithLogger(config *TencentCloudCSSDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &TencentCloudCSSDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *TencentCloudCSSDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 SSL upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css_test.go b/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css_test.go index 4efe7bba..92127b3b 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-css/tencentcloud_css_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.TencentCloudCSSDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SecretId: fSecretId, SecretKey: fSecretKey, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go b/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go index 4cddbc59..e9438898 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "strings" xerrors "github.com/pkg/errors" @@ -14,10 +13,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" ) -type TencentCloudECDNDeployerConfig struct { +type DeployerConfig struct { // 腾讯云 SecretId。 SecretId string `json:"secretId"` // 腾讯云 SecretKey。 @@ -26,31 +25,23 @@ type TencentCloudECDNDeployerConfig struct { Domain string `json:"domain"` } -type TencentCloudECDNDeployer struct { - config *TencentCloudECDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClients *wSdkClients sslUploader uploader.Uploader } -var _ deployer.Deployer = (*TencentCloudECDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) type wSdkClients struct { ssl *tcSsl.Client cdn *tcCdn.Client } -func New(config *TencentCloudECDNDeployerConfig) (*TencentCloudECDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *TencentCloudECDNDeployerConfig, logger logger.Logger) (*TencentCloudECDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } clients, err := createSdkClients(config.SecretId, config.SecretKey) @@ -58,7 +49,7 @@ func NewWithLogger(config *TencentCloudECDNDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create sdk clients") } - uploader, err := uploaderp.New(&uploaderp.TencentCloudSSLUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ SecretId: config.SecretId, SecretKey: config.SecretKey, }) @@ -66,15 +57,20 @@ func NewWithLogger(config *TencentCloudECDNDeployerConfig, logger logger.Logger) return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &TencentCloudECDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClients: clients, sslUploader: uploader, }, nil } -func (d *TencentCloudECDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 SSL upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { @@ -118,7 +114,7 @@ func (d *TencentCloudECDNDeployer) Deploy(ctx context.Context, certPem string, p return &deployer.DeployResult{}, nil } -func (d *TencentCloudECDNDeployer) getDomainsByCertificateId(cloudCertId string) ([]string, error) { +func (d *DeployerProvider) getDomainsByCertificateId(cloudCertId string) ([]string, error) { // 获取证书中的可用域名 // REF: https://cloud.tencent.com/document/product/228/42491 describeCertDomainsReq := tcCdn.NewDescribeCertDomainsRequest() diff --git a/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn_test.go b/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn_test.go index 764aa5fb..da03a7ab 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-ecdn/tencentcloud_ecdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.TencentCloudECDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SecretId: fSecretId, SecretKey: fSecretKey, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo.go b/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo.go index 2de4dcc5..99669038 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo.go @@ -1,4 +1,4 @@ -package tencentcloudeteo +package tencentcloudeo import ( "context" @@ -13,10 +13,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" ) -type TencentCloudEODeployerConfig struct { +type DeployerConfig struct { // 腾讯云 SecretId。 SecretId string `json:"secretId"` // 腾讯云 SecretKey。 @@ -27,31 +27,23 @@ type TencentCloudEODeployerConfig struct { Domain string `json:"domain"` } -type TencentCloudEODeployer struct { - config *TencentCloudEODeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClients *wSdkClients sslUploader uploader.Uploader } -var _ deployer.Deployer = (*TencentCloudEODeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) type wSdkClients struct { ssl *tcSsl.Client teo *tcTeo.Client } -func New(config *TencentCloudEODeployerConfig) (*TencentCloudEODeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *TencentCloudEODeployerConfig, logger logger.Logger) (*TencentCloudEODeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } clients, err := createSdkClients(config.SecretId, config.SecretKey) @@ -59,7 +51,7 @@ func NewWithLogger(config *TencentCloudEODeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create sdk clients") } - uploader, err := uploaderp.New(&uploaderp.TencentCloudSSLUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ SecretId: config.SecretId, SecretKey: config.SecretKey, }) @@ -67,15 +59,20 @@ func NewWithLogger(config *TencentCloudEODeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &TencentCloudEODeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClients: clients, sslUploader: uploader, }, nil } -func (d *TencentCloudEODeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if d.config.ZoneId == "" { return nil, errors.New("config `zoneId` is required") } diff --git a/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo_test.go b/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo_test.go index bc5280f7..8e5b70fe 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo_test.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-eo/tencentcloud_eo_test.go @@ -1,4 +1,4 @@ -package tencentcloudeteo_test +package tencentcloudeo_test import ( "context" @@ -56,7 +56,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.TencentCloudEODeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ SecretId: fSecretId, SecretKey: fSecretKey, ZoneId: fZoneId, diff --git a/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy/tencentcloud_ssl_deploy.go b/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy/tencentcloud_ssl_deploy.go index 37c00ea4..f943ad6e 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy/tencentcloud_ssl_deploy.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-ssl-deploy/tencentcloud_ssl_deploy.go @@ -14,10 +14,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" ) -type TencentCloudSSLDeployDeployerConfig struct { +type DeployerConfig struct { // 腾讯云 SecretId。 SecretId string `json:"secretId"` // 腾讯云 SecretKey。 @@ -30,26 +30,18 @@ type TencentCloudSSLDeployDeployerConfig struct { ResourceIds []string `json:"resourceIds"` } -type TencentCloudSSLDeployDeployer struct { - config *TencentCloudSSLDeployDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *tcSsl.Client sslUploader uploader.Uploader } -var _ deployer.Deployer = (*TencentCloudSSLDeployDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *TencentCloudSSLDeployDeployerConfig) (*TencentCloudSSLDeployDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *TencentCloudSSLDeployDeployerConfig, logger logger.Logger) (*TencentCloudSSLDeployDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.SecretId, config.SecretKey, config.Region) @@ -57,7 +49,7 @@ func NewWithLogger(config *TencentCloudSSLDeployDeployerConfig, logger logger.Lo return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.TencentCloudSSLUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ SecretId: config.SecretId, SecretKey: config.SecretKey, }) @@ -65,15 +57,20 @@ func NewWithLogger(config *TencentCloudSSLDeployDeployerConfig, logger logger.Lo return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &TencentCloudSSLDeployDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *TencentCloudSSLDeployDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if d.config.ResourceType == "" { return nil, errors.New("config `resourceType` is required") } diff --git a/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod.go b/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod.go new file mode 100644 index 00000000..fbce8d6f --- /dev/null +++ b/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod.go @@ -0,0 +1,104 @@ +package tencentcloudvod + +import ( + "context" + + xerrors "github.com/pkg/errors" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" + tcVod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod/v20180717" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" +) + +type DeployerConfig struct { + // 腾讯云 SecretId。 + SecretId string `json:"secretId"` + // 腾讯云 SecretKey。 + SecretKey string `json:"secretKey"` + // 点播应用 ID。 + SubAppId int64 `json:"subAppId"` + // 点播加速域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *tcVod.Client + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.SecretId, config.SecretKey) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk clients") + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + SecretId: config.SecretId, + SecretKey: config.SecretKey, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 上传证书到 SSL + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", upres) + } + + // 设置点播域名 HTTPS 证书 + // REF: https://cloud.tencent.com/document/api/266/102015 + setVodDomainCertificateReq := tcVod.NewSetVodDomainCertificateRequest() + setVodDomainCertificateReq.Domain = common.StringPtr(d.config.Domain) + setVodDomainCertificateReq.Operation = common.StringPtr("Set") + setVodDomainCertificateReq.CertID = common.StringPtr(upres.CertId) + if d.config.SubAppId != 0 { + setVodDomainCertificateReq.SubAppId = common.Uint64Ptr(uint64(d.config.SubAppId)) + } + setVodDomainCertificateResp, err := d.sdkClient.SetVodDomainCertificate(setVodDomainCertificateReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.SetVodDomainCertificate'") + } else { + d.logger.Logt("已设置点播域名 HTTPS 证书", setVodDomainCertificateResp.Response) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(secretId, secretKey string) (*tcVod.Client, error) { + credential := common.NewCredential(secretId, secretKey) + client, err := tcVod.NewClient(credential, "", profile.NewClientProfile()) + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod_test.go b/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod_test.go new file mode 100644 index 00000000..60871a31 --- /dev/null +++ b/internal/pkg/core/deployer/providers/tencentcloud-vod/tencentcloud_vod_test.go @@ -0,0 +1,87 @@ +package tencentcloudvod_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-vod" +) + +var ( + fInputCertPath string + fInputKeyPath string + fSecretId string + fSecretKey string + fRegion string + fDomain string + fSubAppId int64 + fInstanceId string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_TENCENTCLOUDVOD_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fSecretId, argsPrefix+"SECRETID", "", "") + flag.StringVar(&fSecretKey, argsPrefix+"SECRETKEY", "", "") + flag.StringVar(&fRegion, argsPrefix+"REGION", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") + flag.Int64Var(&fSubAppId, argsPrefix+"SUBAPPID", 0, "") + flag.StringVar(&fInstanceId, argsPrefix+"INSTANCEID", "", "") +} + +/* +Shell command to run this test: + + go test -v ./tencentcloud_vod_test.go -args \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDVOD_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDVOD_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDVOD_SECRETID="your-secret-id" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDVOD_SECRETKEY="your-secret-key" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDVOD_REGION="ap-guangzhou" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDVOD_SUBAPPID="your-app-id" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDVOD_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("SECRETID: %v", fSecretId), + fmt.Sprintf("SECRETKEY: %v", fSecretKey), + fmt.Sprintf("REGION: %v", fRegion), + fmt.Sprintf("DOMAIN: %v", fDomain), + fmt.Sprintf("INSTANCEID: %v", fInstanceId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + SecretId: fSecretId, + SecretKey: fSecretKey, + Region: fRegion, + SubAppId: fSubAppId, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf.go b/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf.go new file mode 100644 index 00000000..7919516c --- /dev/null +++ b/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf.go @@ -0,0 +1,131 @@ +package tencentcloudwaf + +import ( + "context" + "errors" + + xerrors "github.com/pkg/errors" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" + tcWaf "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf/v20180125" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/tencentcloud-ssl" +) + +type DeployerConfig struct { + // 腾讯云 SecretId。 + SecretId string `json:"secretId"` + // 腾讯云 SecretKey。 + SecretKey string `json:"secretKey"` + // 腾讯云地域。 + Region string `json:"region"` + // 防护域名(不支持泛域名)。 + Domain string `json:"domain"` + // 防护域名 ID。 + DomainId string `json:"domainId"` + // 防护域名所属实例 ID。 + InstanceId string `json:"instanceId"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *tcWaf.Client + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.SecretId, config.SecretKey, config.Region) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk clients") + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + SecretId: config.SecretId, + SecretKey: config.SecretKey, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + if d.config.Domain == "" { + return nil, errors.New("config `domain` is required") + } + if d.config.DomainId == "" { + return nil, errors.New("config `domainId` is required") + } + if d.config.InstanceId == "" { + return nil, errors.New("config `instanceId` is required") + } + + // 上传证书到 SSL + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", upres) + } + + // 查询单个 SaaS 型 WAF 域名详情 + // REF: https://cloud.tencent.com/document/api/627/82938 + describeDomainDetailsSaasReq := tcWaf.NewDescribeDomainDetailsSaasRequest() + describeDomainDetailsSaasReq.Domain = common.StringPtr(d.config.Domain) + describeDomainDetailsSaasReq.DomainId = common.StringPtr(d.config.DomainId) + describeDomainDetailsSaasReq.InstanceId = common.StringPtr(d.config.InstanceId) + describeDomainDetailsSaasResp, err := d.sdkClient.DescribeDomainDetailsSaas(describeDomainDetailsSaasReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDomainDetailsSaas'") + } else { + d.logger.Logt("已查询到 SaaS 型 WAF 域名详情", describeDomainDetailsSaasResp.Response) + } + + // 编辑 SaaS 型 WAF 域名 + // REF: https://cloud.tencent.com/document/api/627/94309 + modifySpartaProtectionReq := tcWaf.NewModifySpartaProtectionRequest() + modifySpartaProtectionReq.Domain = common.StringPtr(d.config.Domain) + modifySpartaProtectionReq.DomainId = common.StringPtr(d.config.DomainId) + modifySpartaProtectionReq.InstanceID = common.StringPtr(d.config.InstanceId) + modifySpartaProtectionReq.CertType = common.Int64Ptr(2) + modifySpartaProtectionReq.SSLId = common.StringPtr(upres.CertId) + modifySpartaProtectionResp, err := d.sdkClient.ModifySpartaProtection(modifySpartaProtectionReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifySpartaProtection'") + } else { + d.logger.Logt("已编辑 SaaS 型 WAF 域名", modifySpartaProtectionResp.Response) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(secretId, secretKey, region string) (*tcWaf.Client, error) { + credential := common.NewCredential(secretId, secretKey) + client, err := tcWaf.NewClient(credential, region, profile.NewClientProfile()) + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf_test.go b/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf_test.go new file mode 100644 index 00000000..42ba1ffe --- /dev/null +++ b/internal/pkg/core/deployer/providers/tencentcloud-waf/tencentcloud_waf_test.go @@ -0,0 +1,89 @@ +package tencentcloudwaf_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-waf" +) + +var ( + fInputCertPath string + fInputKeyPath string + fSecretId string + fSecretKey string + fRegion string + fDomain string + fDomainId string + fInstanceId string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_TENCENTCLOUDWAF_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fSecretId, argsPrefix+"SECRETID", "", "") + flag.StringVar(&fSecretKey, argsPrefix+"SECRETKEY", "", "") + flag.StringVar(&fRegion, argsPrefix+"REGION", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") + flag.StringVar(&fDomainId, argsPrefix+"DOMAINID", "", "") + flag.StringVar(&fInstanceId, argsPrefix+"INSTANCEID", "", "") +} + +/* +Shell command to run this test: + + go test -v ./tencentcloud_waf_test.go -args \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDWAF_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDWAF_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDWAF_SECRETID="your-secret-id" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDWAF_SECRETKEY="your-secret-key" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDWAF_REGION="ap-guangzhou" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDWAF_DOMAIN="example.com" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDWAF_DOMAINID="your-domain-id" \ + --CERTIMATE_DEPLOYER_TENCENTCLOUDWAF_INSTANCEID="your-instance-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("SECRETID: %v", fSecretId), + fmt.Sprintf("SECRETKEY: %v", fSecretKey), + fmt.Sprintf("REGION: %v", fRegion), + fmt.Sprintf("DOMAIN: %v", fDomain), + fmt.Sprintf("INSTANCEID: %v", fInstanceId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + SecretId: fSecretId, + SecretKey: fSecretKey, + Region: fRegion, + Domain: fDomain, + DomainId: fDomainId, + InstanceId: fInstanceId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go index fa104ba3..8c19621b 100644 --- a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go +++ b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn.go @@ -13,10 +13,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ucloud-ussl" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ucloud-ussl" ) -type UCloudUCDNDeployerConfig struct { +type DeployerConfig struct { // 优刻得 API 私钥。 PrivateKey string `json:"privateKey"` // 优刻得 API 公钥。 @@ -27,26 +27,18 @@ type UCloudUCDNDeployerConfig struct { DomainId string `json:"domainId"` } -type UCloudUCDNDeployer struct { - config *UCloudUCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *uCdn.UCDNClient sslUploader uploader.Uploader } -var _ deployer.Deployer = (*UCloudUCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *UCloudUCDNDeployerConfig) (*UCloudUCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *UCloudUCDNDeployerConfig, logger logger.Logger) (*UCloudUCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.PrivateKey, config.PublicKey) @@ -54,7 +46,7 @@ func NewWithLogger(config *UCloudUCDNDeployerConfig, logger logger.Logger) (*UCl return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.UCloudUSSLUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ PrivateKey: config.PrivateKey, PublicKey: config.PublicKey, ProjectId: config.ProjectId, @@ -63,15 +55,20 @@ func NewWithLogger(config *UCloudUCDNDeployerConfig, logger logger.Logger) (*UCl return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &UCloudUCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *UCloudUCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 USSL upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go index d8b703b5..fc952da1 100644 --- a/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go +++ b/internal/pkg/core/deployer/providers/ucloud-ucdn/ucloud_ucdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomainId), }, "\n")) - deployer, err := provider.New(&provider.UCloudUCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ PrivateKey: fPrivateKey, PublicKey: fPublicKey, DomainId: fDomainId, diff --git a/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3.go b/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3.go index ccf03224..592a1cf6 100644 --- a/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3.go +++ b/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3.go @@ -2,7 +2,6 @@ import ( "context" - "errors" xerrors "github.com/pkg/errors" usdk "github.com/ucloud/ucloud-sdk-go/ucloud" @@ -11,11 +10,11 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ucloud-ussl" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ucloud-ussl" usdkFile "github.com/usual2970/certimate/internal/pkg/vendors/ucloud-sdk/ufile" ) -type UCloudUS3DeployerConfig struct { +type DeployerConfig struct { // 优刻得 API 私钥。 PrivateKey string `json:"privateKey"` // 优刻得 API 公钥。 @@ -30,26 +29,18 @@ type UCloudUS3DeployerConfig struct { Domain string `json:"domain"` } -type UCloudUS3Deployer struct { - config *UCloudUS3DeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *usdkFile.UFileClient sslUploader uploader.Uploader } -var _ deployer.Deployer = (*UCloudUS3Deployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *UCloudUS3DeployerConfig) (*UCloudUS3Deployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *UCloudUS3DeployerConfig, logger logger.Logger) (*UCloudUS3Deployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.PrivateKey, config.PublicKey, config.Region) @@ -57,7 +48,7 @@ func NewWithLogger(config *UCloudUS3DeployerConfig, logger logger.Logger) (*UClo return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.UCloudUSSLUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ PrivateKey: config.PrivateKey, PublicKey: config.PublicKey, ProjectId: config.ProjectId, @@ -66,15 +57,20 @@ func NewWithLogger(config *UCloudUS3DeployerConfig, logger logger.Logger) (*UClo return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &UCloudUS3Deployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *UCloudUS3Deployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 USSL upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3_test.go b/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3_test.go index e4175bfc..52f78664 100644 --- a/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3_test.go +++ b/internal/pkg/core/deployer/providers/ucloud-us3/ucloud_us3_test.go @@ -60,7 +60,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.UCloudUS3DeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ PrivateKey: fPrivateKey, PublicKey: fPublicKey, Region: fRegion, diff --git a/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn.go b/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn.go index de3e76e4..229c3c01 100644 --- a/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn.go +++ b/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn.go @@ -12,10 +12,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-cdn" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-cdn" ) -type VolcEngineCDNDeployerConfig struct { +type DeployerConfig struct { // 火山引擎 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 火山引擎 AccessKeySecret。 @@ -24,33 +24,25 @@ type VolcEngineCDNDeployerConfig struct { Domain string `json:"domain"` } -type VolcEngineCDNDeployer struct { - config *VolcEngineCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *veCdn.CDN sslUploader uploader.Uploader } -var _ deployer.Deployer = (*VolcEngineCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *VolcEngineCDNDeployerConfig) (*VolcEngineCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *VolcEngineCDNDeployerConfig, logger logger.Logger) (*VolcEngineCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client := veCdn.NewInstance() client.Client.SetAccessKey(config.AccessKeyId) client.Client.SetSecretKey(config.AccessKeySecret) - uploader, err := uploaderp.New(&uploaderp.VolcEngineCDNUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: config.AccessKeyId, AccessKeySecret: config.AccessKeySecret, }) @@ -58,15 +50,20 @@ func NewWithLogger(config *VolcEngineCDNDeployerConfig, logger logger.Logger) (* return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &VolcEngineCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *VolcEngineCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 CDN upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn_test.go b/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn_test.go index 3a3ff62d..c94b9828 100644 --- a/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-cdn/volcengine_cdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.VolcEngineCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/volcengine-clb/consts.go b/internal/pkg/core/deployer/providers/volcengine-clb/consts.go new file mode 100644 index 00000000..dc1e992b --- /dev/null +++ b/internal/pkg/core/deployer/providers/volcengine-clb/consts.go @@ -0,0 +1,8 @@ +package volcengineclb + +type ResourceType string + +const ( + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = ResourceType("listener") +) diff --git a/internal/pkg/core/deployer/providers/volcengine-clb/defines.go b/internal/pkg/core/deployer/providers/volcengine-clb/defines.go deleted file mode 100644 index ec211199..00000000 --- a/internal/pkg/core/deployer/providers/volcengine-clb/defines.go +++ /dev/null @@ -1,8 +0,0 @@ -package volcengineclb - -type DeployResourceType string - -const ( - // 资源类型:部署到指定监听器。 - DEPLOY_RESOURCE_LISTENER = DeployResourceType("listener") -) diff --git a/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb.go b/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb.go index 08c096fd..73372781 100644 --- a/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb.go +++ b/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb.go @@ -13,10 +13,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-certcenter" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-certcenter" ) -type VolcEngineCLBDeployerConfig struct { +type DeployerConfig struct { // 火山引擎 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 火山引擎 AccessKeySecret。 @@ -24,32 +24,24 @@ type VolcEngineCLBDeployerConfig struct { // 火山引擎地域。 Region string `json:"region"` // 部署资源类型。 - ResourceType DeployResourceType `json:"resourceType"` + ResourceType ResourceType `json:"resourceType"` // 负载均衡监听器 ID。 - // 部署资源类型为 [DEPLOY_RESOURCE_LISTENER] 时必填。 + // 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。 ListenerId string `json:"listenerId,omitempty"` } -type VolcEngineCLBDeployer struct { - config *VolcEngineCLBDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *veClb.CLB sslUploader uploader.Uploader } -var _ deployer.Deployer = (*VolcEngineCLBDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *VolcEngineCLBDeployerConfig) (*VolcEngineCLBDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *VolcEngineCLBDeployerConfig, logger logger.Logger) (*VolcEngineCLBDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -57,7 +49,7 @@ func NewWithLogger(config *VolcEngineCLBDeployerConfig, logger logger.Logger) (* return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.VolcEngineCertCenterUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: config.AccessKeyId, AccessKeySecret: config.AccessKeySecret, Region: config.Region, @@ -66,15 +58,19 @@ func NewWithLogger(config *VolcEngineCLBDeployerConfig, logger logger.Logger) (* return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &VolcEngineCLBDeployer{ - logger: logger, + return &DeployerProvider{ config: config, sdkClient: client, sslUploader: uploader, }, nil } -func (d *VolcEngineCLBDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到证书中心 upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { @@ -85,7 +81,7 @@ func (d *VolcEngineCLBDeployer) Deploy(ctx context.Context, certPem string, priv // 根据部署资源类型决定部署方式 switch d.config.ResourceType { - case DEPLOY_RESOURCE_LISTENER: + case RESOURCE_TYPE_LISTENER: if err := d.deployToListener(ctx, upres.CertId); err != nil { return nil, err } @@ -97,7 +93,7 @@ func (d *VolcEngineCLBDeployer) Deploy(ctx context.Context, certPem string, priv return &deployer.DeployResult{}, nil } -func (d *VolcEngineCLBDeployer) deployToListener(ctx context.Context, cloudCertId string) error { +func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error { if d.config.ListenerId == "" { return errors.New("config `listenerId` is required") } diff --git a/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb_test.go b/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb_test.go index 45d79f44..0b589f88 100644 --- a/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-clb/volcengine_clb_test.go @@ -56,11 +56,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("LISTENERID: %v", fListenerId), }, "\n")) - deployer, err := provider.New(&provider.VolcEngineCLBDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Region: fRegion, - ResourceType: provider.DEPLOY_RESOURCE_LISTENER, + ResourceType: provider.RESOURCE_TYPE_LISTENER, ListenerId: fListenerId, }) if err != nil { diff --git a/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn.go b/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn.go index 0f9f8e51..835eae4b 100644 --- a/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn.go +++ b/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "strings" xerrors "github.com/pkg/errors" @@ -13,10 +12,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-certcenter" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-certcenter" ) -type VolcEngineDCDNDeployerConfig struct { +type DeployerConfig struct { // 火山引擎 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 火山引擎 AccessKeySecret。 @@ -27,26 +26,18 @@ type VolcEngineDCDNDeployerConfig struct { Domain string `json:"domain"` } -type VolcEngineDCDNDeployer struct { - config *VolcEngineDCDNDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *veDcdn.DCDN sslUploader uploader.Uploader } -var _ deployer.Deployer = (*VolcEngineDCDNDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *VolcEngineDCDNDeployerConfig) (*VolcEngineDCDNDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *VolcEngineDCDNDeployerConfig, logger logger.Logger) (*VolcEngineDCDNDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -54,7 +45,7 @@ func NewWithLogger(config *VolcEngineDCDNDeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.VolcEngineCertCenterUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: config.AccessKeyId, AccessKeySecret: config.AccessKeySecret, Region: config.Region, @@ -63,15 +54,20 @@ func NewWithLogger(config *VolcEngineDCDNDeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &VolcEngineDCDNDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *VolcEngineDCDNDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到证书中心 upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn_test.go b/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn_test.go index 2236b1e1..1d34a295 100644 --- a/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-dcdn/volcengine_dcdn_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.VolcEngineDCDNDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex.go b/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex.go new file mode 100644 index 00000000..57d133ec --- /dev/null +++ b/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex.go @@ -0,0 +1,144 @@ +package volcengineimagex + +import ( + "context" + "errors" + + xerrors "github.com/pkg/errors" + veBase "github.com/volcengine/volc-sdk-golang/base" + veImageX "github.com/volcengine/volc-sdk-golang/service/imagex/v2" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-certcenter" +) + +type DeployerConfig struct { + // 火山引擎 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 火山引擎 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 火山引擎地域。 + Region string `json:"region"` + // 服务 ID。 + ServiceId string `json:"serviceId"` + // 自定义域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *veImageX.Imagex + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: config.AccessKeyId, + AccessKeySecret: config.AccessKeySecret, + Region: config.Region, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + if d.config.ServiceId == "" { + return nil, errors.New("config `serviceId` is required") + } + if d.config.Domain == "" { + return nil, errors.New("config `domain` is required") + } + + // 上传证书到证书中心 + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", upres) + } + + // 获取域名配置 + // REF: https://www.volcengine.com/docs/508/9366 + getDomainConfigReq := &veImageX.GetDomainConfigQuery{ + ServiceID: d.config.ServiceId, + DomainName: d.config.Domain, + } + getDomainConfigResp, err := d.sdkClient.GetDomainConfig(context.TODO(), getDomainConfigReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'imagex.GetDomainConfig'") + } else { + d.logger.Logt("已获取域名配置", getDomainConfigResp) + } + + // 更新 HTTPS 配置 + // REF: https://www.volcengine.com/docs/508/66012 + updateHttpsReq := &veImageX.UpdateHTTPSReq{ + UpdateHTTPSQuery: &veImageX.UpdateHTTPSQuery{ + ServiceID: d.config.ServiceId, + }, + UpdateHTTPSBody: &veImageX.UpdateHTTPSBody{ + Domain: getDomainConfigResp.Result.Domain, + HTTPS: &veImageX.UpdateHTTPSBodyHTTPS{ + CertID: upres.CertId, + EnableHTTP2: getDomainConfigResp.Result.HTTPSConfig.EnableHTTP2, + EnableHTTPS: getDomainConfigResp.Result.HTTPSConfig.EnableHTTPS, + EnableOcsp: getDomainConfigResp.Result.HTTPSConfig.EnableOcsp, + TLSVersions: getDomainConfigResp.Result.HTTPSConfig.TLSVersions, + EnableForceRedirect: getDomainConfigResp.Result.HTTPSConfig.EnableForceRedirect, + ForceRedirectType: getDomainConfigResp.Result.HTTPSConfig.ForceRedirectType, + ForceRedirectCode: getDomainConfigResp.Result.HTTPSConfig.ForceRedirectCode, + }, + }, + } + updateHttpsResp, err := d.sdkClient.UpdateHTTPS(context.TODO(), updateHttpsReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'imagex.UpdateHttps'") + } else { + d.logger.Logt("已更新 HTTPS 配置", updateHttpsResp) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret, region string) (*veImageX.Imagex, error) { + var instance *veImageX.Imagex + if region == "" { + instance = veImageX.NewInstance() + } else { + instance = veImageX.NewInstanceWithRegion(region) + } + + instance.SetCredential(veBase.Credentials{ + AccessKeyID: accessKeyId, + SecretAccessKey: accessKeySecret, + }) + + return instance, nil +} diff --git a/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex_test.go b/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex_test.go new file mode 100644 index 00000000..f2acb8f2 --- /dev/null +++ b/internal/pkg/core/deployer/providers/volcengine-imagex/volcengine_imagex_test.go @@ -0,0 +1,85 @@ +package volcengineimagex_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-imagex" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fRegion string + fServiceId string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_VOLCENGINEIMAGEX_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fRegion, argsPrefix+"REGION", "", "") + flag.StringVar(&fServiceId, argsPrefix+"SERVICEID", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./volcengine_imagex_test.go -args \ + --CERTIMATE_DEPLOYER_VOLCENGINEIMAGEX_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_VOLCENGINEIMAGEX_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_VOLCENGINEIMAGEX_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_VOLCENGINEIMAGEX_ACCESSKEYSECRET="your-access-key-secret" \ + --CERTIMATE_DEPLOYER_VOLCENGINEIMAGEX_REGION="cn-north-1" \ + --CERTIMATE_DEPLOYER_VOLCENGINEIMAGEX_SERVICEID="your-service-id" \ + --CERTIMATE_DEPLOYER_VOLCENGINEIMAGEX_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("REGION: %v", fRegion), + fmt.Sprintf("SERVICEID: %v", fServiceId), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Region: fRegion, + ServiceId: fServiceId, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live.go b/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live.go index b23f4c6c..240e5c9c 100644 --- a/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live.go +++ b/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live.go @@ -13,10 +13,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-live" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-live" ) -type VolcEngineLiveDeployerConfig struct { +type DeployerConfig struct { // 火山引擎 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 火山引擎 AccessKeySecret。 @@ -25,33 +25,25 @@ type VolcEngineLiveDeployerConfig struct { Domain string `json:"domain"` } -type VolcEngineLiveDeployer struct { - config *VolcEngineLiveDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *veLive.Live sslUploader uploader.Uploader } -var _ deployer.Deployer = (*VolcEngineLiveDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *VolcEngineLiveDeployerConfig) (*VolcEngineLiveDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *VolcEngineLiveDeployerConfig, logger logger.Logger) (*VolcEngineLiveDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client := veLive.NewInstance() client.SetAccessKey(config.AccessKeyId) client.SetSecretKey(config.AccessKeySecret) - uploader, err := uploaderp.New(&uploaderp.VolcEngineLiveUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: config.AccessKeyId, AccessKeySecret: config.AccessKeySecret, }) @@ -59,15 +51,20 @@ func NewWithLogger(config *VolcEngineLiveDeployerConfig, logger logger.Logger) ( return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &VolcEngineLiveDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *VolcEngineLiveDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { // 上传证书到 Live upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) if err != nil { diff --git a/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live_test.go b/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live_test.go index 28097c75..2222e7b9 100644 --- a/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-live/volcengine_live_test.go @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.VolcEngineLiveDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, Domain: fDomain, diff --git a/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos.go b/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos.go index 9a238f27..142a8b9f 100644 --- a/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos.go +++ b/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos.go @@ -11,10 +11,10 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" "github.com/usual2970/certimate/internal/pkg/core/uploader" - uploaderp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-certcenter" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-certcenter" ) -type VolcEngineTOSDeployerConfig struct { +type DeployerConfig struct { // 火山引擎 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 火山引擎 AccessKeySecret。 @@ -27,26 +27,18 @@ type VolcEngineTOSDeployerConfig struct { Domain string `json:"domain"` } -type VolcEngineTOSDeployer struct { - config *VolcEngineTOSDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger sdkClient *veTos.ClientV2 sslUploader uploader.Uploader } -var _ deployer.Deployer = (*VolcEngineTOSDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *VolcEngineTOSDeployerConfig) (*VolcEngineTOSDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *VolcEngineTOSDeployerConfig, logger logger.Logger) (*VolcEngineTOSDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -54,7 +46,7 @@ func NewWithLogger(config *VolcEngineTOSDeployerConfig, logger logger.Logger) (* return nil, xerrors.Wrap(err, "failed to create sdk client") } - uploader, err := uploaderp.New(&uploaderp.VolcEngineCertCenterUploaderConfig{ + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ AccessKeyId: config.AccessKeyId, AccessKeySecret: config.AccessKeySecret, Region: config.Region, @@ -63,15 +55,20 @@ func NewWithLogger(config *VolcEngineTOSDeployerConfig, logger logger.Logger) (* return nil, xerrors.Wrap(err, "failed to create ssl uploader") } - return &VolcEngineTOSDeployer{ - logger: logger, + return &DeployerProvider{ config: config, + logger: logger.NewNilLogger(), sdkClient: client, sslUploader: uploader, }, nil } -func (d *VolcEngineTOSDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { if d.config.Bucket == "" { return nil, errors.New("config `bucket` is required") } diff --git a/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos_test.go b/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos_test.go index b70130c4..0c9b398a 100644 --- a/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos_test.go +++ b/internal/pkg/core/deployer/providers/volcengine-tos/volcengine_tos_test.go @@ -60,9 +60,11 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) - deployer, err := provider.New(&provider.VolcEngineTOSDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, + Region: fRegion, + Bucket: fBucket, Domain: fDomain, }) if err != nil { diff --git a/internal/pkg/core/deployer/providers/webhook/webhook.go b/internal/pkg/core/deployer/providers/webhook/webhook.go index 694befc5..7a9edfda 100644 --- a/internal/pkg/core/deployer/providers/webhook/webhook.go +++ b/internal/pkg/core/deployer/providers/webhook/webhook.go @@ -3,7 +3,6 @@ package webhook import ( "context" "encoding/json" - "errors" "strings" "time" @@ -15,32 +14,24 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/certs" ) -type WebhookDeployerConfig struct { +type DeployerConfig struct { // Webhook URL。 WebhookUrl string `json:"webhookUrl"` // Webhook 回调数据(JSON 格式)。 WebhookData string `json:"webhookData,omitempty"` } -type WebhookDeployer struct { - config *WebhookDeployerConfig +type DeployerProvider struct { + config *DeployerConfig logger logger.Logger httpClient *resty.Client } -var _ deployer.Deployer = (*WebhookDeployer)(nil) +var _ deployer.Deployer = (*DeployerProvider)(nil) -func New(config *WebhookDeployerConfig) (*WebhookDeployer, error) { - return NewWithLogger(config, logger.NewNilLogger()) -} - -func NewWithLogger(config *WebhookDeployerConfig, logger logger.Logger) (*WebhookDeployer, error) { +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { if config == nil { - return nil, errors.New("config is nil") - } - - if logger == nil { - return nil, errors.New("logger is nil") + panic("config is nil") } client := resty.New(). @@ -48,14 +39,19 @@ func NewWithLogger(config *WebhookDeployerConfig, logger logger.Logger) (*Webhoo SetRetryCount(3). SetRetryWaitTime(5 * time.Second) - return &WebhookDeployer{ + return &DeployerProvider{ config: config, - logger: logger, + logger: logger.NewNilLogger(), httpClient: client, }, nil } -func (d *WebhookDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, xerrors.Wrap(err, "failed to parse x509") diff --git a/internal/pkg/core/deployer/providers/webhook/webhook_test.go b/internal/pkg/core/deployer/providers/webhook/webhook_test.go index 92b57e56..7dd6f24a 100644 --- a/internal/pkg/core/deployer/providers/webhook/webhook_test.go +++ b/internal/pkg/core/deployer/providers/webhook/webhook_test.go @@ -48,7 +48,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("WEBHOOKDATA: %v", fWebhookData), }, "\n")) - deployer, err := provider.New(&provider.WebhookDeployerConfig{ + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ WebhookUrl: fWebhookUrl, WebhookData: fWebhookData, }) diff --git a/internal/pkg/core/logger/builtin.go b/internal/pkg/core/logger/builtin.go index 9787817d..fa8932dc 100644 --- a/internal/pkg/core/logger/builtin.go +++ b/internal/pkg/core/logger/builtin.go @@ -35,8 +35,8 @@ func (l *DefaultLogger) Logt(tag string, data ...any) { reflect.Float32, reflect.Float64: s = fmt.Sprintf("%v", v) default: - jsonData, _ := json.Marshal(v) - s = string(jsonData) + jbytes, _ := json.Marshal(v) + s = string(jbytes) } } diff --git a/internal/pkg/core/notifier/providers/bark/bark.go b/internal/pkg/core/notifier/providers/bark/bark.go index 74683f82..f9589096 100644 --- a/internal/pkg/core/notifier/providers/bark/bark.go +++ b/internal/pkg/core/notifier/providers/bark/bark.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "github.com/nikoksr/notify" "github.com/nikoksr/notify/service/bark" @@ -10,7 +9,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/notifier" ) -type BarkNotifierConfig struct { +type NotifierConfig struct { // Bark 服务地址。 // 零值时默认使用官方服务器。 ServerUrl string `json:"serverUrl"` @@ -18,23 +17,23 @@ type BarkNotifierConfig struct { DeviceKey string `json:"deviceKey"` } -type BarkNotifier struct { - config *BarkNotifierConfig +type NotifierProvider struct { + config *NotifierConfig } -var _ notifier.Notifier = (*BarkNotifier)(nil) +var _ notifier.Notifier = (*NotifierProvider)(nil) -func New(config *BarkNotifierConfig) (*BarkNotifier, error) { +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - return &BarkNotifier{ + return &NotifierProvider{ config: config, }, nil } -func (n *BarkNotifier) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { var srv notify.Notifier if n.config.ServerUrl == "" { srv = bark.New(n.config.DeviceKey) diff --git a/internal/pkg/core/notifier/providers/bark/bark_test.go b/internal/pkg/core/notifier/providers/bark/bark_test.go index e3045cae..bd0441b4 100644 --- a/internal/pkg/core/notifier/providers/bark/bark_test.go +++ b/internal/pkg/core/notifier/providers/bark/bark_test.go @@ -44,7 +44,7 @@ func TestNotify(t *testing.T) { fmt.Sprintf("DEVICEKEY: %v", fDeviceKey), }, "\n")) - notifier, err := provider.New(&provider.BarkNotifierConfig{ + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ ServerUrl: fServerUrl, DeviceKey: fDeviceKey, }) diff --git a/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go b/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go index 52fe5f27..f8eba8a7 100644 --- a/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go +++ b/internal/pkg/core/notifier/providers/dingtalk/dingtalk.go @@ -2,37 +2,36 @@ import ( "context" - "errors" "github.com/nikoksr/notify/service/dingding" "github.com/usual2970/certimate/internal/pkg/core/notifier" ) -type DingTalkNotifierConfig struct { +type NotifierConfig struct { // 钉钉机器人的 Token。 AccessToken string `json:"accessToken"` // 钉钉机器人的 Secret。 Secret string `json:"secret"` } -type DingTalkNotifier struct { - config *DingTalkNotifierConfig +type NotifierProvider struct { + config *NotifierConfig } -var _ notifier.Notifier = (*DingTalkNotifier)(nil) +var _ notifier.Notifier = (*NotifierProvider)(nil) -func New(config *DingTalkNotifierConfig) (*DingTalkNotifier, error) { +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - return &DingTalkNotifier{ + return &NotifierProvider{ config: config, }, nil } -func (n *DingTalkNotifier) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { srv := dingding.New(&dingding.Config{ Token: n.config.AccessToken, Secret: n.config.Secret, diff --git a/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go b/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go index 621a57ca..836cf498 100644 --- a/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go +++ b/internal/pkg/core/notifier/providers/dingtalk/dingtalk_test.go @@ -43,7 +43,7 @@ func TestNotify(t *testing.T) { fmt.Sprintf("SECRET: %v", fSecret), }, "\n")) - notifier, err := provider.New(&provider.DingTalkNotifierConfig{ + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ AccessToken: fAccessToken, Secret: fSecret, }) diff --git a/internal/pkg/core/notifier/providers/email/email.go b/internal/pkg/core/notifier/providers/email/email.go index 127c5528..4a69d50a 100644 --- a/internal/pkg/core/notifier/providers/email/email.go +++ b/internal/pkg/core/notifier/providers/email/email.go @@ -3,7 +3,6 @@ import ( "context" "crypto/tls" - "errors" "fmt" "net/smtp" @@ -12,7 +11,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/notifier" ) -type EmailNotifierConfig struct { +type NotifierConfig struct { // SMTP 服务器地址。 SmtpHost string `json:"smtpHost"` // SMTP 服务器端口。 @@ -30,23 +29,23 @@ type EmailNotifierConfig struct { ReceiverAddress string `json:"receiverAddress"` } -type EmailNotifier struct { - config *EmailNotifierConfig +type NotifierProvider struct { + config *NotifierConfig } -var _ notifier.Notifier = (*EmailNotifier)(nil) +var _ notifier.Notifier = (*NotifierProvider)(nil) -func New(config *EmailNotifierConfig) (*EmailNotifier, error) { +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - return &EmailNotifier{ + return &NotifierProvider{ config: config, }, nil } -func (n *EmailNotifier) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { var smtpAuth smtp.Auth if n.config.Username != "" || n.config.Password != "" { smtpAuth = smtp.PlainAuth("", n.config.Username, n.config.Password, n.config.SmtpHost) diff --git a/internal/pkg/core/notifier/providers/email/email_test.go b/internal/pkg/core/notifier/providers/email/email_test.go index 1c52d4de..df006c24 100644 --- a/internal/pkg/core/notifier/providers/email/email_test.go +++ b/internal/pkg/core/notifier/providers/email/email_test.go @@ -64,7 +64,7 @@ func TestNotify(t *testing.T) { fmt.Sprintf("RECEIVERADDRESS: %v", fReceiverAddress), }, "\n")) - notifier, err := provider.New(&provider.EmailNotifierConfig{ + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ SmtpHost: fSmtpHost, SmtpPort: int32(fSmtpPort), SmtpTLS: fSmtpTLS, diff --git a/internal/pkg/core/notifier/providers/lark/lark.go b/internal/pkg/core/notifier/providers/lark/lark.go index 4714e280..9c92691a 100644 --- a/internal/pkg/core/notifier/providers/lark/lark.go +++ b/internal/pkg/core/notifier/providers/lark/lark.go @@ -2,35 +2,34 @@ import ( "context" - "errors" "github.com/nikoksr/notify/service/lark" "github.com/usual2970/certimate/internal/pkg/core/notifier" ) -type LarkNotifierConfig struct { +type NotifierConfig struct { // 飞书机器人 Webhook 地址。 WebhookUrl string `json:"webhookUrl"` } -type LarkNotifier struct { - config *LarkNotifierConfig +type NotifierProvider struct { + config *NotifierConfig } -var _ notifier.Notifier = (*LarkNotifier)(nil) +var _ notifier.Notifier = (*NotifierProvider)(nil) -func New(config *LarkNotifierConfig) (*LarkNotifier, error) { +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - return &LarkNotifier{ + return &NotifierProvider{ config: config, }, nil } -func (n *LarkNotifier) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { srv := lark.NewWebhookService(n.config.WebhookUrl) err = srv.Send(ctx, subject, message) diff --git a/internal/pkg/core/notifier/providers/lark/lark_test.go b/internal/pkg/core/notifier/providers/lark/lark_test.go index 0b91ebd7..2b04a7a6 100644 --- a/internal/pkg/core/notifier/providers/lark/lark_test.go +++ b/internal/pkg/core/notifier/providers/lark/lark_test.go @@ -38,7 +38,7 @@ func TestNotify(t *testing.T) { fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl), }, "\n")) - notifier, err := provider.New(&provider.LarkNotifierConfig{ + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ WebhookUrl: fWebhookUrl, }) if err != nil { diff --git a/internal/pkg/core/notifier/providers/serverchan/serverchan.go b/internal/pkg/core/notifier/providers/serverchan/serverchan.go index 7b5a6de0..51df0893 100644 --- a/internal/pkg/core/notifier/providers/serverchan/serverchan.go +++ b/internal/pkg/core/notifier/providers/serverchan/serverchan.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "net/http" notifyHttp "github.com/nikoksr/notify/service/http" @@ -10,28 +9,28 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/notifier" ) -type ServerChanNotifierConfig struct { +type NotifierConfig struct { // ServerChan 服务地址。 Url string `json:"url"` } -type ServerChanNotifier struct { - config *ServerChanNotifierConfig +type NotifierProvider struct { + config *NotifierConfig } -var _ notifier.Notifier = (*ServerChanNotifier)(nil) +var _ notifier.Notifier = (*NotifierProvider)(nil) -func New(config *ServerChanNotifierConfig) (*ServerChanNotifier, error) { +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - return &ServerChanNotifier{ + return &NotifierProvider{ config: config, }, nil } -func (n *ServerChanNotifier) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { srv := notifyHttp.New() srv.AddReceivers(¬ifyHttp.Webhook{ diff --git a/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go b/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go index db2a4946..6f9c61a3 100644 --- a/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go +++ b/internal/pkg/core/notifier/providers/serverchan/serverchan_test.go @@ -38,7 +38,7 @@ func TestNotify(t *testing.T) { fmt.Sprintf("URL: %v", fUrl), }, "\n")) - notifier, err := provider.New(&provider.ServerChanNotifierConfig{ + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ Url: fUrl, }) if err != nil { diff --git a/internal/pkg/core/notifier/providers/telegram/telegram.go b/internal/pkg/core/notifier/providers/telegram/telegram.go index d1dcc45f..6a1889b3 100644 --- a/internal/pkg/core/notifier/providers/telegram/telegram.go +++ b/internal/pkg/core/notifier/providers/telegram/telegram.go @@ -2,37 +2,36 @@ import ( "context" - "errors" "github.com/nikoksr/notify/service/telegram" "github.com/usual2970/certimate/internal/pkg/core/notifier" ) -type TelegramNotifierConfig struct { +type NotifierConfig struct { // Telegram API Token。 ApiToken string `json:"apiToken"` // Telegram Chat ID。 ChatId int64 `json:"chatId"` } -type TelegramNotifier struct { - config *TelegramNotifierConfig +type NotifierProvider struct { + config *NotifierConfig } -var _ notifier.Notifier = (*TelegramNotifier)(nil) +var _ notifier.Notifier = (*NotifierProvider)(nil) -func New(config *TelegramNotifierConfig) (*TelegramNotifier, error) { +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - return &TelegramNotifier{ + return &NotifierProvider{ config: config, }, nil } -func (n *TelegramNotifier) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { srv, err := telegram.New(n.config.ApiToken) if err != nil { return nil, err diff --git a/internal/pkg/core/notifier/providers/telegram/telegram_test.go b/internal/pkg/core/notifier/providers/telegram/telegram_test.go index fca71e8a..10e93547 100644 --- a/internal/pkg/core/notifier/providers/telegram/telegram_test.go +++ b/internal/pkg/core/notifier/providers/telegram/telegram_test.go @@ -44,7 +44,7 @@ func TestNotify(t *testing.T) { fmt.Sprintf("CHATID: %v", fChartId), }, "\n")) - notifier, err := provider.New(&provider.TelegramNotifierConfig{ + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ ApiToken: fApiToken, ChatId: fChartId, }) diff --git a/internal/pkg/core/notifier/providers/webhook/webhook.go b/internal/pkg/core/notifier/providers/webhook/webhook.go index 3c2bc05f..55c0e668 100644 --- a/internal/pkg/core/notifier/providers/webhook/webhook.go +++ b/internal/pkg/core/notifier/providers/webhook/webhook.go @@ -2,35 +2,34 @@ import ( "context" - "errors" "github.com/nikoksr/notify/service/http" "github.com/usual2970/certimate/internal/pkg/core/notifier" ) -type WebhookNotifierConfig struct { +type NotifierConfig struct { // Webhook URL。 Url string `json:"url"` } -type WebhookNotifier struct { - config *WebhookNotifierConfig +type NotifierProvider struct { + config *NotifierConfig } -var _ notifier.Notifier = (*WebhookNotifier)(nil) +var _ notifier.Notifier = (*NotifierProvider)(nil) -func New(config *WebhookNotifierConfig) (*WebhookNotifier, error) { +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - return &WebhookNotifier{ + return &NotifierProvider{ config: config, }, nil } -func (n *WebhookNotifier) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { srv := http.New() srv.AddReceiversURLs(n.config.Url) diff --git a/internal/pkg/core/notifier/providers/webhook/webhook_test.go b/internal/pkg/core/notifier/providers/webhook/webhook_test.go index 14e2adf6..7afe6be4 100644 --- a/internal/pkg/core/notifier/providers/webhook/webhook_test.go +++ b/internal/pkg/core/notifier/providers/webhook/webhook_test.go @@ -38,7 +38,7 @@ func TestNotify(t *testing.T) { fmt.Sprintf("URL: %v", fUrl), }, "\n")) - notifier, err := provider.New(&provider.WebhookNotifierConfig{ + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ Url: fUrl, }) if err != nil { diff --git a/internal/pkg/core/notifier/providers/wecom/wecom.go b/internal/pkg/core/notifier/providers/wecom/wecom.go index 20938009..63342cb5 100644 --- a/internal/pkg/core/notifier/providers/wecom/wecom.go +++ b/internal/pkg/core/notifier/providers/wecom/wecom.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "net/http" notifyHttp "github.com/nikoksr/notify/service/http" @@ -10,28 +9,28 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/notifier" ) -type WeComNotifierConfig struct { +type NotifierConfig struct { // 企业微信机器人 Webhook 地址。 WebhookUrl string `json:"webhookUrl"` } -type WeComNotifier struct { - config *WeComNotifierConfig +type NotifierProvider struct { + config *NotifierConfig } -var _ notifier.Notifier = (*WeComNotifier)(nil) +var _ notifier.Notifier = (*NotifierProvider)(nil) -func New(config *WeComNotifierConfig) (*WeComNotifier, error) { +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } - return &WeComNotifier{ + return &NotifierProvider{ config: config, }, nil } -func (n *WeComNotifier) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { srv := notifyHttp.New() srv.AddReceivers(¬ifyHttp.Webhook{ diff --git a/internal/pkg/core/notifier/providers/wecom/wecom_test.go b/internal/pkg/core/notifier/providers/wecom/wecom_test.go index e1378849..b823adea 100644 --- a/internal/pkg/core/notifier/providers/wecom/wecom_test.go +++ b/internal/pkg/core/notifier/providers/wecom/wecom_test.go @@ -38,7 +38,7 @@ func TestNotify(t *testing.T) { fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl), }, "\n")) - notifier, err := provider.New(&provider.WeComNotifierConfig{ + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ WebhookUrl: fWebhookUrl, }) if err != nil { diff --git a/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go b/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go index ac71202e..202339c4 100644 --- a/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go +++ b/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "strings" "time" @@ -16,7 +15,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/certs" ) -type AliyunCASUploaderConfig struct { +type UploaderConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -25,16 +24,16 @@ type AliyunCASUploaderConfig struct { Region string `json:"region"` } -type AliyunCASUploader struct { - config *AliyunCASUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *aliyunCas.Client } -var _ uploader.Uploader = (*AliyunCASUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *AliyunCASUploaderConfig) (*AliyunCASUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient( @@ -46,13 +45,13 @@ func New(config *AliyunCASUploaderConfig) (*AliyunCASUploader, error) { return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &AliyunCASUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *AliyunCASUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { @@ -112,7 +111,7 @@ func (u *AliyunCASUploader) Upload(ctx context.Context, certPem string, privkeyP if listUserCertificateOrderResp.Body.CertificateOrderList == nil || len(listUserCertificateOrderResp.Body.CertificateOrderList) < int(listUserCertificateOrderLimit) { break } else { - listUserCertificateOrderPage += 1 + listUserCertificateOrderPage++ } } diff --git a/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go b/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go index 63779b48..c53eced2 100644 --- a/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go +++ b/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go @@ -4,7 +4,6 @@ import ( "context" "crypto/sha256" "encoding/hex" - "errors" "fmt" "regexp" "strings" @@ -19,7 +18,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/certs" ) -type AliyunSLBUploaderConfig struct { +type UploaderConfig struct { // 阿里云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 阿里云 AccessKeySecret。 @@ -28,16 +27,16 @@ type AliyunSLBUploaderConfig struct { Region string `json:"region"` } -type AliyunSLBUploader struct { - config *AliyunSLBUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *aliyunSlb.Client } -var _ uploader.Uploader = (*AliyunSLBUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *AliyunSLBUploaderConfig) (*AliyunSLBUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient( @@ -49,13 +48,13 @@ func New(config *AliyunSLBUploaderConfig) (*AliyunSLBUploader, error) { return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &AliyunSLBUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *AliyunSLBUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { diff --git a/internal/pkg/core/uploader/providers/aws-acm/aws_acm.go b/internal/pkg/core/uploader/providers/aws-acm/aws_acm.go index cac4d833..18ffd3aa 100644 --- a/internal/pkg/core/uploader/providers/aws-acm/aws_acm.go +++ b/internal/pkg/core/uploader/providers/aws-acm/aws_acm.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "time" @@ -16,7 +15,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/certs" ) -type AWSCertificateManagerUploaderConfig struct { +type UploaderConfig struct { // AWS AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // AWS SecretAccessKey。 @@ -25,16 +24,16 @@ type AWSCertificateManagerUploaderConfig struct { Region string `json:"region"` } -type AWSCertificateManagerUploader struct { - config *AWSCertificateManagerUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *awsAcm.Client } -var _ uploader.Uploader = (*AWSCertificateManagerUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *AWSCertificateManagerUploaderConfig) (*AWSCertificateManagerUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region) @@ -42,13 +41,13 @@ func New(config *AWSCertificateManagerUploaderConfig) (*AWSCertificateManagerUpl return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &AWSCertificateManagerUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *AWSCertificateManagerUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { diff --git a/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go b/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go index f1bf2b46..04116e31 100644 --- a/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go +++ b/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go @@ -5,7 +5,6 @@ import ( "crypto/sha1" "crypto/sha256" "encoding/hex" - "errors" "fmt" "strings" "time" @@ -17,36 +16,36 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/certs" ) -type ByteplusCDNUploaderConfig struct { +type UploaderConfig struct { // BytePlus AccessKey。 AccessKey string `json:"accessKey"` // BytePlus SecretKey。 SecretKey string `json:"secretKey"` } -type ByteplusCDNUploader struct { - config *ByteplusCDNUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *bpCdn.CDN } -var _ uploader.Uploader = (*ByteplusCDNUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *ByteplusCDNUploaderConfig) (*ByteplusCDNUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client := bpCdn.NewInstance() client.Client.SetAccessKey(config.AccessKey) client.Client.SetSecretKey(config.SecretKey) - return &ByteplusCDNUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *ByteplusCDNUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { diff --git a/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go b/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go index a38e832c..82856b6e 100644 --- a/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go +++ b/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "time" @@ -12,23 +11,23 @@ import ( doge "github.com/usual2970/certimate/internal/pkg/vendors/dogecloud-sdk" ) -type DogeCloudUploaderConfig struct { +type UploaderConfig struct { // 多吉云 AccessKey。 AccessKey string `json:"accessKey"` // 多吉云 SecretKey。 SecretKey string `json:"secretKey"` } -type DogeCloudUploader struct { - config *DogeCloudUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *doge.Client } -var _ uploader.Uploader = (*DogeCloudUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *DogeCloudUploaderConfig) (*DogeCloudUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient( @@ -39,13 +38,13 @@ func New(config *DogeCloudUploaderConfig) (*DogeCloudUploader, error) { return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &DogeCloudUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *DogeCloudUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 生成新证书名(需符合多吉云命名规则) var certId, certName string certName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) diff --git a/internal/pkg/core/uploader/providers/gcore-cdn/gcore_cdn.go b/internal/pkg/core/uploader/providers/gcore-cdn/gcore_cdn.go new file mode 100644 index 00000000..b990c694 --- /dev/null +++ b/internal/pkg/core/uploader/providers/gcore-cdn/gcore_cdn.go @@ -0,0 +1,83 @@ +package gcorecdn + +import ( + "context" + "errors" + "fmt" + "time" + + gprovider "github.com/G-Core/gcorelabscdn-go/gcore/provider" + gsslcerts "github.com/G-Core/gcorelabscdn-go/sslcerts" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/uploader" + gcoresdk "github.com/usual2970/certimate/internal/pkg/vendors/gcore-sdk/common" +) + +type UploaderConfig struct { + // Gcore API Token。 + ApiToken string `json:"apiToken"` +} + +type UploaderProvider struct { + config *UploaderConfig + sdkClient *gsslcerts.Service +} + +var _ uploader.Uploader = (*UploaderProvider)(nil) + +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiToken) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &UploaderProvider{ + config: config, + sdkClient: client, + }, nil +} + +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { + // 生成新证书名(需符合 Gcore 命名规则) + var certId, certName string + certName = fmt.Sprintf("certimate_%d", time.Now().UnixMilli()) + + // 新增证书 + // REF: https://api.gcore.com/docs/cdn#tag/CA-certificates/operation/ca_certificates-add + createCertificateReq := &gsslcerts.CreateRequest{ + Name: certName, + Cert: certPem, + PrivateKey: privkeyPem, + Automated: false, + ValidateRootCA: false, + } + createCertificateResp, err := u.sdkClient.Create(context.TODO(), createCertificateReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'sslcerts.Create'") + } + + certId = fmt.Sprintf("%d", createCertificateResp.ID) + certName = createCertificateResp.Name + return &uploader.UploadResult{ + CertId: certId, + CertName: certName, + }, nil +} + +func createSdkClient(apiToken string) (*gsslcerts.Service, error) { + if apiToken == "" { + return nil, errors.New("invalid gcore api token") + } + + requester := gprovider.NewClient( + gcoresdk.BASE_URL, + gprovider.WithSigner(gcoresdk.NewAuthRequestSigner(apiToken)), + ) + service := gsslcerts.NewService(requester) + return service, nil +} diff --git a/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go b/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go index 2ea7d031..96a1ff7b 100644 --- a/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go +++ b/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go @@ -21,7 +21,7 @@ import ( hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" ) -type HuaweiCloudELBUploaderConfig struct { +type UploaderConfig struct { // 华为云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 华为云 SecretAccessKey。 @@ -30,16 +30,16 @@ type HuaweiCloudELBUploaderConfig struct { Region string `json:"region"` } -type HuaweiCloudELBUploader struct { - config *HuaweiCloudELBUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *hcElb.ElbClient } -var _ uploader.Uploader = (*HuaweiCloudELBUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *HuaweiCloudELBUploaderConfig) (*HuaweiCloudELBUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient( @@ -51,13 +51,13 @@ func New(config *HuaweiCloudELBUploaderConfig) (*HuaweiCloudELBUploader, error) return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &HuaweiCloudELBUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *HuaweiCloudELBUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { diff --git a/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go b/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go index c0618da0..9406c55d 100644 --- a/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go +++ b/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "time" @@ -17,7 +16,7 @@ import ( hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" ) -type HuaweiCloudSCMUploaderConfig struct { +type UploaderConfig struct { // 华为云 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 华为云 SecretAccessKey。 @@ -26,16 +25,16 @@ type HuaweiCloudSCMUploaderConfig struct { Region string `json:"region"` } -type HuaweiCloudSCMUploader struct { - config *HuaweiCloudSCMUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *hcScm.ScmClient } -var _ uploader.Uploader = (*HuaweiCloudSCMUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *HuaweiCloudSCMUploaderConfig) (*HuaweiCloudSCMUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient( @@ -47,13 +46,13 @@ func New(config *HuaweiCloudSCMUploaderConfig) (*HuaweiCloudSCMUploader, error) return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &HuaweiCloudSCMUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *HuaweiCloudSCMUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { diff --git a/internal/pkg/core/uploader/providers/huaweicloud-waf/huaweicloud_waf.go b/internal/pkg/core/uploader/providers/huaweicloud-waf/huaweicloud_waf.go new file mode 100644 index 00000000..19e7cea7 --- /dev/null +++ b/internal/pkg/core/uploader/providers/huaweicloud-waf/huaweicloud_waf.go @@ -0,0 +1,214 @@ +package huaweicloudwaf + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic" + "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global" + hcIam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3" + hcIamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model" + hcIamRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region" + hcWaf "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1" + hcWafModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/model" + hcWafRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/region" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/uploader" + "github.com/usual2970/certimate/internal/pkg/utils/certs" + hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" +) + +type UploaderConfig struct { + // 华为云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 华为云 SecretAccessKey。 + SecretAccessKey string `json:"secretAccessKey"` + // 华为云区域。 + Region string `json:"region"` +} + +type UploaderProvider struct { + config *UploaderConfig + sdkClient *hcWaf.WafClient +} + +var _ uploader.Uploader = (*UploaderProvider)(nil) + +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient( + config.AccessKeyId, + config.SecretAccessKey, + config.Region, + ) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &UploaderProvider{ + config: config, + sdkClient: client, + }, nil +} + +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { + // 解析证书内容 + certX509, err := certs.ParseCertificateFromPEM(certPem) + if err != nil { + return nil, err + } + + // 遍历查询已有证书,避免重复上传 + // REF: https://support.huaweicloud.com/api-waf/ListCertificates.html + // REF: https://support.huaweicloud.com/api-waf/ShowCertificate.html + listCertificatesPage := int32(1) + listCertificatesPageSize := int32(100) + for { + listCertificatesReq := &hcWafModel.ListCertificatesRequest{ + Page: hwsdk.Int32Ptr(listCertificatesPage), + Pagesize: hwsdk.Int32Ptr(listCertificatesPageSize), + } + listCertificatesResp, err := u.sdkClient.ListCertificates(listCertificatesReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.ListCertificates'") + } + + if listCertificatesResp.Items != nil { + for _, certItem := range *listCertificatesResp.Items { + showCertificateReq := &hcWafModel.ShowCertificateRequest{ + CertificateId: certItem.Id, + } + showCertificateResp, err := u.sdkClient.ShowCertificate(showCertificateReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.ShowCertificate'") + } + + var isSameCert bool + if *showCertificateResp.Content == certPem { + isSameCert = true + } else { + oldCertX509, err := certs.ParseCertificateFromPEM(*showCertificateResp.Content) + if err != nil { + continue + } + + isSameCert = certs.EqualCertificate(certX509, oldCertX509) + } + + // 如果已存在相同证书,直接返回已有的证书信息 + if isSameCert { + return &uploader.UploadResult{ + CertId: certItem.Id, + CertName: certItem.Name, + }, nil + } + } + } + + if listCertificatesResp.Items == nil || len(*listCertificatesResp.Items) < int(listCertificatesPageSize) { + break + } else { + listCertificatesPage++ + } + } + + // 生成新证书名(需符合华为云命名规则) + var certId, certName string + certName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) + + // 创建证书 + // REF: https://support.huaweicloud.com/api-waf/CreateCertificate.html + createCertificateReq := &hcWafModel.CreateCertificateRequest{ + Body: &hcWafModel.CreateCertificateRequestBody{ + Name: certName, + Content: certPem, + Key: privkeyPem, + }, + } + createCertificateResp, err := u.sdkClient.CreateCertificate(createCertificateReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.CreateCertificate'") + } + + certId = *createCertificateResp.Id + certName = *createCertificateResp.Name + return &uploader.UploadResult{ + CertId: certId, + CertName: certName, + }, nil +} + +func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcWaf.WafClient, error) { + projectId, err := getSdkProjectId(accessKeyId, secretAccessKey, region) + if err != nil { + return nil, err + } + + auth, err := basic.NewCredentialsBuilder(). + WithAk(accessKeyId). + WithSk(secretAccessKey). + WithProjectId(projectId). + SafeBuild() + if err != nil { + return nil, err + } + + hcRegion, err := hcWafRegion.SafeValueOf(region) + if err != nil { + return nil, err + } + + hcClient, err := hcWaf.WafClientBuilder(). + WithRegion(hcRegion). + WithCredential(auth). + SafeBuild() + if err != nil { + return nil, err + } + + client := hcWaf.NewWafClient(hcClient) + return client, nil +} + +func getSdkProjectId(accessKeyId, secretAccessKey, region string) (string, error) { + auth, err := global.NewCredentialsBuilder(). + WithAk(accessKeyId). + WithSk(secretAccessKey). + SafeBuild() + if err != nil { + return "", err + } + + hcRegion, err := hcIamRegion.SafeValueOf(region) + if err != nil { + return "", err + } + + hcClient, err := hcIam.IamClientBuilder(). + WithRegion(hcRegion). + WithCredential(auth). + SafeBuild() + if err != nil { + return "", err + } + + client := hcIam.NewIamClient(hcClient) + + request := &hcIamModel.KeystoneListProjectsRequest{ + Name: ®ion, + } + response, err := client.KeystoneListProjects(request) + if err != nil { + return "", err + } else if response.Projects == nil || len(*response.Projects) == 0 { + return "", errors.New("no project found") + } + + return (*response.Projects)[0].Id, nil +} diff --git a/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl.go b/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl.go new file mode 100644 index 00000000..afbbf3e1 --- /dev/null +++ b/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl.go @@ -0,0 +1,139 @@ +package jdcloudssl + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "fmt" + "strings" + "time" + + jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core" + jdSslApi "github.com/jdcloud-api/jdcloud-sdk-go/services/ssl/apis" + jdSslClient "github.com/jdcloud-api/jdcloud-sdk-go/services/ssl/client" + xerrors "github.com/pkg/errors" + "golang.org/x/exp/slices" + + "github.com/usual2970/certimate/internal/pkg/core/uploader" + "github.com/usual2970/certimate/internal/pkg/utils/certs" +) + +type UploaderConfig struct { + // 京东云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 京东云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` +} + +type UploaderProvider struct { + config *UploaderConfig + sdkClient *jdSslClient.SslClient +} + +var _ uploader.Uploader = (*UploaderProvider)(nil) + +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &UploaderProvider{ + config: config, + sdkClient: client, + }, nil +} + +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { + // 解析证书内容 + certX509, err := certs.ParseCertificateFromPEM(certPem) + if err != nil { + return nil, err + } + + // 格式化私钥内容,以便后续计算私钥摘要 + privkeyPem = strings.TrimSpace(privkeyPem) + privkeyPem = strings.ReplaceAll(privkeyPem, "\r", "") + privkeyPem = strings.ReplaceAll(privkeyPem, "\n", "\r\n") + privkeyPem = privkeyPem + "\r\n" + + // 遍历查看证书列表,避免重复上传 + // REF: https://docs.jdcloud.com/cn/ssl-certificate/api/describecerts + describeCertsPageNumber := 1 + describeCertsPageSize := 10 + for { + describeCertsReq := jdSslApi.NewDescribeCertsRequest() + describeCertsReq.SetDomainName(certX509.Subject.CommonName) + describeCertsReq.SetPageNumber(describeCertsPageNumber) + describeCertsReq.SetPageSize(describeCertsPageSize) + describeCertsResp, err := u.sdkClient.DescribeCerts(describeCertsReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.DescribeCerts'") + } + + for _, certDetail := range describeCertsResp.Result.CertListDetails { + // 先尝试匹配 CN + if !strings.EqualFold(certX509.Subject.CommonName, certDetail.CommonName) { + continue + } + + // 再尝试匹配 SAN + if !slices.Equal(certX509.DNSNames, certDetail.DnsNames) { + continue + } + + // 再尝试匹配证书有效期 + oldCertNotBefore, _ := time.Parse(time.RFC3339, certDetail.StartTime) + oldCertNotAfter, _ := time.Parse(time.RFC3339, certDetail.EndTime) + if !certX509.NotBefore.Equal(oldCertNotBefore) || !certX509.NotAfter.Equal(oldCertNotAfter) { + continue + } + + // 最后尝试匹配私钥摘要 + newKeyDigest := sha256.Sum256([]byte(privkeyPem)) + newKeyDigestHex := hex.EncodeToString(newKeyDigest[:]) + if !strings.EqualFold(newKeyDigestHex, certDetail.Digest) { + continue + } + + // 如果以上都匹配,则视为已存在相同证书,直接返回已有的证书信息 + return &uploader.UploadResult{ + CertId: certDetail.CertId, + CertName: certDetail.CertName, + }, nil + } + + if len(describeCertsResp.Result.CertListDetails) < int(describeCertsPageSize) { + break + } else { + describeCertsPageNumber++ + } + } + + // 生成新证书名(需符合京东云命名规则) + certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) + + // 上传证书 + // REF: https://docs.jdcloud.com/cn/ssl-certificate/api/uploadcert + uploadCertReq := jdSslApi.NewUploadCertRequest(certName, privkeyPem, certPem) + uploadCertResp, err := u.sdkClient.UploadCert(uploadCertReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'ssl.UploadCertificate'") + } + + return &uploader.UploadResult{ + CertId: uploadCertResp.Result.CertId, + CertName: certName, + }, nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*jdSslClient.SslClient, error) { + clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret) + client := jdSslClient.NewSslClient(clientCredentials) + client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn)) + return client, nil +} diff --git a/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl_test.go b/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl_test.go new file mode 100644 index 00000000..ec02ce49 --- /dev/null +++ b/internal/pkg/core/uploader/providers/jdcloud-ssl/jdcloud_ssl_test.go @@ -0,0 +1,72 @@ +package jdcloudssl_test + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/jdcloud-ssl" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string +) + +func init() { + argsPrefix := "CERTIMATE_UPLOADER_JDCLOUDSSL_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") +} + +/* +Shell command to run this test: + + go test -v ./jdcloud_ssl_test.go -args \ + --CERTIMATE_UPLOADER_JDCLOUDSSL_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_UPLOADER_JDCLOUDSSL_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_UPLOADER_JDCLOUDSSL_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_UPLOADER_JDCLOUDSSL_ACCESSKEYSECRET="your-access-key-secret" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + }, "\n")) + + uploader, err := provider.NewUploader(&provider.UploaderConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := uploader.Upload(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + sres, _ := json.Marshal(res) + t.Logf("ok: %s", string(sres)) + }) +} diff --git a/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go b/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go index 851cbf01..05c57be8 100644 --- a/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go +++ b/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go @@ -2,7 +2,6 @@ import ( "context" - "errors" "fmt" "time" @@ -14,23 +13,23 @@ import ( qiniuEx "github.com/usual2970/certimate/internal/pkg/vendors/qiniu-sdk" ) -type QiniuSSLCertUploaderConfig struct { +type UploaderConfig struct { // 七牛云 AccessKey。 AccessKey string `json:"accessKey"` // 七牛云 SecretKey。 SecretKey string `json:"secretKey"` } -type QiniuSSLCertUploader struct { - config *QiniuSSLCertUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *qiniuEx.Client } -var _ uploader.Uploader = (*QiniuSSLCertUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *QiniuSSLCertUploaderConfig) (*QiniuSSLCertUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient( @@ -41,13 +40,13 @@ func New(config *QiniuSSLCertUploaderConfig) (*QiniuSSLCertUploader, error) { return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &QiniuSSLCertUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *QiniuSSLCertUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { diff --git a/internal/pkg/core/uploader/providers/tencentcloud-ssl/tencentcloud_ssl.go b/internal/pkg/core/uploader/providers/tencentcloud-ssl/tencentcloud_ssl.go index a94af000..a76bf2a0 100644 --- a/internal/pkg/core/uploader/providers/tencentcloud-ssl/tencentcloud_ssl.go +++ b/internal/pkg/core/uploader/providers/tencentcloud-ssl/tencentcloud_ssl.go @@ -2,7 +2,6 @@ import ( "context" - "errors" xerrors "github.com/pkg/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" @@ -12,23 +11,23 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/uploader" ) -type TencentCloudSSLUploaderConfig struct { +type UploaderConfig struct { // 腾讯云 SecretId。 SecretId string `json:"secretId"` // 腾讯云 SecretKey。 SecretKey string `json:"secretKey"` } -type TencentCloudSSLUploader struct { - config *TencentCloudSSLUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *tcSsl.Client } -var _ uploader.Uploader = (*TencentCloudSSLUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *TencentCloudSSLUploaderConfig) (*TencentCloudSSLUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient( @@ -39,13 +38,13 @@ func New(config *TencentCloudSSLUploaderConfig) (*TencentCloudSSLUploader, error return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &TencentCloudSSLUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *TencentCloudSSLUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 上传新证书 // REF: https://cloud.tencent.com/document/product/400/41665 uploadCertificateReq := tcSsl.NewUploadCertificateRequest() diff --git a/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go b/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go index 9c5fa2b3..aaa03999 100644 --- a/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go +++ b/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go @@ -20,7 +20,7 @@ import ( usdkSsl "github.com/usual2970/certimate/internal/pkg/vendors/ucloud-sdk/ussl" ) -type UCloudUSSLUploaderConfig struct { +type UploaderConfig struct { // 优刻得 API 私钥。 PrivateKey string `json:"privateKey"` // 优刻得 API 公钥。 @@ -29,16 +29,16 @@ type UCloudUSSLUploaderConfig struct { ProjectId string `json:"projectId,omitempty"` } -type UCloudUSSLUploader struct { - config *UCloudUSSLUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *usdkSsl.USSLClient } -var _ uploader.Uploader = (*UCloudUSSLUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *UCloudUSSLUploaderConfig) (*UCloudUSSLUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient(config.PrivateKey, config.PublicKey) @@ -46,13 +46,13 @@ func New(config *UCloudUSSLUploaderConfig) (*UCloudUSSLUploader, error) { return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &UCloudUSSLUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *UCloudUSSLUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 生成新证书名(需符合优刻得命名规则) var certId, certName string certName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) @@ -92,7 +92,7 @@ func (u *UCloudUSSLUploader) Upload(ctx context.Context, certPem string, privkey }, nil } -func (u *UCloudUSSLUploader) getExistCert(ctx context.Context, certPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { diff --git a/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl_test.go b/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl_test.go index c0a0f719..b6324fd5 100644 --- a/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl_test.go +++ b/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl_test.go @@ -49,7 +49,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("PUBLICKEY: %v", fPublicKey), }, "\n")) - uploader, err := provider.New(&provider.UCloudUSSLUploaderConfig{ + uploader, err := provider.NewUploader(&provider.UploaderConfig{ PrivateKey: fPrivateKey, PublicKey: fPublicKey, }) diff --git a/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go b/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go index 23f05281..9b5c9b56 100644 --- a/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go +++ b/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go @@ -5,7 +5,6 @@ import ( "crypto/sha1" "crypto/sha256" "encoding/hex" - "errors" "fmt" "strings" "time" @@ -18,36 +17,36 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/certs" ) -type VolcEngineCDNUploaderConfig struct { +type UploaderConfig struct { // 火山引擎 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 火山引擎 AccessKeySecret。 AccessKeySecret string `json:"accessKeySecret"` } -type VolcEngineCDNUploader struct { - config *VolcEngineCDNUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *veCdn.CDN } -var _ uploader.Uploader = (*VolcEngineCDNUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *VolcEngineCDNUploaderConfig) (*VolcEngineCDNUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client := veCdn.NewInstance() client.Client.SetAccessKey(config.AccessKeyId) client.Client.SetSecretKey(config.AccessKeySecret) - return &VolcEngineCDNUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *VolcEngineCDNUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { diff --git a/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter.go b/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter.go index 5e301ebd..1ff133e5 100644 --- a/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter.go +++ b/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter.go @@ -2,7 +2,6 @@ package volcenginecertcenter import ( "context" - "errors" xerrors "github.com/pkg/errors" ve "github.com/volcengine/volcengine-go-sdk/volcengine" @@ -12,7 +11,7 @@ import ( vesdkCc "github.com/usual2970/certimate/internal/pkg/vendors/volcengine-sdk/certcenter" ) -type VolcEngineCertCenterUploaderConfig struct { +type UploaderConfig struct { // 火山引擎 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 火山引擎 AccessKeySecret。 @@ -21,16 +20,16 @@ type VolcEngineCertCenterUploaderConfig struct { Region string `json:"region"` } -type VolcEngineCertCenterUploader struct { - config *VolcEngineCertCenterUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *vesdkCc.CertCenter } -var _ uploader.Uploader = (*VolcEngineCertCenterUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *VolcEngineCertCenterUploaderConfig) (*VolcEngineCertCenterUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region) @@ -38,13 +37,13 @@ func New(config *VolcEngineCertCenterUploaderConfig) (*VolcEngineCertCenterUploa return nil, xerrors.Wrap(err, "failed to create sdk client") } - return &VolcEngineCertCenterUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *VolcEngineCertCenterUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 上传证书 // REF: https://www.volcengine.com/docs/6638/1365580 importCertificateReq := &vesdkCc.ImportCertificateInput{ diff --git a/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter_test.go b/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter_test.go index 5f15c44f..5c312707 100644 --- a/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter_test.go +++ b/internal/pkg/core/uploader/providers/volcengine-certcenter/volcengine_certcenter_test.go @@ -49,7 +49,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), }, "\n")) - uploader, err := provider.New(&provider.VolcEngineCertCenterUploaderConfig{ + uploader, err := provider.NewUploader(&provider.UploaderConfig{ AccessKeyId: fAccessKeyId, AccessKeySecret: fAccessKeySecret, }) diff --git a/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go b/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go index c7861cb6..3a7a39eb 100644 --- a/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go +++ b/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go @@ -2,7 +2,6 @@ package volcenginelive import ( "context" - "errors" "fmt" "strings" "time" @@ -15,36 +14,36 @@ import ( "github.com/usual2970/certimate/internal/pkg/utils/certs" ) -type VolcEngineLiveUploaderConfig struct { +type UploaderConfig struct { // 火山引擎 AccessKeyId。 AccessKeyId string `json:"accessKeyId"` // 火山引擎 AccessKeySecret。 AccessKeySecret string `json:"accessKeySecret"` } -type VolcEngineLiveUploader struct { - config *VolcEngineLiveUploaderConfig +type UploaderProvider struct { + config *UploaderConfig sdkClient *veLive.Live } -var _ uploader.Uploader = (*VolcEngineLiveUploader)(nil) +var _ uploader.Uploader = (*UploaderProvider)(nil) -func New(config *VolcEngineLiveUploaderConfig) (*VolcEngineLiveUploader, error) { +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { if config == nil { - return nil, errors.New("config is nil") + panic("config is nil") } client := veLive.NewInstance() client.SetAccessKey(config.AccessKeyId) client.SetSecretKey(config.AccessKeySecret) - return &VolcEngineLiveUploader{ + return &UploaderProvider{ config: config, sdkClient: client, }, nil } -func (u *VolcEngineLiveUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { +func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { diff --git a/internal/pkg/utils/maps/maps.go b/internal/pkg/utils/maps/maps.go index a88b6629..4a4417d0 100644 --- a/internal/pkg/utils/maps/maps.go +++ b/internal/pkg/utils/maps/maps.go @@ -207,8 +207,3 @@ func Populate(dict map[string]any, output any) error { return decoder.Decode(dict) } - -// Deprecated: Use [Populate] instead. -func Decode(dict map[string]any, output any) error { - return Populate(dict, output) -} diff --git a/internal/pkg/vendors/baishan-sdk/api.go b/internal/pkg/vendors/baishan-sdk/api.go new file mode 100644 index 00000000..c2f76adc --- /dev/null +++ b/internal/pkg/vendors/baishan-sdk/api.go @@ -0,0 +1,32 @@ +package baishansdk + +import ( + "net/http" +) + +func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) { + resp := CreateCertificateResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/v2/domain/certificate", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *Client) GetDomainConfig(req *GetDomainConfigRequest) (*GetDomainConfigResponse, error) { + resp := GetDomainConfigResponse{} + err := c.sendRequestWithResult(http.MethodGet, "/v2/domain/config", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *Client) SetDomainConfig(req *SetDomainConfigRequest) (*SetDomainConfigResponse, error) { + resp := SetDomainConfigResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/v2/domain/config", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/internal/pkg/vendors/baishan-sdk/client.go b/internal/pkg/vendors/baishan-sdk/client.go new file mode 100644 index 00000000..44015b62 --- /dev/null +++ b/internal/pkg/vendors/baishan-sdk/client.go @@ -0,0 +1,83 @@ +package baishansdk + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + apiToken string + + client *resty.Client +} + +func NewClient(apiToken string) *Client { + client := resty.New() + + return &Client{ + apiToken: apiToken, + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R() + req.Method = method + req.URL = "https://cdn.api.baishan.com" + path + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req. + SetQueryParams(qs). + SetQueryParam("token", c.apiToken) + } else { + req = req. + SetHeader("Content-Type", "application/json"). + SetQueryParam("token", c.apiToken). + SetBody(params) + } + + resp, err := req.Send() + if err != nil { + return nil, fmt.Errorf("baishan api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("baishan api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("baishan api error: failed to parse response: %w", err) + } else if errcode := result.GetCode(); errcode != 0 { + return fmt.Errorf("baishan api error: %d - %s", errcode, result.GetMessage()) + } + + return nil +} diff --git a/internal/pkg/vendors/baishan-sdk/models.go b/internal/pkg/vendors/baishan-sdk/models.go new file mode 100644 index 00000000..78685571 --- /dev/null +++ b/internal/pkg/vendors/baishan-sdk/models.go @@ -0,0 +1,73 @@ +package baishansdk + +type BaseResponse interface { + GetCode() int + GetMessage() string +} + +type baseResponse struct { + Code int `json:"code"` + Message string `json:"message"` +} + +func (r *baseResponse) GetCode() int { + return r.Code +} + +func (r *baseResponse) GetMessage() string { + return r.Message +} + +type CreateCertificateRequest struct { + Certificate string `json:"certificate"` + Key string `json:"key"` + Name string `json:"name"` +} + +type CreateCertificateResponse struct { + baseResponse + Data *DomainCertificate `json:"data"` +} + +type GetDomainConfigRequest struct { + Domains string `json:"domains"` + Config string `json:"config"` +} + +type GetDomainConfigResponse struct { + baseResponse + Data []*struct { + Domain string `json:"domain"` + Config *DomainConfig `json:"config"` + } `json:"data"` +} + +type SetDomainConfigRequest struct { + Domains string `json:"domains"` + Config *DomainConfig `json:"config"` +} + +type SetDomainConfigResponse struct { + baseResponse + Data *struct { + Config *DomainConfig `json:"config"` + } `json:"data"` +} + +type DomainCertificate struct { + CertId int64 `json:"cert_id"` + Name string `json:"name"` + CertStartTime string `json:"cert_start_time"` + CertExpireTime string `json:"cert_expire_time"` +} + +type DomainConfig struct { + Https *DomainConfigHttps `json:"https"` +} + +type DomainConfigHttps struct { + CertId int64 `json:"cert_id"` + ForceHttps *string `json:"force_https,omitempty"` + EnableHttp2 *string `json:"http2,omitempty"` + EnableOcsp *string `json:"ocsp,omitempty"` +} diff --git a/internal/pkg/vendors/btpanel-sdk/api.go b/internal/pkg/vendors/btpanel-sdk/api.go index 5c58e3df..434e7f6e 100644 --- a/internal/pkg/vendors/btpanel-sdk/api.go +++ b/internal/pkg/vendors/btpanel-sdk/api.go @@ -1,26 +1,46 @@ package btpanelsdk -type BaseResponse interface { - GetStatus() *bool - GetMsg() *string +func (c *Client) ConfigSavePanelSSL(req *ConfigSavePanelSSLRequest) (*ConfigSavePanelSSLResponse, error) { + resp := ConfigSavePanelSSLResponse{} + err := c.sendRequestWithResult("/config?action=SavePanelSSL", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil } -type SetSiteSSLRequest struct { - Type string `json:"type"` - SiteName string `json:"siteName"` - Key string `json:"key"` - Csr string `json:"csr"` +func (c *Client) SiteSetSSL(req *SiteSetSSLRequest) (*SiteSetSSLResponse, error) { + resp := SiteSetSSLResponse{} + err := c.sendRequestWithResult("/site?action=SetSSL", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil } -type SetSiteSSLResponse struct { - Status *bool `json:"status,omitempty"` - Msg *string `json:"msg,omitempty"` +func (c *Client) SystemServiceAdmin(req *SystemServiceAdminRequest) (*SystemServiceAdminResponse, error) { + resp := SystemServiceAdminResponse{} + err := c.sendRequestWithResult("/system?action=ServiceAdmin", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil } -func (r *SetSiteSSLResponse) GetStatus() *bool { - return r.Status +func (c *Client) SSLCertSaveCert(req *SSLCertSaveCertRequest) (*SSLCertSaveCertResponse, error) { + resp := SSLCertSaveCertResponse{} + err := c.sendRequestWithResult("/ssl/cert/save_cert", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil } -func (r *SetSiteSSLResponse) GetMsg() *string { - return r.Msg +func (c *Client) SSLSetBatchCertToSite(req *SSLSetBatchCertToSiteRequest) (*SSLSetBatchCertToSiteResponse, error) { + resp := SSLSetBatchCertToSiteResponse{} + err := c.sendRequestWithResult("/ssl?action=SetBatchCertToSite", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil } diff --git a/internal/pkg/vendors/btpanel-sdk/client.go b/internal/pkg/vendors/btpanel-sdk/client.go index 9557612a..67e9fdb1 100644 --- a/internal/pkg/vendors/btpanel-sdk/client.go +++ b/internal/pkg/vendors/btpanel-sdk/client.go @@ -9,45 +9,31 @@ import ( "time" "github.com/go-resty/resty/v2" - - "github.com/usual2970/certimate/internal/pkg/utils/maps" ) -type BaoTaPanelClient struct { +type Client struct { apiHost string apiKey string - client *resty.Client + + client *resty.Client } -func NewBaoTaPanelClient(apiHost, apiKey string) *BaoTaPanelClient { +func NewClient(apiHost, apiKey string) *Client { client := resty.New() - return &BaoTaPanelClient{ - apiHost: apiHost, + return &Client{ + apiHost: strings.TrimRight(apiHost, "/"), apiKey: apiKey, client: client, } } -func (c *BaoTaPanelClient) WithTimeout(timeout time.Duration) *BaoTaPanelClient { +func (c *Client) WithTimeout(timeout time.Duration) *Client { c.client.SetTimeout(timeout) return c } -func (c *BaoTaPanelClient) SetSiteSSL(req *SetSiteSSLRequest) (*SetSiteSSLResponse, error) { - params := make(map[string]any) - jsonData, _ := json.Marshal(req) - json.Unmarshal(jsonData, ¶ms) - - result := SetSiteSSLResponse{} - err := c.sendRequestWithResult("/site?action=SetSSL", params, &result) - if err != nil { - return nil, err - } - return &result, nil -} - -func (c *BaoTaPanelClient) generateSignature(timestamp string) string { +func (c *Client) generateSignature(timestamp string) string { keyMd5 := md5.Sum([]byte(c.apiKey)) keyMd5Hex := strings.ToLower(hex.EncodeToString(keyMd5[:])) @@ -56,50 +42,50 @@ func (c *BaoTaPanelClient) generateSignature(timestamp string) string { return signMd5Hex } -func (c *BaoTaPanelClient) sendRequest(path string, params map[string]any) (*resty.Response, error) { - if params == nil { - params = make(map[string]any) - } - +func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, error) { timestamp := time.Now().Unix() - params["request_time"] = timestamp - params["request_token"] = c.generateSignature(fmt.Sprintf("%d", timestamp)) - url := strings.TrimRight(c.apiHost, "/") + path + data := make(map[string]any) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + data[k] = v + } + } + } + data["request_time"] = timestamp + data["request_token"] = c.generateSignature(fmt.Sprintf("%d", timestamp)) + + url := c.apiHost + path req := c.client.R(). SetHeader("Content-Type", "application/json"). - SetBody(params) + SetBody(data) resp, err := req.Post(url) if err != nil { - return nil, fmt.Errorf("baota: failed to send request: %w", err) - } - - if resp.IsError() { - return nil, fmt.Errorf("baota: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return nil, fmt.Errorf("baota api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("baota api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) } return resp, nil } -func (c *BaoTaPanelClient) sendRequestWithResult(path string, params map[string]any, result BaseResponse) error { +func (c *Client) sendRequestWithResult(path string, params interface{}, result BaseResponse) error { resp, err := c.sendRequest(path, params) if err != nil { return err } - jsonResp := make(map[string]any) - if err := json.Unmarshal(resp.Body(), &jsonResp); err != nil { - return fmt.Errorf("baota: failed to parse response: %w", err) - } - if err := maps.Populate(jsonResp, &result); err != nil { - return fmt.Errorf("baota: failed to parse response: %w", err) - } - - if result.GetStatus() != nil && !*result.GetStatus() { - if result.GetMsg() == nil { + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("baota api error: failed to parse response: %w", err) + } else if errstatus := result.GetStatus(); errstatus != nil && !*errstatus { + if result.GetMessage() == nil { return fmt.Errorf("baota api error: unknown error") } else { - return fmt.Errorf("baota api error: %s", *result.GetMsg()) + return fmt.Errorf("baota api error: %s", *result.GetMessage()) } } diff --git a/internal/pkg/vendors/btpanel-sdk/models.go b/internal/pkg/vendors/btpanel-sdk/models.go new file mode 100644 index 00000000..8625e539 --- /dev/null +++ b/internal/pkg/vendors/btpanel-sdk/models.go @@ -0,0 +1,75 @@ +package btpanelsdk + +type BaseResponse interface { + GetStatus() *bool + GetMessage() *string +} + +type baseResponse struct { + Status *bool `json:"status,omitempty"` + Message *string `json:"msg,omitempty"` +} + +func (r *baseResponse) GetStatus() *bool { + return r.Status +} + +func (r *baseResponse) GetMessage() *string { + return r.Message +} + +type ConfigSavePanelSSLRequest struct { + PrivateKey string `json:"privateKey"` + Certificate string `json:"certPem"` +} + +type ConfigSavePanelSSLResponse struct { + baseResponse +} + +type SiteSetSSLRequest struct { + Type string `json:"type"` + SiteName string `json:"siteName"` + PrivateKey string `json:"key"` + Certificate string `json:"csr"` +} + +type SiteSetSSLResponse struct { + baseResponse +} + +type SystemServiceAdminRequest struct { + Name string `json:"name"` + Type string `json:"type"` +} + +type SystemServiceAdminResponse struct { + baseResponse +} + +type SSLCertSaveCertRequest struct { + PrivateKey string `json:"key"` + Certificate string `json:"csr"` +} + +type SSLCertSaveCertResponse struct { + baseResponse + SSLHash string `json:"ssl_hash"` +} + +type SSLSetBatchCertToSiteRequest struct { + BatchInfo []*SSLSetBatchCertToSiteRequestBatchInfo `json:"BatchInfo"` +} + +type SSLSetBatchCertToSiteRequestBatchInfo struct { + SSLHash string `json:"ssl_hash"` + SiteName string `json:"siteName"` + CertName string `json:"certName"` +} + +type SSLSetBatchCertToSiteResponse struct { + baseResponse + TotalCount int32 `json:"total"` + SuccessCount int32 `json:"success"` + FailedCount int32 `json:"faild"` +} diff --git a/internal/pkg/vendors/cachefly-sdk/api.go b/internal/pkg/vendors/cachefly-sdk/api.go new file mode 100644 index 00000000..ad6e3b8d --- /dev/null +++ b/internal/pkg/vendors/cachefly-sdk/api.go @@ -0,0 +1,14 @@ +package cacheflysdk + +import ( + "net/http" +) + +func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) { + resp := CreateCertificateResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/certificates", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/internal/pkg/vendors/cachefly-sdk/client.go b/internal/pkg/vendors/cachefly-sdk/client.go new file mode 100644 index 00000000..505aafaf --- /dev/null +++ b/internal/pkg/vendors/cachefly-sdk/client.go @@ -0,0 +1,79 @@ +package cacheflysdk + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + apiToken string + + client *resty.Client +} + +func NewClient(apiToken string) *Client { + client := resty.New() + + return &Client{ + apiToken: apiToken, + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R() + req.Method = method + req.URL = "https://api.cachefly.com/api/2.5" + path + req = req.SetHeader("x-cf-authorization", "Bearer "+c.apiToken) + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req.SetQueryParams(qs) + } else { + req = req. + SetHeader("Content-Type", "application/json"). + SetBody(params) + } + + resp, err := req.Send() + if err != nil { + return nil, fmt.Errorf("cachefly api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("cachefly api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("cachefly api error: failed to parse response: %w", err) + } + + return nil +} diff --git a/internal/pkg/vendors/cachefly-sdk/models.go b/internal/pkg/vendors/cachefly-sdk/models.go new file mode 100644 index 00000000..aaca7723 --- /dev/null +++ b/internal/pkg/vendors/cachefly-sdk/models.go @@ -0,0 +1,35 @@ +package cacheflysdk + +type BaseResponse interface { + GetMessage() *string +} + +type baseResponse struct { + Message *string `json:"message,omitempty"` +} + +func (r *baseResponse) GetMessage() *string { + return r.Message +} + +type CreateCertificateRequest struct { + Certificate string `json:"certificate"` + CertificateKey string `json:"certificateKey"` + Password *string `json:"password"` +} + +type CreateCertificateResponse struct { + baseResponse + Id string `json:"_id"` + SubjectCommonName string `json:"subjectCommonName"` + SubjectNames []string `json:"subjectNames"` + Expired bool `json:"expired"` + Expiring bool `json:"expiring"` + InUse bool `json:"inUse"` + Managed bool `json:"managed"` + Services []string `json:"services"` + Domains []string `json:"domains"` + NotBefore string `json:"notBefore"` + NotAfter string `json:"notAfter"` + CreatedAt string `json:"createdAt"` +} diff --git a/internal/pkg/vendors/cdnfly-sdk/api.go b/internal/pkg/vendors/cdnfly-sdk/api.go new file mode 100644 index 00000000..211baa33 --- /dev/null +++ b/internal/pkg/vendors/cdnfly-sdk/api.go @@ -0,0 +1,42 @@ +package cdnflysdk + +import ( + "fmt" + "net/http" +) + +func (c *Client) GetSite(req *GetSiteRequest) (*GetSiteResponse, error) { + resp := GetSiteResponse{} + err := c.sendRequestWithResult(http.MethodGet, fmt.Sprintf("/v1/sites/%s", req.Id), req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *Client) UpdateSite(req *UpdateSiteRequest) (*UpdateSiteResponse, error) { + resp := UpdateSiteResponse{} + err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/v1/sites/%s", req.Id), req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) { + resp := CreateCertificateResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/v1/certs", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *Client) UpdateCertificate(req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { + resp := UpdateCertificateResponse{} + err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/v1/certs/%s", req.Id), req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/internal/pkg/vendors/cdnfly-sdk/client.go b/internal/pkg/vendors/cdnfly-sdk/client.go new file mode 100644 index 00000000..7e2e93f4 --- /dev/null +++ b/internal/pkg/vendors/cdnfly-sdk/client.go @@ -0,0 +1,87 @@ +package cdnflysdk + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + apiHost string + apiKey string + apiSecret string + + client *resty.Client +} + +func NewClient(apiHost, apiKey, apiSecret string) *Client { + client := resty.New() + + return &Client{ + apiHost: strings.TrimRight(apiHost, "/"), + apiKey: apiKey, + apiSecret: apiSecret, + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R() + req.Method = method + req.URL = c.apiHost + path + req = req. + SetHeader("api-key", c.apiKey). + SetHeader("api-secret", c.apiSecret) + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req.SetQueryParams(qs) + } else { + req = req. + SetHeader("Content-Type", "application/json"). + SetBody(params) + } + + resp, err := req.Send() + if err != nil { + return nil, fmt.Errorf("cdnfly api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("cdnfly api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("cdnfly api error: failed to parse response: %w", err) + } else if errcode := result.GetCode(); errcode != "" && errcode != "0" { + return fmt.Errorf("cdnfly api error: %s - %s", errcode, result.GetMessage()) + } + + return nil +} diff --git a/internal/pkg/vendors/cdnfly-sdk/models.go b/internal/pkg/vendors/cdnfly-sdk/models.go new file mode 100644 index 00000000..873b80b0 --- /dev/null +++ b/internal/pkg/vendors/cdnfly-sdk/models.go @@ -0,0 +1,70 @@ +package cdnflysdk + +type BaseResponse interface { + GetCode() string + GetMessage() string +} + +type baseResponse struct { + Code string `json:"code"` + Message string `json:"msg"` +} + +func (r *baseResponse) GetCode() string { + return r.Code +} + +func (r *baseResponse) GetMessage() string { + return r.Message +} + +type GetSiteRequest struct { + Id string `json:"-"` +} + +type GetSiteResponse struct { + baseResponse + Data *struct { + Id string `json:"id"` + Name string `json:"name"` + Domain string `json:"domain"` + HttpsListen string `json:"https_listen"` + } `json:"data"` +} + +type UpdateSiteRequest struct { + Id string `json:"-"` + HttpsListen *string `json:"https_listen,omitempty"` + Enable *bool `json:"enable,omitempty"` +} + +type UpdateSiteResponse struct { + baseResponse +} + +type CreateCertificateRequest struct { + Name string `json:"name"` + Description *string `json:"des,omitempty"` + Type string `json:"type"` + Cert string `json:"cert"` + Key string `json:"key"` +} + +type CreateCertificateResponse struct { + baseResponse + Data string `json:"data"` +} + +type UpdateCertificateRequest struct { + Id string `json:"-"` + Name *string `json:"name,omitempty"` + Description *string `json:"des,omitempty"` + Type *string `json:"type,omitempty"` + Cert *string `json:"cert,omitempty"` + Key *string `json:"key,omitempty"` + Enable *bool `json:"enable,omitempty"` +} + +type UpdateCertificateResponse struct { + baseResponse +} diff --git a/internal/pkg/vendors/cmcc-sdk/README.md b/internal/pkg/vendors/cmcc-sdk/README.md new file mode 100644 index 00000000..c64eebe8 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/README.md @@ -0,0 +1,14 @@ +移动云 Go SDK 文档: [https://ecloud.10086.cn/op-help-center/doc/article/53799](https://ecloud.10086.cn/op-help-center/doc/article/53799) + +移动云 Go SDK 下载地址: [https://ecloud.10086.cn/api/query/developer/nexus/service/rest/repository/browse/go-sdk/gitlab.ecloud.com/ecloud/](https://ecloud.10086.cn/api/query/developer/nexus/service/rest/repository/browse/go-sdk/gitlab.ecloud.com/ecloud/) + +--- + +将其引入本地目录的原因是: + +1. 原始包必须通过移动云私有仓库获取, 为构建带来不便。 +2. 原始包存在部分内容错误, 需要自行修改, 如: + + - 存在一些编译错误; + - 返回错误的时候, 未返回错误信息; + - 解析响应体错误。 diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/client.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/client.go new file mode 100644 index 00000000..d2c2ccde --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/client.go @@ -0,0 +1,144 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package ecloudsdkclouddns + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkclouddns/model" + "gitlab.ecloud.com/ecloud/ecloudsdkcore" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" +) + +type Client struct { + APIClient *ecloudsdkcore.APIClient + config *config.Config + httpRequest *ecloudsdkcore.HttpRequest +} + +func NewClient(config *config.Config) *Client { + client := &Client{} + client.config = config + apiClient := ecloudsdkcore.NewAPIClient() + httpRequest := ecloudsdkcore.NewDefaultHttpRequest() + httpRequest.Product = product + httpRequest.Version = version + httpRequest.SdkVersion = sdkVersion + client.httpRequest = httpRequest + client.APIClient = apiClient + return client +} + +func NewClientByCustomized(config *config.Config, httpRequest *ecloudsdkcore.HttpRequest) *Client { + client := &Client{} + client.config = config + apiClient := ecloudsdkcore.NewAPIClient() + httpRequest.Product = product + httpRequest.Version = version + httpRequest.SdkVersion = sdkVersion + client.httpRequest = httpRequest + client.APIClient = apiClient + return client +} + +const ( + product string = "clouddns" + version string = "v1" + sdkVersion string = "1.0.1" +) + +// CreateRecord 新增解析记录 +func (c *Client) CreateRecord(request *model.CreateRecordRequest) (*model.CreateRecordResponse, error) { + c.httpRequest.Action = "createRecord" + c.httpRequest.Body = request + returnValue := &model.CreateRecordResponse{} + if _, err := c.APIClient.Excute(c.httpRequest, c.config, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +// CreateRecordOpenapi 新增解析记录Openapi +func (c *Client) CreateRecordOpenapi(request *model.CreateRecordOpenapiRequest) (*model.CreateRecordOpenapiResponse, error) { + c.httpRequest.Action = "createRecordOpenapi" + c.httpRequest.Body = request + returnValue := &model.CreateRecordOpenapiResponse{} + if _, err := c.APIClient.Excute(c.httpRequest, c.config, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +// DeleteRecord 删除解析记录 +func (c *Client) DeleteRecord(request *model.DeleteRecordRequest) (*model.DeleteRecordResponse, error) { + c.httpRequest.Action = "deleteRecord" + c.httpRequest.Body = request + returnValue := &model.DeleteRecordResponse{} + if _, err := c.APIClient.Excute(c.httpRequest, c.config, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +// DeleteRecordOpenapi 删除解析记录Openapi +func (c *Client) DeleteRecordOpenapi(request *model.DeleteRecordOpenapiRequest) (*model.DeleteRecordOpenapiResponse, error) { + c.httpRequest.Action = "deleteRecordOpenapi" + c.httpRequest.Body = request + returnValue := &model.DeleteRecordOpenapiResponse{} + if _, err := c.APIClient.Excute(c.httpRequest, c.config, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +// ListRecord 查询解析记录 +func (c *Client) ListRecord(request *model.ListRecordRequest) (*model.ListRecordResponse, error) { + c.httpRequest.Action = "listRecord" + c.httpRequest.Body = request + returnValue := &model.ListRecordResponse{} + if _, err := c.APIClient.Excute(c.httpRequest, c.config, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +// ListRecordOpenapi 查询解析记录Openapi +func (c *Client) ListRecordOpenapi(request *model.ListRecordOpenapiRequest) (*model.ListRecordOpenapiResponse, error) { + c.httpRequest.Action = "listRecordOpenapi" + c.httpRequest.Body = request + returnValue := &model.ListRecordOpenapiResponse{} + if _, err := c.APIClient.Excute(c.httpRequest, c.config, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +// ModifyRecord 修改解析记录 +func (c *Client) ModifyRecord(request *model.ModifyRecordRequest) (*model.ModifyRecordResponse, error) { + c.httpRequest.Action = "modifyRecord" + c.httpRequest.Body = request + returnValue := &model.ModifyRecordResponse{} + if _, err := c.APIClient.Excute(c.httpRequest, c.config, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +// ModifyRecordOpenapi 修改解析记录Openapi +func (c *Client) ModifyRecordOpenapi(request *model.ModifyRecordOpenapiRequest) (*model.ModifyRecordOpenapiResponse, error) { + c.httpRequest.Action = "modifyRecordOpenapi" + c.httpRequest.Body = request + returnValue := &model.ModifyRecordOpenapiResponse{} + if _, err := c.APIClient.Excute(c.httpRequest, c.config, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/go.mod b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/go.mod new file mode 100644 index 00000000..39812459 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/go.mod @@ -0,0 +1,7 @@ +module gitlab.ecloud.com/ecloud/ecloudsdkclouddns + +go 1.23.0 + +require gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 + +replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 => ../ecloudsdkcore@v1.0.0 diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_body.go new file mode 100644 index 00000000..1175cde9 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_body.go @@ -0,0 +1,54 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) +type CreateRecordBodyTypeEnum string + +// List of Type +const ( + CreateRecordBodyTypeEnumA CreateRecordBodyTypeEnum = "A" + CreateRecordBodyTypeEnumAaaa CreateRecordBodyTypeEnum = "AAAA" + CreateRecordBodyTypeEnumCaa CreateRecordBodyTypeEnum = "CAA" + CreateRecordBodyTypeEnumCmauth CreateRecordBodyTypeEnum = "CMAUTH" + CreateRecordBodyTypeEnumCname CreateRecordBodyTypeEnum = "CNAME" + CreateRecordBodyTypeEnumMx CreateRecordBodyTypeEnum = "MX" + CreateRecordBodyTypeEnumNs CreateRecordBodyTypeEnum = "NS" + CreateRecordBodyTypeEnumPtr CreateRecordBodyTypeEnum = "PTR" + CreateRecordBodyTypeEnumRp CreateRecordBodyTypeEnum = "RP" + CreateRecordBodyTypeEnumSpf CreateRecordBodyTypeEnum = "SPF" + CreateRecordBodyTypeEnumSrv CreateRecordBodyTypeEnum = "SRV" + CreateRecordBodyTypeEnumTxt CreateRecordBodyTypeEnum = "TXT" + CreateRecordBodyTypeEnumUrl CreateRecordBodyTypeEnum = "URL" +) + +type CreateRecordBody struct { + position.Body + // 主机头 + Rr string `json:"rr"` + + // 域名名称 + DomainName string `json:"domainName"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId"` + + // MX优先级,若“记录类型”选择”MX”,则需要配置该参数,默认是5 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type CreateRecordBodyTypeEnum `json:"type"` + + // 缓存的生命周期,默认可配置600s + Ttl *int32 `json:"ttl,omitempty"` + + // 记录值 + Value string `json:"value"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_body.go new file mode 100644 index 00000000..3db9f0f7 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_body.go @@ -0,0 +1,51 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) +type CreateRecordOpenapiBodyTypeEnum string + +// List of Type +const ( + CreateRecordOpenapiBodyTypeEnumA CreateRecordOpenapiBodyTypeEnum = "A" + CreateRecordOpenapiBodyTypeEnumAaaa CreateRecordOpenapiBodyTypeEnum = "AAAA" + CreateRecordOpenapiBodyTypeEnumCname CreateRecordOpenapiBodyTypeEnum = "CNAME" + CreateRecordOpenapiBodyTypeEnumMx CreateRecordOpenapiBodyTypeEnum = "MX" + CreateRecordOpenapiBodyTypeEnumTxt CreateRecordOpenapiBodyTypeEnum = "TXT" + CreateRecordOpenapiBodyTypeEnumNs CreateRecordOpenapiBodyTypeEnum = "NS" + CreateRecordOpenapiBodyTypeEnumSpf CreateRecordOpenapiBodyTypeEnum = "SPF" + CreateRecordOpenapiBodyTypeEnumSrv CreateRecordOpenapiBodyTypeEnum = "SRV" + CreateRecordOpenapiBodyTypeEnumCaa CreateRecordOpenapiBodyTypeEnum = "CAA" + CreateRecordOpenapiBodyTypeEnumCmauth CreateRecordOpenapiBodyTypeEnum = "CMAUTH" +) + +type CreateRecordOpenapiBody struct { + position.Body + // 主机头 + Rr string `json:"rr"` + + // 域名名称 + DomainName string `json:"domainName"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId"` + + // MX优先级,若“记录类型”选择”MX”,则需要配置该参数,默认是5 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type CreateRecordOpenapiBodyTypeEnum `json:"type"` + + // 缓存的生命周期,默认可配置600s + Ttl *int32 `json:"ttl,omitempty"` + + // 记录值 + Value string `json:"value"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_request.go new file mode 100644 index 00000000..d43fded1 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_request.go @@ -0,0 +1,12 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type CreateRecordOpenapiRequest struct { + + CreateRecordOpenapiBody *CreateRecordOpenapiBody `json:"createRecordOpenapiBody,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response.go new file mode 100644 index 00000000..a33b47c7 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type CreateRecordOpenapiResponseStateEnum string + +// List of State +const ( + CreateRecordOpenapiResponseStateEnumError CreateRecordOpenapiResponseStateEnum = "ERROR" + CreateRecordOpenapiResponseStateEnumException CreateRecordOpenapiResponseStateEnum = "EXCEPTION" + CreateRecordOpenapiResponseStateEnumForbidden CreateRecordOpenapiResponseStateEnum = "FORBIDDEN" + CreateRecordOpenapiResponseStateEnumOk CreateRecordOpenapiResponseStateEnum = "OK" +) + +type CreateRecordOpenapiResponse struct { + + RequestId string `json:"requestId,omitempty"` + + ErrorMessage string `json:"errorMessage,omitempty"` + + ErrorCode string `json:"errorCode,omitempty"` + + State CreateRecordOpenapiResponseStateEnum `json:"state,omitempty"` + + Body *CreateRecordOpenapiResponseBody `json:"body,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_body.go new file mode 100644 index 00000000..e7c62769 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_body.go @@ -0,0 +1,80 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type CreateRecordOpenapiResponseBodyTypeEnum string + +// List of Type +const ( + CreateRecordOpenapiResponseBodyTypeEnumA CreateRecordOpenapiResponseBodyTypeEnum = "A" + CreateRecordOpenapiResponseBodyTypeEnumAaaa CreateRecordOpenapiResponseBodyTypeEnum = "AAAA" + CreateRecordOpenapiResponseBodyTypeEnumCname CreateRecordOpenapiResponseBodyTypeEnum = "CNAME" + CreateRecordOpenapiResponseBodyTypeEnumMx CreateRecordOpenapiResponseBodyTypeEnum = "MX" + CreateRecordOpenapiResponseBodyTypeEnumTxt CreateRecordOpenapiResponseBodyTypeEnum = "TXT" + CreateRecordOpenapiResponseBodyTypeEnumNs CreateRecordOpenapiResponseBodyTypeEnum = "NS" + CreateRecordOpenapiResponseBodyTypeEnumSpf CreateRecordOpenapiResponseBodyTypeEnum = "SPF" + CreateRecordOpenapiResponseBodyTypeEnumSrv CreateRecordOpenapiResponseBodyTypeEnum = "SRV" + CreateRecordOpenapiResponseBodyTypeEnumCaa CreateRecordOpenapiResponseBodyTypeEnum = "CAA" + CreateRecordOpenapiResponseBodyTypeEnumCmauth CreateRecordOpenapiResponseBodyTypeEnum = "CMAUTH" +) +type CreateRecordOpenapiResponseBodyStateEnum string + +// List of State +const ( + CreateRecordOpenapiResponseBodyStateEnumDisabled CreateRecordOpenapiResponseBodyStateEnum = "DISABLED" + CreateRecordOpenapiResponseBodyStateEnumEnabled CreateRecordOpenapiResponseBodyStateEnum = "ENABLED" +) + +type CreateRecordOpenapiResponseBody struct { + + // 主机头 + Rr string `json:"rr,omitempty"` + + // 修改时间 + ModifiedTime string `json:"modifiedTime,omitempty"` + + // 线路中文名 + LineZh string `json:"lineZh,omitempty"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId,omitempty"` + + // 权重值 + Weight *int32 `json:"weight,omitempty"` + + // MX优先级 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type CreateRecordOpenapiResponseBodyTypeEnum `json:"type,omitempty"` + + // 缓存的生命周期 + Ttl *int32 `json:"ttl,omitempty"` + + // 标签 + Tags *[]CreateRecordOpenapiResponseTags `json:"tags,omitempty"` + + // 解析记录ID + RecordId string `json:"recordId,omitempty"` + + // 域名名称 + DomainName string `json:"domainName,omitempty"` + + // 线路英文名 + LineEn string `json:"lineEn,omitempty"` + + // 状态 + State CreateRecordOpenapiResponseBodyStateEnum `json:"state,omitempty"` + + // 记录值 + Value string `json:"value,omitempty"` + + // 定时发布时间 + Pubdate string `json:"pubdate,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_tags.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_tags.go new file mode 100644 index 00000000..a4bda62c --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_openapi_response_tags.go @@ -0,0 +1,16 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type CreateRecordOpenapiResponseTags struct { + + // 标签ID + TagId string `json:"tagId,omitempty"` + + // 标签名称 + Value string `json:"value,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_request.go new file mode 100644 index 00000000..715f03ff --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_request.go @@ -0,0 +1,12 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type CreateRecordRequest struct { + + CreateRecordBody *CreateRecordBody `json:"createRecordBody,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response.go new file mode 100644 index 00000000..bd277c0c --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type CreateRecordResponseStateEnum string + +// List of State +const ( + CreateRecordResponseStateEnumError CreateRecordResponseStateEnum = "ERROR" + CreateRecordResponseStateEnumException CreateRecordResponseStateEnum = "EXCEPTION" + CreateRecordResponseStateEnumForbidden CreateRecordResponseStateEnum = "FORBIDDEN" + CreateRecordResponseStateEnumOk CreateRecordResponseStateEnum = "OK" +) + +type CreateRecordResponse struct { + + RequestId string `json:"requestId,omitempty"` + + ErrorMessage string `json:"errorMessage,omitempty"` + + ErrorCode string `json:"errorCode,omitempty"` + + State CreateRecordResponseStateEnum `json:"state,omitempty"` + + Body *CreateRecordResponseBody `json:"body,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_body.go new file mode 100644 index 00000000..64660b91 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_body.go @@ -0,0 +1,94 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type CreateRecordResponseBodyTypeEnum string + +// List of Type +const ( + CreateRecordResponseBodyTypeEnumA CreateRecordResponseBodyTypeEnum = "A" + CreateRecordResponseBodyTypeEnumAaaa CreateRecordResponseBodyTypeEnum = "AAAA" + CreateRecordResponseBodyTypeEnumCaa CreateRecordResponseBodyTypeEnum = "CAA" + CreateRecordResponseBodyTypeEnumCmauth CreateRecordResponseBodyTypeEnum = "CMAUTH" + CreateRecordResponseBodyTypeEnumCname CreateRecordResponseBodyTypeEnum = "CNAME" + CreateRecordResponseBodyTypeEnumMx CreateRecordResponseBodyTypeEnum = "MX" + CreateRecordResponseBodyTypeEnumNs CreateRecordResponseBodyTypeEnum = "NS" + CreateRecordResponseBodyTypeEnumPtr CreateRecordResponseBodyTypeEnum = "PTR" + CreateRecordResponseBodyTypeEnumRp CreateRecordResponseBodyTypeEnum = "RP" + CreateRecordResponseBodyTypeEnumSpf CreateRecordResponseBodyTypeEnum = "SPF" + CreateRecordResponseBodyTypeEnumSrv CreateRecordResponseBodyTypeEnum = "SRV" + CreateRecordResponseBodyTypeEnumTxt CreateRecordResponseBodyTypeEnum = "TXT" + CreateRecordResponseBodyTypeEnumUrl CreateRecordResponseBodyTypeEnum = "URL" +) +type CreateRecordResponseBodyTimedStatusEnum string + +// List of TimedStatus +const ( + CreateRecordResponseBodyTimedStatusEnumDisabled CreateRecordResponseBodyTimedStatusEnum = "DISABLED" + CreateRecordResponseBodyTimedStatusEnumEnabled CreateRecordResponseBodyTimedStatusEnum = "ENABLED" + CreateRecordResponseBodyTimedStatusEnumTimed CreateRecordResponseBodyTimedStatusEnum = "TIMED" +) +type CreateRecordResponseBodyStateEnum string + +// List of State +const ( + CreateRecordResponseBodyStateEnumDisabled CreateRecordResponseBodyStateEnum = "DISABLED" + CreateRecordResponseBodyStateEnumEnabled CreateRecordResponseBodyStateEnum = "ENABLED" +) + +type CreateRecordResponseBody struct { + + // 主机头 + Rr string `json:"rr,omitempty"` + + // 修改时间 + ModifiedTime string `json:"modifiedTime,omitempty"` + + // 线路中文名 + LineZh string `json:"lineZh,omitempty"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId,omitempty"` + + // 权重值 + Weight *int32 `json:"weight,omitempty"` + + // MX优先级 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type CreateRecordResponseBodyTypeEnum `json:"type,omitempty"` + + // 缓存的生命周期 + Ttl *int32 `json:"ttl,omitempty"` + + // 标签 + Tags *[]CreateRecordResponseTags `json:"tags,omitempty"` + + // 解析记录ID + RecordId string `json:"recordId,omitempty"` + + // 定时状态 + TimedStatus CreateRecordResponseBodyTimedStatusEnum `json:"timedStatus,omitempty"` + + // 域名名称 + DomainName string `json:"domainName,omitempty"` + + // 线路英文名 + LineEn string `json:"lineEn,omitempty"` + + // 状态 + State CreateRecordResponseBodyStateEnum `json:"state,omitempty"` + + // 记录值 + Value string `json:"value,omitempty"` + + // 定时发布时间 + Pubdate string `json:"pubdate,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_tags.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_tags.go new file mode 100644 index 00000000..003680d5 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/create_record_response_tags.go @@ -0,0 +1,16 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type CreateRecordResponseTags struct { + + // 标签ID + TagId string `json:"tagId,omitempty"` + + // 标签名称 + Value string `json:"value,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_body.go new file mode 100644 index 00000000..326b9abb --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_body.go @@ -0,0 +1,15 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) + +type DeleteRecordBody struct { + position.Body + // 解析记录ID列表 + RecordIdList []string `json:"recordIdList"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_body.go new file mode 100644 index 00000000..e5614ac5 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_body.go @@ -0,0 +1,15 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) + +type DeleteRecordOpenapiBody struct { + position.Body + // 待删除的解析记录ID请求体 + RecordIdList []string `json:"recordIdList"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_request.go new file mode 100644 index 00000000..1cb684d6 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_request.go @@ -0,0 +1,12 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type DeleteRecordOpenapiRequest struct { + + DeleteRecordOpenapiBody *DeleteRecordOpenapiBody `json:"deleteRecordOpenapiBody,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response.go new file mode 100644 index 00000000..ca528148 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type DeleteRecordOpenapiResponseStateEnum string + +// List of State +const ( + DeleteRecordOpenapiResponseStateEnumError DeleteRecordOpenapiResponseStateEnum = "ERROR" + DeleteRecordOpenapiResponseStateEnumException DeleteRecordOpenapiResponseStateEnum = "EXCEPTION" + DeleteRecordOpenapiResponseStateEnumForbidden DeleteRecordOpenapiResponseStateEnum = "FORBIDDEN" + DeleteRecordOpenapiResponseStateEnumOk DeleteRecordOpenapiResponseStateEnum = "OK" +) + +type DeleteRecordOpenapiResponse struct { + + RequestId string `json:"requestId,omitempty"` + + ErrorMessage string `json:"errorMessage,omitempty"` + + ErrorCode string `json:"errorCode,omitempty"` + + State DeleteRecordOpenapiResponseStateEnum `json:"state,omitempty"` + + Body *[]DeleteRecordOpenapiResponseBody `json:"body,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response_body.go new file mode 100644 index 00000000..67da3ab7 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_openapi_response_body.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type DeleteRecordOpenapiResponseBodyCodeEnum string + +// List of Code +const ( + DeleteRecordOpenapiResponseBodyCodeEnumError DeleteRecordOpenapiResponseBodyCodeEnum = "ERROR" + DeleteRecordOpenapiResponseBodyCodeEnumSuccess DeleteRecordOpenapiResponseBodyCodeEnum = "SUCCESS" +) + +type DeleteRecordOpenapiResponseBody struct { + + // 结果说明 + Msg string `json:"msg,omitempty"` + + // 解析记录ID + RecordId string `json:"recordId,omitempty"` + + // 结果码 + Code DeleteRecordOpenapiResponseBodyCodeEnum `json:"code,omitempty"` + + // 域名 + DomainName string `json:"domainName,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_request.go new file mode 100644 index 00000000..678fd8ef --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_request.go @@ -0,0 +1,12 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type DeleteRecordRequest struct { + + DeleteRecordBody *DeleteRecordBody `json:"deleteRecordBody,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response.go new file mode 100644 index 00000000..051d7105 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type DeleteRecordResponseStateEnum string + +// List of State +const ( + DeleteRecordResponseStateEnumError DeleteRecordResponseStateEnum = "ERROR" + DeleteRecordResponseStateEnumException DeleteRecordResponseStateEnum = "EXCEPTION" + DeleteRecordResponseStateEnumForbidden DeleteRecordResponseStateEnum = "FORBIDDEN" + DeleteRecordResponseStateEnumOk DeleteRecordResponseStateEnum = "OK" +) + +type DeleteRecordResponse struct { + + RequestId string `json:"requestId,omitempty"` + + ErrorMessage string `json:"errorMessage,omitempty"` + + ErrorCode string `json:"errorCode,omitempty"` + + State DeleteRecordResponseStateEnum `json:"state,omitempty"` + + Body *[]DeleteRecordResponseBody `json:"body,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response_body.go new file mode 100644 index 00000000..45320290 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/delete_record_response_body.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type DeleteRecordResponseBodyCodeEnum string + +// List of Code +const ( + DeleteRecordResponseBodyCodeEnumError DeleteRecordResponseBodyCodeEnum = "ERROR" + DeleteRecordResponseBodyCodeEnumSuccess DeleteRecordResponseBodyCodeEnum = "SUCCESS" +) + +type DeleteRecordResponseBody struct { + + // 结果说明 + Msg string `json:"msg,omitempty"` + + // 解析记录ID + RecordId string `json:"recordId,omitempty"` + + // 结果码 + Code DeleteRecordResponseBodyCodeEnum `json:"code,omitempty"` + + // 域名 + DomainName string `json:"domainName,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_body.go new file mode 100644 index 00000000..5eadce7a --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_body.go @@ -0,0 +1,18 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) + +type ListRecordBody struct { + position.Body + // 域名 + DomainName string `json:"domainName"` + + // 可以匹配主机头rr、记录值value、备注description,并且是模糊搜索 + DataLike string `json:"dataLike,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_body.go new file mode 100644 index 00000000..63f5e13a --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_body.go @@ -0,0 +1,15 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) + +type ListRecordOpenapiBody struct { + position.Body + // 域名 + DomainName string `json:"domainName"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_query.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_query.go new file mode 100644 index 00000000..ee89a7f5 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_query.go @@ -0,0 +1,18 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) + +type ListRecordOpenapiQuery struct { + position.Query + // 页大小 + PageSize *int32 `json:"pageSize,omitempty"` + + // 当前页 + Page *int32 `json:"page,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_request.go new file mode 100644 index 00000000..34540481 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_request.go @@ -0,0 +1,14 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type ListRecordOpenapiRequest struct { + + ListRecordOpenapiQuery *ListRecordOpenapiQuery `json:"listRecordOpenapiQuery,omitempty"` + + ListRecordOpenapiBody *ListRecordOpenapiBody `json:"listRecordOpenapiBody,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response.go new file mode 100644 index 00000000..36646938 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type ListRecordOpenapiResponseStateEnum string + +// List of State +const ( + ListRecordOpenapiResponseStateEnumError ListRecordOpenapiResponseStateEnum = "ERROR" + ListRecordOpenapiResponseStateEnumException ListRecordOpenapiResponseStateEnum = "EXCEPTION" + ListRecordOpenapiResponseStateEnumForbidden ListRecordOpenapiResponseStateEnum = "FORBIDDEN" + ListRecordOpenapiResponseStateEnumOk ListRecordOpenapiResponseStateEnum = "OK" +) + +type ListRecordOpenapiResponse struct { + + RequestId string `json:"requestId,omitempty"` + + ErrorMessage string `json:"errorMessage,omitempty"` + + ErrorCode string `json:"errorCode,omitempty"` + + State ListRecordOpenapiResponseStateEnum `json:"state,omitempty"` + + Body *ListRecordOpenapiResponseBody `json:"body,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_body.go new file mode 100644 index 00000000..8c6f7302 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_body.go @@ -0,0 +1,25 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type ListRecordOpenapiResponseBody struct { + + // 当前页的具体数据列表 + Data *[]ListRecordOpenapiResponseData `json:"data,omitempty"` + + // 总数据量 + TotalNum *int32 `json:"totalNum,omitempty"` + + // 总页数 + TotalPages *int32 `json:"totalPages,omitempty"` + + // 页大小 + PageSize *int32 `json:"pageSize,omitempty"` + + // 当前页码,从0开始,0表示第一页 + Page *int32 `json:"page,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_data.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_data.go new file mode 100644 index 00000000..19981aa9 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_data.go @@ -0,0 +1,91 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type ListRecordOpenapiResponseDataTypeEnum string + +// List of Type +const ( + ListRecordOpenapiResponseDataTypeEnumA ListRecordOpenapiResponseDataTypeEnum = "A" + ListRecordOpenapiResponseDataTypeEnumAaaa ListRecordOpenapiResponseDataTypeEnum = "AAAA" + ListRecordOpenapiResponseDataTypeEnumCname ListRecordOpenapiResponseDataTypeEnum = "CNAME" + ListRecordOpenapiResponseDataTypeEnumMx ListRecordOpenapiResponseDataTypeEnum = "MX" + ListRecordOpenapiResponseDataTypeEnumTxt ListRecordOpenapiResponseDataTypeEnum = "TXT" + ListRecordOpenapiResponseDataTypeEnumNs ListRecordOpenapiResponseDataTypeEnum = "NS" + ListRecordOpenapiResponseDataTypeEnumSpf ListRecordOpenapiResponseDataTypeEnum = "SPF" + ListRecordOpenapiResponseDataTypeEnumSrv ListRecordOpenapiResponseDataTypeEnum = "SRV" + ListRecordOpenapiResponseDataTypeEnumCaa ListRecordOpenapiResponseDataTypeEnum = "CAA" + ListRecordOpenapiResponseDataTypeEnumCmauth ListRecordOpenapiResponseDataTypeEnum = "CMAUTH" +) +type ListRecordOpenapiResponseDataTimedStatusEnum string + +// List of TimedStatus +const ( + ListRecordOpenapiResponseDataTimedStatusEnumDisabled ListRecordOpenapiResponseDataTimedStatusEnum = "DISABLED" + ListRecordOpenapiResponseDataTimedStatusEnumEnabled ListRecordOpenapiResponseDataTimedStatusEnum = "ENABLED" + ListRecordOpenapiResponseDataTimedStatusEnumTimed ListRecordOpenapiResponseDataTimedStatusEnum = "TIMED" +) +type ListRecordOpenapiResponseDataStateEnum string + +// List of State +const ( + ListRecordOpenapiResponseDataStateEnumDisabled ListRecordOpenapiResponseDataStateEnum = "DISABLED" + ListRecordOpenapiResponseDataStateEnumEnabled ListRecordOpenapiResponseDataStateEnum = "ENABLED" +) + +type ListRecordOpenapiResponseData struct { + + // 主机头 + Rr string `json:"rr,omitempty"` + + // 修改时间 + ModifiedTime string `json:"modifiedTime,omitempty"` + + // 线路中文名 + LineZh string `json:"lineZh,omitempty"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId,omitempty"` + + // 权重值 + Weight *int32 `json:"weight,omitempty"` + + // MX优先级 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type ListRecordOpenapiResponseDataTypeEnum `json:"type,omitempty"` + + // 缓存的生命周期 + Ttl *int32 `json:"ttl,omitempty"` + + // 标签 + Tags *[]ListRecordOpenapiResponseTags `json:"tags,omitempty"` + + // 解析记录ID + RecordId string `json:"recordId,omitempty"` + + // 定时状态 + TimedStatus ListRecordOpenapiResponseDataTimedStatusEnum `json:"timedStatus,omitempty"` + + // 域名名称 + DomainName string `json:"domainName,omitempty"` + + // 线路英文名 + LineEn string `json:"lineEn,omitempty"` + + // 状态 + State ListRecordOpenapiResponseDataStateEnum `json:"state,omitempty"` + + // 记录值 + Value string `json:"value,omitempty"` + + // 定时发布时间 + Pubdate string `json:"pubdate,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_tags.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_tags.go new file mode 100644 index 00000000..867f667f --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_openapi_response_tags.go @@ -0,0 +1,16 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type ListRecordOpenapiResponseTags struct { + + // 标签ID + TagId string `json:"tagId,omitempty"` + + // 标签名称 + Value string `json:"value,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_query.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_query.go new file mode 100644 index 00000000..df871a76 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_query.go @@ -0,0 +1,18 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) + +type ListRecordQuery struct { + position.Query + // 页大小 + PageSize *int32 `json:"pageSize,omitempty"` + + // 当前页 + CurrentPage *int32 `json:"currentPage,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_request.go new file mode 100644 index 00000000..5ff9df08 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_request.go @@ -0,0 +1,14 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type ListRecordRequest struct { + + ListRecordBody *ListRecordBody `json:"listRecordBody,omitempty"` + + ListRecordQuery *ListRecordQuery `json:"listRecordQuery,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response.go new file mode 100644 index 00000000..b11f3d21 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type ListRecordResponseStateEnum string + +// List of State +const ( + ListRecordResponseStateEnumError ListRecordResponseStateEnum = "ERROR" + ListRecordResponseStateEnumException ListRecordResponseStateEnum = "EXCEPTION" + ListRecordResponseStateEnumForbidden ListRecordResponseStateEnum = "FORBIDDEN" + ListRecordResponseStateEnumOk ListRecordResponseStateEnum = "OK" +) + +type ListRecordResponse struct { + + RequestId string `json:"requestId,omitempty"` + + ErrorMessage string `json:"errorMessage,omitempty"` + + ErrorCode string `json:"errorCode,omitempty"` + + State ListRecordResponseStateEnum `json:"state,omitempty"` + + Body *ListRecordResponseBody `json:"body,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_body.go new file mode 100644 index 00000000..0acf543d --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_body.go @@ -0,0 +1,22 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type ListRecordResponseBody struct { + + // 总页数 + TotalPages *int32 `json:"totalPages,omitempty"` + + // 当前页码,从0开始,0表示第一页 + CurrentPage *int32 `json:"currentPage,omitempty"` + + // 当前页的具体数据列表 + Results *[]ListRecordResponseResults `json:"results,omitempty"` + + // 总数据量 + TotalElements *int64 `json:"totalElements,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_results.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_results.go new file mode 100644 index 00000000..7498fca3 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/list_record_response_results.go @@ -0,0 +1,91 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type ListRecordResponseResultsTypeEnum string + +// List of Type +const ( + ListRecordResponseResultsTypeEnumA ListRecordResponseResultsTypeEnum = "A" + ListRecordResponseResultsTypeEnumAaaa ListRecordResponseResultsTypeEnum = "AAAA" + ListRecordResponseResultsTypeEnumCaa ListRecordResponseResultsTypeEnum = "CAA" + ListRecordResponseResultsTypeEnumCmauth ListRecordResponseResultsTypeEnum = "CMAUTH" + ListRecordResponseResultsTypeEnumCname ListRecordResponseResultsTypeEnum = "CNAME" + ListRecordResponseResultsTypeEnumMx ListRecordResponseResultsTypeEnum = "MX" + ListRecordResponseResultsTypeEnumNs ListRecordResponseResultsTypeEnum = "NS" + ListRecordResponseResultsTypeEnumPtr ListRecordResponseResultsTypeEnum = "PTR" + ListRecordResponseResultsTypeEnumRp ListRecordResponseResultsTypeEnum = "RP" + ListRecordResponseResultsTypeEnumSpf ListRecordResponseResultsTypeEnum = "SPF" + ListRecordResponseResultsTypeEnumSrv ListRecordResponseResultsTypeEnum = "SRV" + ListRecordResponseResultsTypeEnumTxt ListRecordResponseResultsTypeEnum = "TXT" + ListRecordResponseResultsTypeEnumUrl ListRecordResponseResultsTypeEnum = "URL" +) +type ListRecordResponseResultsTimedStatusEnum string + +// List of TimedStatus +const ( + ListRecordResponseResultsTimedStatusEnumDisabled ListRecordResponseResultsTimedStatusEnum = "DISABLED" + ListRecordResponseResultsTimedStatusEnumEnabled ListRecordResponseResultsTimedStatusEnum = "ENABLED" + ListRecordResponseResultsTimedStatusEnumTimed ListRecordResponseResultsTimedStatusEnum = "TIMED" +) +type ListRecordResponseResultsStateEnum string + +// List of State +const ( + ListRecordResponseResultsStateEnumDisabled ListRecordResponseResultsStateEnum = "DISABLED" + ListRecordResponseResultsStateEnumEnabled ListRecordResponseResultsStateEnum = "ENABLED" +) + +type ListRecordResponseResults struct { + + // 主机头 + Rr string `json:"rr,omitempty"` + + // 修改时间 + ModifiedTime string `json:"modifiedTime,omitempty"` + + // 线路中文名 + LineZh string `json:"lineZh,omitempty"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId,omitempty"` + + // 权重值 + Weight *int32 `json:"weight,omitempty"` + + // MX优先级 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type ListRecordResponseResultsTypeEnum `json:"type,omitempty"` + + // 缓存的生命周期 + Ttl *int32 `json:"ttl,omitempty"` + + // 解析记录ID + RecordId string `json:"recordId,omitempty"` + + // 定时状态 + TimedStatus ListRecordResponseResultsTimedStatusEnum `json:"timedStatus,omitempty"` + + // 域名名称 + DomainName string `json:"domainName,omitempty"` + + // 线路英文名 + LineEn string `json:"lineEn,omitempty"` + + // 状态 + State ListRecordResponseResultsStateEnum `json:"state,omitempty"` + + // 记录值 + Value string `json:"value,omitempty"` + + // 定时发布时间 + Pubdate string `json:"pubdate,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_body.go new file mode 100644 index 00000000..ab772c09 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_body.go @@ -0,0 +1,57 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) +type ModifyRecordBodyTypeEnum string + +// List of Type +const ( + ModifyRecordBodyTypeEnumA ModifyRecordBodyTypeEnum = "A" + ModifyRecordBodyTypeEnumAaaa ModifyRecordBodyTypeEnum = "AAAA" + ModifyRecordBodyTypeEnumCaa ModifyRecordBodyTypeEnum = "CAA" + ModifyRecordBodyTypeEnumCmauth ModifyRecordBodyTypeEnum = "CMAUTH" + ModifyRecordBodyTypeEnumCname ModifyRecordBodyTypeEnum = "CNAME" + ModifyRecordBodyTypeEnumMx ModifyRecordBodyTypeEnum = "MX" + ModifyRecordBodyTypeEnumNs ModifyRecordBodyTypeEnum = "NS" + ModifyRecordBodyTypeEnumPtr ModifyRecordBodyTypeEnum = "PTR" + ModifyRecordBodyTypeEnumRp ModifyRecordBodyTypeEnum = "RP" + ModifyRecordBodyTypeEnumSpf ModifyRecordBodyTypeEnum = "SPF" + ModifyRecordBodyTypeEnumSrv ModifyRecordBodyTypeEnum = "SRV" + ModifyRecordBodyTypeEnumTxt ModifyRecordBodyTypeEnum = "TXT" + ModifyRecordBodyTypeEnumUrl ModifyRecordBodyTypeEnum = "URL" +) + +type ModifyRecordBody struct { + position.Body + // 解析记录ID + RecordId string `json:"recordId"` + + // 主机头 + Rr string `json:"rr,omitempty"` + + // 域名名称 + DomainName string `json:"domainName"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId,omitempty"` + + // MX优先级 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type ModifyRecordBodyTypeEnum `json:"type,omitempty"` + + // 缓存的生命周期 + Ttl *int32 `json:"ttl,omitempty"` + + // 记录值 + Value string `json:"value,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_body.go new file mode 100644 index 00000000..3bde8919 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_body.go @@ -0,0 +1,54 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore/position" +) +type ModifyRecordOpenapiBodyTypeEnum string + +// List of Type +const ( + ModifyRecordOpenapiBodyTypeEnumA ModifyRecordOpenapiBodyTypeEnum = "A" + ModifyRecordOpenapiBodyTypeEnumAaaa ModifyRecordOpenapiBodyTypeEnum = "AAAA" + ModifyRecordOpenapiBodyTypeEnumCname ModifyRecordOpenapiBodyTypeEnum = "CNAME" + ModifyRecordOpenapiBodyTypeEnumMx ModifyRecordOpenapiBodyTypeEnum = "MX" + ModifyRecordOpenapiBodyTypeEnumTxt ModifyRecordOpenapiBodyTypeEnum = "TXT" + ModifyRecordOpenapiBodyTypeEnumNs ModifyRecordOpenapiBodyTypeEnum = "NS" + ModifyRecordOpenapiBodyTypeEnumSpf ModifyRecordOpenapiBodyTypeEnum = "SPF" + ModifyRecordOpenapiBodyTypeEnumSrv ModifyRecordOpenapiBodyTypeEnum = "SRV" + ModifyRecordOpenapiBodyTypeEnumCaa ModifyRecordOpenapiBodyTypeEnum = "CAA" + ModifyRecordOpenapiBodyTypeEnumCmauth ModifyRecordOpenapiBodyTypeEnum = "CMAUTH" +) + +type ModifyRecordOpenapiBody struct { + position.Body + // 解析记录ID + RecordId string `json:"recordId"` + + // 主机头 + Rr string `json:"rr,omitempty"` + + // 域名名称 + DomainName string `json:"domainName"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId,omitempty"` + + // MX优先级 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type ModifyRecordOpenapiBodyTypeEnum `json:"type,omitempty"` + + // 缓存的生命周期 + Ttl *int32 `json:"ttl,omitempty"` + + // 记录值 + Value string `json:"value,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_request.go new file mode 100644 index 00000000..fd1ffe00 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_request.go @@ -0,0 +1,12 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type ModifyRecordOpenapiRequest struct { + + ModifyRecordOpenapiBody *ModifyRecordOpenapiBody `json:"modifyRecordOpenapiBody,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response.go new file mode 100644 index 00000000..97d7552f --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type ModifyRecordOpenapiResponseStateEnum string + +// List of State +const ( + ModifyRecordOpenapiResponseStateEnumError ModifyRecordOpenapiResponseStateEnum = "ERROR" + ModifyRecordOpenapiResponseStateEnumException ModifyRecordOpenapiResponseStateEnum = "EXCEPTION" + ModifyRecordOpenapiResponseStateEnumForbidden ModifyRecordOpenapiResponseStateEnum = "FORBIDDEN" + ModifyRecordOpenapiResponseStateEnumOk ModifyRecordOpenapiResponseStateEnum = "OK" +) + +type ModifyRecordOpenapiResponse struct { + + RequestId string `json:"requestId,omitempty"` + + ErrorMessage string `json:"errorMessage,omitempty"` + + ErrorCode string `json:"errorCode,omitempty"` + + State ModifyRecordOpenapiResponseStateEnum `json:"state,omitempty"` + + Body *ModifyRecordOpenapiResponseBody `json:"body,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_body.go new file mode 100644 index 00000000..6dfcec71 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_body.go @@ -0,0 +1,91 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type ModifyRecordOpenapiResponseBodyTypeEnum string + +// List of Type +const ( + ModifyRecordOpenapiResponseBodyTypeEnumA ModifyRecordOpenapiResponseBodyTypeEnum = "A" + ModifyRecordOpenapiResponseBodyTypeEnumAaaa ModifyRecordOpenapiResponseBodyTypeEnum = "AAAA" + ModifyRecordOpenapiResponseBodyTypeEnumCname ModifyRecordOpenapiResponseBodyTypeEnum = "CNAME" + ModifyRecordOpenapiResponseBodyTypeEnumMx ModifyRecordOpenapiResponseBodyTypeEnum = "MX" + ModifyRecordOpenapiResponseBodyTypeEnumTxt ModifyRecordOpenapiResponseBodyTypeEnum = "TXT" + ModifyRecordOpenapiResponseBodyTypeEnumNs ModifyRecordOpenapiResponseBodyTypeEnum = "NS" + ModifyRecordOpenapiResponseBodyTypeEnumSpf ModifyRecordOpenapiResponseBodyTypeEnum = "SPF" + ModifyRecordOpenapiResponseBodyTypeEnumSrv ModifyRecordOpenapiResponseBodyTypeEnum = "SRV" + ModifyRecordOpenapiResponseBodyTypeEnumCaa ModifyRecordOpenapiResponseBodyTypeEnum = "CAA" + ModifyRecordOpenapiResponseBodyTypeEnumCmauth ModifyRecordOpenapiResponseBodyTypeEnum = "CMAUTH" +) +type ModifyRecordOpenapiResponseBodyTimedStatusEnum string + +// List of TimedStatus +const ( + ModifyRecordOpenapiResponseBodyTimedStatusEnumDisabled ModifyRecordOpenapiResponseBodyTimedStatusEnum = "DISABLED" + ModifyRecordOpenapiResponseBodyTimedStatusEnumEnabled ModifyRecordOpenapiResponseBodyTimedStatusEnum = "ENABLED" + ModifyRecordOpenapiResponseBodyTimedStatusEnumTimed ModifyRecordOpenapiResponseBodyTimedStatusEnum = "TIMED" +) +type ModifyRecordOpenapiResponseBodyStateEnum string + +// List of State +const ( + ModifyRecordOpenapiResponseBodyStateEnumDisabled ModifyRecordOpenapiResponseBodyStateEnum = "DISABLED" + ModifyRecordOpenapiResponseBodyStateEnumEnabled ModifyRecordOpenapiResponseBodyStateEnum = "ENABLED" +) + +type ModifyRecordOpenapiResponseBody struct { + + // 主机头 + Rr string `json:"rr,omitempty"` + + // 修改时间 + ModifiedTime string `json:"modifiedTime,omitempty"` + + // 线路中文名 + LineZh string `json:"lineZh,omitempty"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId,omitempty"` + + // 权重值 + Weight *int32 `json:"weight,omitempty"` + + // MX优先级 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type ModifyRecordOpenapiResponseBodyTypeEnum `json:"type,omitempty"` + + // 缓存的生命周期 + Ttl *int32 `json:"ttl,omitempty"` + + // 标签 + Tags *[]ModifyRecordOpenapiResponseTags `json:"tags,omitempty"` + + // 解析记录ID + RecordId string `json:"recordId,omitempty"` + + // 定时状态 + TimedStatus ModifyRecordOpenapiResponseBodyTimedStatusEnum `json:"timedStatus,omitempty"` + + // 域名名称 + DomainName string `json:"domainName,omitempty"` + + // 线路英文名 + LineEn string `json:"lineEn,omitempty"` + + // 状态 + State ModifyRecordOpenapiResponseBodyStateEnum `json:"state,omitempty"` + + // 记录值 + Value string `json:"value,omitempty"` + + // 定时发布时间 + Pubdate string `json:"pubdate,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_tags.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_tags.go new file mode 100644 index 00000000..62c2e780 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_openapi_response_tags.go @@ -0,0 +1,16 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type ModifyRecordOpenapiResponseTags struct { + + // 标签ID + TagId string `json:"tagId,omitempty"` + + // 标签名称 + Value string `json:"value,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_request.go new file mode 100644 index 00000000..d92abb44 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_request.go @@ -0,0 +1,12 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + + +type ModifyRecordRequest struct { + + ModifyRecordBody *ModifyRecordBody `json:"modifyRecordBody,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response.go new file mode 100644 index 00000000..45b1ae0f --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response.go @@ -0,0 +1,29 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type ModifyRecordResponseStateEnum string + +// List of State +const ( + ModifyRecordResponseStateEnumError ModifyRecordResponseStateEnum = "ERROR" + ModifyRecordResponseStateEnumException ModifyRecordResponseStateEnum = "EXCEPTION" + ModifyRecordResponseStateEnumForbidden ModifyRecordResponseStateEnum = "FORBIDDEN" + ModifyRecordResponseStateEnumOk ModifyRecordResponseStateEnum = "OK" +) + +type ModifyRecordResponse struct { + + RequestId string `json:"requestId,omitempty"` + + ErrorMessage string `json:"errorMessage,omitempty"` + + ErrorCode string `json:"errorCode,omitempty"` + + State ModifyRecordResponseStateEnum `json:"state,omitempty"` + + Body *ModifyRecordResponseBody `json:"body,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response_body.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response_body.go new file mode 100644 index 00000000..3df09342 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1/model/modify_record_response_body.go @@ -0,0 +1,77 @@ +// @Title Golang SDK Client +// @Description This code is auto generated +// @Author Ecloud SDK + +package model + + +type ModifyRecordResponseBodyTypeEnum string + +// List of Type +const ( + ModifyRecordResponseBodyTypeEnumA ModifyRecordResponseBodyTypeEnum = "A" + ModifyRecordResponseBodyTypeEnumAaaa ModifyRecordResponseBodyTypeEnum = "AAAA" + ModifyRecordResponseBodyTypeEnumCaa ModifyRecordResponseBodyTypeEnum = "CAA" + ModifyRecordResponseBodyTypeEnumCmauth ModifyRecordResponseBodyTypeEnum = "CMAUTH" + ModifyRecordResponseBodyTypeEnumCname ModifyRecordResponseBodyTypeEnum = "CNAME" + ModifyRecordResponseBodyTypeEnumMx ModifyRecordResponseBodyTypeEnum = "MX" + ModifyRecordResponseBodyTypeEnumNs ModifyRecordResponseBodyTypeEnum = "NS" + ModifyRecordResponseBodyTypeEnumPtr ModifyRecordResponseBodyTypeEnum = "PTR" + ModifyRecordResponseBodyTypeEnumRp ModifyRecordResponseBodyTypeEnum = "RP" + ModifyRecordResponseBodyTypeEnumSpf ModifyRecordResponseBodyTypeEnum = "SPF" + ModifyRecordResponseBodyTypeEnumSrv ModifyRecordResponseBodyTypeEnum = "SRV" + ModifyRecordResponseBodyTypeEnumTxt ModifyRecordResponseBodyTypeEnum = "TXT" + ModifyRecordResponseBodyTypeEnumUrl ModifyRecordResponseBodyTypeEnum = "URL" +) +type ModifyRecordResponseBodyStateEnum string + +// List of State +const ( + ModifyRecordResponseBodyStateEnumDisabled ModifyRecordResponseBodyStateEnum = "DISABLED" + ModifyRecordResponseBodyStateEnumEnabled ModifyRecordResponseBodyStateEnum = "ENABLED" +) + +type ModifyRecordResponseBody struct { + + // 主机头 + Rr string `json:"rr,omitempty"` + + // 修改时间 + ModifiedTime string `json:"modifiedTime,omitempty"` + + // 线路中文名 + LineZh string `json:"lineZh,omitempty"` + + // 备注 + Description string `json:"description,omitempty"` + + // 线路ID + LineId string `json:"lineId,omitempty"` + + // 权重值 + Weight *int32 `json:"weight,omitempty"` + + // MX优先级 + MxPri *int32 `json:"mxPri,omitempty"` + + // 记录类型 + Type ModifyRecordResponseBodyTypeEnum `json:"type,omitempty"` + + // 缓存的生命周期 + Ttl *int32 `json:"ttl,omitempty"` + + // 解析记录ID + RecordId string `json:"recordId,omitempty"` + + // 域名名称 + DomainName string `json:"domainName,omitempty"` + + // 线路英文名 + LineEn string `json:"lineEn,omitempty"` + + // 状态 + State ModifyRecordResponseBodyStateEnum `json:"state,omitempty"` + + // 记录值 + Value string `json:"value,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_client.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_client.go new file mode 100644 index 00000000..8f73b499 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_client.go @@ -0,0 +1,507 @@ +package ecloudsdkcore + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +var ( + jsonCheck = regexp.MustCompile("(?i:(?:application|text)/json)") + xmlCheck = regexp.MustCompile("(?i:(?:application|text)/xml)") +) + +// APIClient manages communication +// In most cases there should be only one, shared, APIClient. +type APIClient struct { + cfg *Configuration + common service +} + +type service struct { + client *APIClient +} + +type HttpRequestPosition string + +const ( + BODY HttpRequestPosition = "Body" + QUERY HttpRequestPosition = "Query" + PATH HttpRequestPosition = "Path" + HEADER HttpRequestPosition = "Header" +) + +const SdkPortalUrl = "/op-apim-portal/apim/request/sdk" +const SdkPortalGatewayUrl = "/api/query/openapi/apim/request/sdk" + +// NewAPIClient creates a new API client. +func NewAPIClient() *APIClient { + cfg := NewConfiguration() + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + c := &APIClient{} + c.cfg = cfg + c.common.client = c + return c +} + +// atoi string to int +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { + if len(contentTypes) == 0 { + return "" + } + if contains(contentTypes, "application/json") { + return "application/json" + } + return contentTypes[0] +} + +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { + if len(accepts) == 0 { + return "" + } + + if contains(accepts, "application/json") { + return "application/json" + } + + return strings.Join(accepts, ",") +} + +// contains is a case insenstive match, finding needle in a haystack +func contains(haystack []string, needle string) bool { + for _, a := range haystack { + if strings.ToLower(a) == strings.ToLower(needle) { + return true + } + } + return false +} + +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + if obj == nil { + return nil + } + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +// parameterToString convert interface{} parameters to string, using a delimiter if format is provided. +func parameterToString(obj interface{}, collectionFormat string, request HttpRequest) (*http.Request, string) { + var delimiter string + + switch collectionFormat { + case "pipes": + delimiter = "|" + case "ssv": + delimiter = " " + case "tsv": + delimiter = "\t" + case "csv": + delimiter = "," + } + + if reflect.TypeOf(obj).Kind() == reflect.Slice { + return nil, strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") + } + + return nil, fmt.Sprintf("%v", obj) +} + +// Excute entry for http call +func (c *APIClient) Excute(httpRequest *HttpRequest, config *config.Config, returnType interface{}) (*http.Response, error) { + httpRequest = buildHttpRequest(httpRequest, config) + request := buildCall(httpRequest) + httpResponse, err := c.callAPI(request) + if err != nil || httpResponse == nil { + return nil, err + } + + responseBody, err := ioutil.ReadAll(httpResponse.Body) + httpResponse.Body.Close() + if err != nil { + return httpResponse, err + } + + if httpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = c.decode(&returnType, responseBody, httpResponse.Header.Get("Content-Type")) + if err != nil { + return httpResponse, fmt.Errorf("%w, response body is: %s", err, string(responseBody)) + } + return httpResponse, nil + } + + if httpResponse.StatusCode >= 300 { + newErr := GenericResponseError{ + body: responseBody, + error: httpResponse.Status, + } + return httpResponse, newErr + } + return httpResponse, err +} + +// callAPI do the request. +func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { + return c.cfg.HTTPClient.Do(request) +} + +// ChangeBasePath Change base path to allow switching to mocks +func (c *APIClient) ChangeBasePath(path string) { + c.cfg.BasePath = path +} + +// buildHttpRequest build the request +func buildHttpRequest(httpRequest *HttpRequest, config *config.Config) *HttpRequest { + openApiRequest := &OpenApiRequest{ + AccessKey: config.AccessKey, + SecretKey: config.SecretKey, + PoolId: config.PoolId, + Api: httpRequest.Action, + Product: httpRequest.Product, + Version: httpRequest.Version, + SdkVersion: httpRequest.SdkVersion, + Language: "Golang", + } + if httpRequest.Body != nil { + reqType := reflect.TypeOf(httpRequest.Body) + if reqType.Kind() == reflect.Ptr { + reqType = reqType.Elem() + } + v := reflect.ValueOf(httpRequest.Body) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + var flag = false + for i := 0; i < reqType.NumField(); i++ { + fieldType := reqType.Field(i) + value := v.FieldByName(fieldType.Name) + if value.Kind() == reflect.Ptr { + if value.IsNil() { + continue + } + value = value.Elem() + + } + propertyType := fieldType.Type + if propertyType.Kind() == reflect.Ptr { + propertyType = propertyType.Elem() + } + + _, flag = propertyType.FieldByName(string(BODY)) + if flag { + openApiRequest.BodyParameter = value.Interface() + continue + } + _, flag = propertyType.FieldByName(string(HEADER)) + if flag { + openApiRequest.HeaderParameter = structToMap(value.Interface()) + continue + } + _, flag = propertyType.FieldByName(string(QUERY)) + if flag { + openApiRequest.QueryParameter = structToMap(value.Interface()) + continue + } + _, flag = propertyType.FieldByName(string(PATH)) + if flag { + openApiRequest.PathParameter = structToMap(value.Interface()) + continue + } + } + } + headers := make(map[string]interface{}) + if httpRequest.HeaderParams != nil { + if openApiRequest.HeaderParameter == nil { + headers = httpRequest.HeaderParams + } else { + headers = mergeMap(openApiRequest.HeaderParameter, httpRequest.HeaderParams) + } + openApiRequest.HeaderParameter = headers + } + httpRequest.Body = openApiRequest + return httpRequest +} + +// mergeMap merge the two map results +func mergeMap(mObj ...map[string]interface{}) map[string]interface{} { + newMap := map[string]interface{}{} + for _, m := range mObj { + for k, v := range m { + newMap[k] = v + } + } + return newMap +} + +// structToMap struct convert to map +func structToMap(value interface{}) map[string]interface{} { + data, _ := json.Marshal(value) + result := make(map[string]interface{}) + json.Unmarshal(data, &result) + return result +} + +func buildCall(httpRequest *HttpRequest) (request *http.Request) { + var url = "" + if len(httpRequest.Url) > 0 { + url = httpRequest.Url + SdkPortalUrl + } else { + url = httpRequest.DefaultUrl + SdkPortalGatewayUrl + } + request, _ = prepareRequest(url, "POST", httpRequest.Body) + return request +} + +// prepareRequest build the request +func prepareRequest(path string, method string, + postBody interface{}, +) (httpRequest *http.Request, err error) { + + var body *bytes.Buffer + + // Detect postBody type and post. + if postBody != nil { + var contentType = detectContentType(postBody) + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } + } + + // Setup path and query parameters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Generate a new request + if body != nil { + httpRequest, err = http.NewRequest(method, url.String(), body) + } else { + httpRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err + } + + // add default header parameters + httpRequest.Header.Add("Content-Type", "application/json") + return httpRequest, nil +} + +func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { + if strings.Contains(contentType, "application/xml") { + if err = xml.Unmarshal(b, v); err != nil { + return err + } + return nil + } else if strings.Contains(contentType, "application/json") { + platformResponse := &APIPlatformResponse{} + if err = json.Unmarshal(b, platformResponse); err != nil { + newErr := GenericResponseError{ + body: b, + error: err.Error(), + } + return newErr + } + platformResponseBodyBytes, _ := json.Marshal(platformResponse.Body) + platformResponseBody := &APIPlatformResponseBody{} + if err = json.Unmarshal(platformResponseBodyBytes, platformResponseBody); err != nil { + return err + } + /* + 找到两层指针指向的元素 + */ + value := reflect.ValueOf(v).Elem().Elem() + + if !value.IsNil() { + structValue := value.Elem() + if structValue.NumField() == 1 && structValue.Field(0).Kind() == reflect.String { + n := len(platformResponseBody.ResponseBody) + structValue.Field(0).SetString(platformResponseBody.ResponseBody[1 : n-1]) + return nil + } + } + + if err = json.Unmarshal([]byte(platformResponseBody.ResponseBody), v); err != nil { + return err + } + return nil + } + return errors.New("undefined response type") +} + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) error { + return fmt.Errorf(format, a...) +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if s, ok := body.(*string); ok { + _, err = bodyBuf.WriteString(*s) + } else if jsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if xmlCheck.MatchString(contentType) { + xml.NewEncoder(bodyBuf).Encode(body) + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("Invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) time.Time { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } + expires = now.Add(lifetime) + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) int { + return utf8.RuneCountInString(s) +} + +// GenericResponseError Provides access to the body, error and model on returned errors. +type GenericResponseError struct { + body []byte + error string + model interface{} +} + +// Error returns non-empty string if there was an error. +func (e GenericResponseError) Error() string { + return e.error +} + +// Body returns the raw bytes of the response +func (e GenericResponseError) Body() []byte { + return e.body +} + +// Model returns the unpacked model of the error +func (e GenericResponseError) Model() interface{} { + return e.model +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_response.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_response.go new file mode 100644 index 00000000..89110877 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/api_response.go @@ -0,0 +1,64 @@ +package ecloudsdkcore + +import ( + "net/http" +) + +type ReturnState string + +const ( + OK ReturnState = "OK" + ERROR ReturnState = "ERROR" + EXCEPTION ReturnState = "EXCEPTION" + ALARM ReturnState = "ALARM" + FORBIDDEN ReturnState = "FORBIDDEN" +) + +type APIResponse struct { + *http.Response `json:"-"` + Message string `json:"message,omitempty"` + // Operation is the name of the swagger operation. + Operation string `json:"operation,omitempty"` + // RequestURL is the request URL. This value is always available, even if the + // embedded *http.Response is nil. + RequestURL string `json:"url,omitempty"` + // Method is the HTTP method used for the request. This value is always + // available, even if the embedded *http.Response is nil. + Method string `json:"method,omitempty"` + // Payload holds the contents of the response body (which may be nil or empty). + // This is provided here as the raw response.Body() reader will have already + // been drained. + Payload []byte `json:"-"` +} + +type APIPlatformResponse struct { + RequestId string `json:"requestId,omitempty"` + State ReturnState `json:"state,omitempty"` + Body interface{} `json:"body,omitempty"` + ErrorCode string `json:"errorCode,omitempty"` + ErrorParams []string `json:"errorParams,omitempty"` + ErrorMessage string `json:"errorMessage,omitempty"` +} + +type APIPlatformResponseBody struct { + // TimeConsuming int64 `json:"timeConsuming,omitempty"` + ResponseBody string `json:"responseBody,omitempty"` + RequestHeader map[string]interface{} `json:"requestHeader,omitempty"` + ResponseHeader map[string]interface{} `json:"responseHeader,omitempty"` + ResponseMessage string `json:"responseMessage,omitempty"` + StatusCode int `json:"statusCode,omitempty"` + HttpMethod string `json:"httpMethod,omitempty"` + RequestUrl string `json:"requestUrl,omitempty"` +} + +func NewAPIResponse(r *http.Response) *APIResponse { + + response := &APIResponse{Response: r} + return response +} + +func NewAPIResponseWithError(errorMessage string) *APIResponse { + + response := &APIResponse{Message: errorMessage} + return response +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/config/config.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/config/config.go new file mode 100644 index 00000000..08ba59e3 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/config/config.go @@ -0,0 +1,9 @@ +package config + +type Config struct { + AccessKey string `json:"accessKey,string"` + SecretKey string `json:"secretKey,string"` + PoolId string `json:"poolId,string"` + ReadTimeOut int `json:"readTimeOut,int"` + ConnectTimeout int `json:"connectTimeout,int"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/configuration.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/configuration.go new file mode 100644 index 00000000..8eeb5df0 --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/configuration.go @@ -0,0 +1,32 @@ +package ecloudsdkcore + +import ( + "net/http" +) + +type APIKey struct { + Key string + Prefix string +} + +type Configuration struct { + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client +} + +func NewConfiguration() *Configuration { + cfg := &Configuration{ + BasePath: "https://ecloud.10086.cn/", + DefaultHeader: make(map[string]string), + UserAgent: "Ecloud-SDK/1.0.0/go", + } + return cfg +} + +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/go.mod b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/go.mod new file mode 100644 index 00000000..141fa34e --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/go.mod @@ -0,0 +1,3 @@ +module gitlab.ecloud.com/ecloud/ecloudsdkcore + +go 1.23.0 diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/http_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/http_request.go new file mode 100644 index 00000000..e498463d --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/http_request.go @@ -0,0 +1,22 @@ +package ecloudsdkcore + +type HttpRequest struct { + Url string `json:"url,omitempty"` + DefaultUrl string `json:"defaultUrl,omitempty"` + Method string `json:"method,omitempty"` + Action string `json:"action,omitempty"` + Product string `json:"product,omitempty"` + Version string `json:"version,omitempty"` + SdkVersion string `json:"sdkVersion,omitempty"` + Body interface{} `json:"body,omitempty"` + PathParams map[string]interface{} `json:"pathParams,omitempty"` + QueryParams map[string]interface{} `json:"queryParams,omitempty"` + HeaderParams map[string]interface{} `json:"headerParams,omitempty"` +} + +func NewDefaultHttpRequest() *HttpRequest { + return &HttpRequest{ + DefaultUrl: "https://ecloud.10086.cn", + Method: "POST", + } +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/open_api_request.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/open_api_request.go new file mode 100644 index 00000000..a7cb4c0b --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/open_api_request.go @@ -0,0 +1,16 @@ +package ecloudsdkcore + +type OpenApiRequest struct { + Product string `json:"product,omitempty"` + Version string `json:"version,omitempty"` + SdkVersion string `json:"sdkVersion,omitempty"` + Language string `json:"language,omitempty"` + Api string `json:"api,omitempty"` + PoolId string `json:"poolId,omitempty"` + HeaderParameter map[string]interface{} `json:"headerParameter,omitempty"` + PathParameter map[string]interface{} `json:"pathParameter,omitempty"` + QueryParameter map[string]interface{} `json:"queryParameter,omitempty"` + BodyParameter interface{} `json:"bodyParameter,omitempty"` + AccessKey string `json:"accessKey,omitempty"` + SecretKey string `json:"secretKey,omitempty"` +} diff --git a/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/position/http_position.go b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/position/http_position.go new file mode 100644 index 00000000..7c2eec1c --- /dev/null +++ b/internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0/position/http_position.go @@ -0,0 +1,13 @@ +package position + +type Body struct { +} + +type Query struct { +} + +type Path struct { +} + +type Header struct { +} diff --git a/internal/pkg/vendors/dnsla-sdk/api.go b/internal/pkg/vendors/dnsla-sdk/api.go new file mode 100644 index 00000000..df8e6026 --- /dev/null +++ b/internal/pkg/vendors/dnsla-sdk/api.go @@ -0,0 +1,52 @@ +package dnslasdk + +import ( + "fmt" + "net/http" + "net/url" +) + +func (c *Client) ListDomains(req *ListDomainsRequest) (*ListDomainsResponse, error) { + resp := ListDomainsResponse{} + err := c.sendRequestWithResult(http.MethodGet, "/domainList", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *Client) ListRecords(req *ListRecordsRequest) (*ListRecordsResponse, error) { + resp := ListRecordsResponse{} + err := c.sendRequestWithResult(http.MethodGet, "/recordList", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *Client) CreateRecord(req *CreateRecordRequest) (*CreateRecordResponse, error) { + resp := CreateRecordResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/record", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *Client) UpdateRecord(req *UpdateRecordRequest) (*UpdateRecordResponse, error) { + resp := UpdateRecordResponse{} + err := c.sendRequestWithResult(http.MethodPut, "/record", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *Client) DeleteRecord(req *DeleteRecordRequest) (*DeleteRecordResponse, error) { + resp := DeleteRecordResponse{} + err := c.sendRequestWithResult(http.MethodDelete, fmt.Sprintf("/record?id=%s", url.QueryEscape(req.Id)), req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/internal/pkg/vendors/dnsla-sdk/client.go b/internal/pkg/vendors/dnsla-sdk/client.go new file mode 100644 index 00000000..60430fa1 --- /dev/null +++ b/internal/pkg/vendors/dnsla-sdk/client.go @@ -0,0 +1,82 @@ +package dnslasdk + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + apiId string + apiSecret string + + client *resty.Client +} + +func NewClient(apiId, apiSecret string) *Client { + client := resty.New() + + return &Client{ + apiId: apiId, + apiSecret: apiSecret, + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R().SetBasicAuth(c.apiId, c.apiSecret) + req.Method = method + req.URL = "https://api.dns.la/api" + path + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req.SetQueryParams(qs) + } else { + req = req. + SetHeader("Content-Type", "application/json"). + SetBody(params) + } + + resp, err := req.Send() + if err != nil { + return nil, fmt.Errorf("dnsla api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("dnsla api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("dnsla api error: failed to parse response: %w", err) + } else if errcode := result.GetCode(); errcode/100 != 2 { + return fmt.Errorf("dnsla api error: %d - %s", errcode, result.GetMessage()) + } + + return nil +} diff --git a/internal/pkg/vendors/dnsla-sdk/models.go b/internal/pkg/vendors/dnsla-sdk/models.go new file mode 100644 index 00000000..85fe7978 --- /dev/null +++ b/internal/pkg/vendors/dnsla-sdk/models.go @@ -0,0 +1,125 @@ +package dnslasdk + +type BaseResponse interface { + GetCode() int + GetMessage() string +} + +type baseResponse struct { + Code int `json:"code"` + Message string `json:"message"` +} + +func (r *baseResponse) GetCode() int { + return r.Code +} + +func (r *baseResponse) GetMessage() string { + return r.Message +} + +type DomainInfo struct { + Id string `json:"id"` + GroupId string `json:"groupId"` + GroupName string `json:"groupName"` + Domain string `json:"domain"` + DisplayDomain string `json:"displayDomain"` + CreatedAt int64 `json:"createdAt"` + UpdatedAt int64 `json:"updatedAt"` +} + +type RecordInfo struct { + Id string `json:"id"` + DomainId string `json:"domainId"` + GroupId string `json:"groupId"` + GroupName string `json:"groupName"` + LineId string `json:"lineId"` + LineCode string `json:"lineCode"` + LineName string `json:"lineName"` + Type int32 `json:"type"` + Host string `json:"host"` + DisplayHost string `json:"displayHost"` + Data string `json:"data"` + DisplayData string `json:"displayData"` + Ttl int32 `json:"ttl"` + Weight int32 `json:"weight"` + Preference int32 `json:"preference"` + CreatedAt int64 `json:"createdAt"` + UpdatedAt int64 `json:"updatedAt"` +} + +type ListDomainsRequest struct { + PageIndex int32 `json:"pageIndex"` + PageSize int32 `json:"pageSize"` + GroupId *string `json:"groupId,omitempty"` +} + +type ListDomainsResponse struct { + baseResponse + Data *struct { + Total int32 `json:"total"` + Results []*DomainInfo `json:"results"` + } `json:"data,omitempty"` +} + +type ListRecordsRequest struct { + PageIndex int32 `json:"pageIndex"` + PageSize int32 `json:"pageSize"` + DomainId string `json:"domainId"` + GroupId *string `json:"groupId,omitempty"` + LineId *string `json:"lineId,omitempty"` + Type *int32 `json:"type,omitempty"` + Host *string `json:"host,omitempty"` + Data *string `json:"data,omitempty"` +} + +type ListRecordsResponse struct { + baseResponse + Data *struct { + Total int32 `json:"total"` + Results []*RecordInfo `json:"results"` + } `json:"data,omitempty"` +} + +type CreateRecordRequest struct { + DomainId string `json:"domainId"` + GroupId *string `json:"groupId,omitempty"` + LineId *string `json:"lineId,omitempty"` + Type int32 `json:"type"` + Host string `json:"host"` + Data string `json:"data"` + Ttl int32 `json:"ttl"` + Weight *int32 `json:"weight,omitempty"` + Preference *int32 `json:"preference,omitempty"` +} + +type CreateRecordResponse struct { + baseResponse + Data *struct { + Id string `json:"id"` + } `json:"data,omitempty"` +} + +type UpdateRecordRequest struct { + Id string `json:"id"` + GroupId *string `json:"groupId,omitempty"` + LineId *string `json:"lineId,omitempty"` + Type *int32 `json:"type,omitempty"` + Host *string `json:"host,omitempty"` + Data *string `json:"data,omitempty"` + Ttl *int32 `json:"ttl,omitempty"` + Weight *int32 `json:"weight,omitempty"` + Preference *int32 `json:"preference,omitempty"` +} + +type UpdateRecordResponse struct { + baseResponse +} + +type DeleteRecordRequest struct { + Id string `json:"-"` +} + +type DeleteRecordResponse struct { + baseResponse +} diff --git a/internal/pkg/vendors/edgio-sdk/applications/README.md b/internal/pkg/vendors/edgio-sdk/applications/README.md new file mode 100644 index 00000000..703b245a --- /dev/null +++ b/internal/pkg/vendors/edgio-sdk/applications/README.md @@ -0,0 +1 @@ +From https://github.com/Edgio/terraform-provider-edgio.git diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/README.md b/internal/pkg/vendors/edgio-sdk/applications/v7/README.md deleted file mode 100644 index fae60236..00000000 --- a/internal/pkg/vendors/edgio-sdk/applications/v7/README.md +++ /dev/null @@ -1,3 +0,0 @@ -```shell -git clone https://github.com/Edgio/terraform-provider-edgio.git -``` diff --git a/internal/pkg/vendors/gcore-sdk/common/endpoint.go b/internal/pkg/vendors/gcore-sdk/common/endpoint.go new file mode 100644 index 00000000..d4032da3 --- /dev/null +++ b/internal/pkg/vendors/gcore-sdk/common/endpoint.go @@ -0,0 +1,3 @@ +package common + +const BASE_URL = "https://api.gcore.com" diff --git a/internal/pkg/vendors/gcore-sdk/common/signer.go b/internal/pkg/vendors/gcore-sdk/common/signer.go new file mode 100644 index 00000000..bc66ee09 --- /dev/null +++ b/internal/pkg/vendors/gcore-sdk/common/signer.go @@ -0,0 +1,24 @@ +package common + +import ( + "net/http" + + "github.com/G-Core/gcorelabscdn-go/gcore" +) + +type AuthRequestSigner struct { + apiToken string +} + +var _ gcore.RequestSigner = (*AuthRequestSigner)(nil) + +func NewAuthRequestSigner(apiToken string) *AuthRequestSigner { + return &AuthRequestSigner{ + apiToken: apiToken, + } +} + +func (s *AuthRequestSigner) Sign(req *http.Request) error { + req.Header.Set("Authorization", "APIKey "+s.apiToken) + return nil +} diff --git a/internal/pkg/vendors/gname-sdk/api.go b/internal/pkg/vendors/gname-sdk/api.go index 33972adc..d01574c7 100644 --- a/internal/pkg/vendors/gname-sdk/api.go +++ b/internal/pkg/vendors/gname-sdk/api.go @@ -1,102 +1,37 @@ package gnamesdk -type BaseResponse interface { - GetCode() int - GetMsg() string +func (c *Client) AddDomainResolution(req *AddDomainResolutionRequest) (*AddDomainResolutionResponse, error) { + result := AddDomainResolutionResponse{} + err := c.sendRequestWithResult("/api/resolution/add", req, &result) + if err != nil { + return nil, err + } + return &result, nil } -type AddDomainResolutionRequest struct { - ZoneName string `json:"ym"` - RecordType string `json:"lx"` - RecordName string `json:"zj"` - RecordValue string `json:"jlz"` - MX int `json:"mx"` - TTL int `json:"ttl"` +func (c *Client) ModifyDomainResolution(req *ModifyDomainResolutionRequest) (*ModifyDomainResolutionResponse, error) { + resp := ModifyDomainResolutionResponse{} + err := c.sendRequestWithResult("/api/resolution/edit", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil } -type AddDomainResolutionResponse struct { - Code int `json:"code"` - Msg string `json:"msg"` - Data int `json:"data"` +func (c *Client) DeleteDomainResolution(req *DeleteDomainResolutionRequest) (*DeleteDomainResolutionResponse, error) { + resp := DeleteDomainResolutionResponse{} + err := c.sendRequestWithResult("/api/resolution/delete", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil } -func (r *AddDomainResolutionResponse) GetCode() int { - return r.Code -} - -func (r *AddDomainResolutionResponse) GetMsg() string { - return r.Msg -} - -type ModifyDomainResolutionRequest struct { - ID string `json:"jxid"` - ZoneName string `json:"ym"` - RecordType string `json:"lx"` - RecordName string `json:"zj"` - RecordValue string `json:"jlz"` - MX int `json:"mx"` - TTL int `json:"ttl"` -} - -type ModifyDomainResolutionResponse struct { - Code int `json:"code"` - Msg string `json:"msg"` -} - -func (r *ModifyDomainResolutionResponse) GetCode() int { - return r.Code -} - -func (r *ModifyDomainResolutionResponse) GetMsg() string { - return r.Msg -} - -type DeleteDomainResolutionRequest struct { - ZoneName string `json:"ym"` - RecordID string `json:"jxid"` -} - -type DeleteDomainResolutionResponse struct { - Code int `json:"code"` - Msg string `json:"msg"` -} - -func (r *DeleteDomainResolutionResponse) GetCode() int { - return r.Code -} - -func (r *DeleteDomainResolutionResponse) GetMsg() string { - return r.Msg -} - -type ListDomainResolutionRequest struct { - ZoneName string `json:"ym"` - Page *int `json:"page,omitempty"` - PageSize *int `json:"limit,omitempty"` -} - -type ListDomainResolutionResponse struct { - Code int `json:"code"` - Msg string `json:"msg"` - Count int `json:"count"` - Data []*ResolutionRecord `json:"data"` - Page int `json:"page"` - PageSize int `json:"pagesize"` -} - -type ResolutionRecord struct { - ID string `json:"id"` - ZoneName string `json:"ym"` - RecordType string `json:"lx"` - RecordName string `json:"zjt"` - RecordValue string `json:"jxz"` - MX int `json:"mx"` -} - -func (r *ListDomainResolutionResponse) GetCode() int { - return r.Code -} - -func (r *ListDomainResolutionResponse) GetMsg() string { - return r.Msg +func (c *Client) ListDomainResolution(req *ListDomainResolutionRequest) (*ListDomainResolutionResponse, error) { + resp := ListDomainResolutionResponse{} + err := c.sendRequestWithResult("/api/resolution/list", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil } diff --git a/internal/pkg/vendors/gname-sdk/client.go b/internal/pkg/vendors/gname-sdk/client.go index 64baab95..8e2a2ba9 100644 --- a/internal/pkg/vendors/gname-sdk/client.go +++ b/internal/pkg/vendors/gname-sdk/client.go @@ -10,84 +10,31 @@ import ( "time" "github.com/go-resty/resty/v2" - - "github.com/usual2970/certimate/internal/pkg/utils/maps" ) -type GnameClient struct { +type Client struct { appId string appKey string + client *resty.Client } -func NewGnameClient(appId, appKey string) *GnameClient { +func NewClient(appId, appKey string) *Client { client := resty.New() - return &GnameClient{ + return &Client{ appId: appId, appKey: appKey, client: client, } } -func (c *GnameClient) WithTimeout(timeout time.Duration) *GnameClient { +func (c *Client) WithTimeout(timeout time.Duration) *Client { c.client.SetTimeout(timeout) return c } -func (c *GnameClient) AddDomainResolution(req *AddDomainResolutionRequest) (*AddDomainResolutionResponse, error) { - params := make(map[string]any) - jsonData, _ := json.Marshal(req) - json.Unmarshal(jsonData, ¶ms) - - result := AddDomainResolutionResponse{} - err := c.sendRequestWithResult("/api/resolution/add", params, &result) - if err != nil { - return nil, err - } - return &result, nil -} - -func (c *GnameClient) ModifyDomainResolution(req *ModifyDomainResolutionRequest) (*ModifyDomainResolutionResponse, error) { - params := make(map[string]any) - jsonData, _ := json.Marshal(req) - json.Unmarshal(jsonData, ¶ms) - - result := ModifyDomainResolutionResponse{} - err := c.sendRequestWithResult("/api/resolution/edit", params, &result) - if err != nil { - return nil, err - } - return &result, nil -} - -func (c *GnameClient) DeleteDomainResolution(req *DeleteDomainResolutionRequest) (*DeleteDomainResolutionResponse, error) { - params := make(map[string]any) - jsonData, _ := json.Marshal(req) - json.Unmarshal(jsonData, ¶ms) - - result := DeleteDomainResolutionResponse{} - err := c.sendRequestWithResult("/api/resolution/delete", params, &result) - if err != nil { - return nil, err - } - return &result, nil -} - -func (c *GnameClient) ListDomainResolution(req *ListDomainResolutionRequest) (*ListDomainResolutionResponse, error) { - params := make(map[string]any) - jsonData, _ := json.Marshal(req) - json.Unmarshal(jsonData, ¶ms) - - result := ListDomainResolutionResponse{} - err := c.sendRequestWithResult("/api/resolution/list", params, &result) - if err != nil { - return nil, err - } - return &result, nil -} - -func (c *GnameClient) generateSignature(params map[string]string) string { +func (c *Client) generateSignature(params map[string]string) string { // Step 1: Sort parameters by ASCII order var keys []string for k := range params { @@ -111,14 +58,17 @@ func (c *GnameClient) generateSignature(params map[string]string) string { return strings.ToUpper(fmt.Sprintf("%x", hash)) } -func (c *GnameClient) sendRequest(path string, params map[string]any) (*resty.Response, error) { - if params == nil { - params = make(map[string]any) - } - +func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, error) { data := make(map[string]string) - for k, v := range params { - data[k] = fmt.Sprintf("%v", v) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + data[k] = fmt.Sprintf("%v", v) + } + } } data["appid"] = c.appId data["gntime"] = fmt.Sprintf("%d", time.Now().Unix()) @@ -130,32 +80,24 @@ func (c *GnameClient) sendRequest(path string, params map[string]any) (*resty.Re SetFormData(data) resp, err := req.Post(url) if err != nil { - return nil, fmt.Errorf("gname: failed to send request: %w", err) - } - - if resp.IsError() { - return nil, fmt.Errorf("gname: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return nil, fmt.Errorf("gname api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("gname api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) } return resp, nil } -func (c *GnameClient) sendRequestWithResult(path string, params map[string]any, result BaseResponse) error { +func (c *Client) sendRequestWithResult(path string, params interface{}, result BaseResponse) error { resp, err := c.sendRequest(path, params) if err != nil { return err } - jsonResp := make(map[string]any) - if err := json.Unmarshal(resp.Body(), &jsonResp); err != nil { - return fmt.Errorf("gname: failed to parse response: %w", err) - } - if err := maps.Populate(jsonResp, &result); err != nil { - return fmt.Errorf("gname: failed to parse response: %w", err) - } - - if result.GetCode() != 1 { - return fmt.Errorf("gname api error: %s", result.GetMsg()) + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("gname api error: failed to parse response: %w", err) + } else if errcode := result.GetCode(); errcode != 1 { + return fmt.Errorf("gname api error: %d - %s", errcode, result.GetMessage()) } return nil diff --git a/internal/pkg/vendors/gname-sdk/models.go b/internal/pkg/vendors/gname-sdk/models.go new file mode 100644 index 00000000..997fb2c4 --- /dev/null +++ b/internal/pkg/vendors/gname-sdk/models.go @@ -0,0 +1,79 @@ +package gnamesdk + +type BaseResponse interface { + GetCode() int + GetMessage() string +} + +type baseResponse struct { + Code int `json:"code"` + Message string `json:"msg"` +} + +func (r *baseResponse) GetCode() int { + return r.Code +} + +func (r *baseResponse) GetMessage() string { + return r.Message +} + +type AddDomainResolutionRequest struct { + ZoneName string `json:"ym"` + RecordType string `json:"lx"` + RecordName string `json:"zj"` + RecordValue string `json:"jlz"` + MX int `json:"mx"` + TTL int `json:"ttl"` +} + +type AddDomainResolutionResponse struct { + baseResponse + Data string `json:"data"` +} + +type ModifyDomainResolutionRequest struct { + ID string `json:"jxid"` + ZoneName string `json:"ym"` + RecordType string `json:"lx"` + RecordName string `json:"zj"` + RecordValue string `json:"jlz"` + MX int `json:"mx"` + TTL int `json:"ttl"` +} + +type ModifyDomainResolutionResponse struct { + baseResponse +} + +type DeleteDomainResolutionRequest struct { + ZoneName string `json:"ym"` + RecordID string `json:"jxid"` +} + +type DeleteDomainResolutionResponse struct { + baseResponse +} + +type ListDomainResolutionRequest struct { + ZoneName string `json:"ym"` + Page *int `json:"page,omitempty"` + PageSize *int `json:"limit,omitempty"` +} + +type ListDomainResolutionResponse struct { + baseResponse + Count int `json:"count"` + Data []*ResolutionRecord `json:"data"` + Page int `json:"page"` + PageSize int `json:"pagesize"` +} + +type ResolutionRecord struct { + ID string `json:"id"` + ZoneName string `json:"ym"` + RecordType string `json:"lx"` + RecordName string `json:"zjt"` + RecordValue string `json:"jxz"` + MX int `json:"mx"` +} diff --git a/internal/pkg/vendors/safeline-sdk/api.go b/internal/pkg/vendors/safeline-sdk/api.go new file mode 100644 index 00000000..17af6c83 --- /dev/null +++ b/internal/pkg/vendors/safeline-sdk/api.go @@ -0,0 +1,10 @@ +package safelinesdk + +func (c *Client) UpdateCertificate(req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { + resp := UpdateCertificateResponse{} + err := c.sendRequestWithResult("/api/open/cert", req, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/internal/pkg/vendors/safeline-sdk/client.go b/internal/pkg/vendors/safeline-sdk/client.go new file mode 100644 index 00000000..c6e6caf1 --- /dev/null +++ b/internal/pkg/vendors/safeline-sdk/client.go @@ -0,0 +1,67 @@ +package safelinesdk + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + apiHost string + apiToken string + + client *resty.Client +} + +func NewClient(apiHost, apiToken string) *Client { + client := resty.New() + + return &Client{ + apiHost: strings.TrimRight(apiHost, "/"), + apiToken: apiToken, + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, error) { + url := c.apiHost + path + req := c.client.R(). + SetHeader("Content-Type", "application/json"). + SetHeader("X-SLCE-API-TOKEN", c.apiToken). + SetBody(params) + resp, err := req.Post(url) + if err != nil { + return nil, fmt.Errorf("safeline api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("safeline api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(path, params) + if err != nil { + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("safeline api error: failed to parse response: %w", err) + } else if errcode := result.GetErrCode(); errcode != nil && *errcode != "" { + if result.GetErrMsg() == nil { + return fmt.Errorf("safeline api error: %s", *errcode) + } else { + return fmt.Errorf("safeline api error: %s - %s", *errcode, *result.GetErrMsg()) + } + } + + return nil +} diff --git a/internal/pkg/vendors/safeline-sdk/models.go b/internal/pkg/vendors/safeline-sdk/models.go new file mode 100644 index 00000000..9fbfb7c9 --- /dev/null +++ b/internal/pkg/vendors/safeline-sdk/models.go @@ -0,0 +1,34 @@ +package safelinesdk + +type BaseResponse interface { + GetErrCode() *string + GetErrMsg() *string +} + +type baseResponse struct { + ErrCode *string `json:"err,omitempty"` + ErrMsg *string `json:"msg,omitempty"` +} + +func (r *baseResponse) GetErrCode() *string { + return r.ErrCode +} + +func (r *baseResponse) GetErrMsg() *string { + return r.ErrMsg +} + +type UpdateCertificateRequest struct { + Id int32 `json:"id"` + Type int32 `json:"type"` + Manual *UpdateCertificateRequestBodyManul `json:"manual"` +} + +type UpdateCertificateRequestBodyManul struct { + Crt string `json:"crt"` + Key string `json:"key"` +} + +type UpdateCertificateResponse struct { + baseResponse +} diff --git a/main.go b/main.go index 73d1a2a9..76f7f1c0 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,11 @@ func main() { var flagDir string flag.StringVar(&flagHttp, "http", "127.0.0.1:8090", "HTTP server address") flag.StringVar(&flagDir, "dir", "/pb_data/database", "Pocketbase data directory") + if len(os.Args) < 2 { + slog.Error("[CERTIMATE] missing exec args") + os.Exit(1) + return + } _ = flag.CommandLine.Parse(os.Args[2:]) // skip the first two arguments: "main.go serve" migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{ diff --git a/migrations/1739462400_collections_snapshot.go b/migrations/1739462400_collections_snapshot.go index 523792c7..67453954 100644 --- a/migrations/1739462400_collections_snapshot.go +++ b/migrations/1739462400_collections_snapshot.go @@ -65,6 +65,7 @@ func init() { "cmcccloud", "ctcccloud", "cucccloud", + "dnsla", "dogecloud", "edgio", "fastly", @@ -73,13 +74,16 @@ func init() { "godaddy", "goedge", "huaweicloud", + "jdcloud", "k8s", "local", + "namecheap", "namedotcom", "namesilo", "ns1", "powerdns", "qiniu", + "qingcloud", "rainyun", "safeline", "ssh", @@ -171,6 +175,7 @@ func init() { "cmcccloud", "ctcccloud", "cucccloud", + "dnsla", "dogecloud", "edgio", "fastly", @@ -179,13 +184,16 @@ func init() { "godaddy", "goedge", "huaweicloud", + "jdcloud", "k8s", "local", + "namecheap", "namedotcom", "namesilo", "ns1", "powerdns", "qiniu", + "qingcloud", "rainyun", "safeline", "ssh", diff --git a/ui/public/imgs/acme/letsencrypt.svg b/ui/public/imgs/acme/letsencrypt.svg index 3a6c2312..b13df853 100644 --- a/ui/public/imgs/acme/letsencrypt.svg +++ b/ui/public/imgs/acme/letsencrypt.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6224" width="200" height="200"><path d="M776.416 1024H247.648a41.184 41.184 0 0 1-41.056-41.024V591.808c0-22.56 18.464-41.056 41.056-41.056h49.664v-63.232c0-118.4 96.352-214.688 214.688-214.688s214.688 96.352 214.688 214.688v63.232h49.664c22.56 0 41.056 18.464 41.056 41.056v391.168a41.184 41.184 0 0 1-41.024 41.056z m-237.632-216.416a54.4 54.4 0 0 0-26.688-101.728h-0.128a54.4 54.4 0 0 0-27.072 101.568l0.256 0.128v52.992a26.784 26.784 0 0 0 53.568 0v-52.992z m-118.336-256.832h183.168v-63.232c0-50.464-41.088-91.552-91.552-91.552s-91.552 41.088-91.552 91.552v63.232z m-226.432-58.304H66.432a37.44 37.44 0 1 1 0-74.944h127.584a37.44 37.44 0 1 1 0 74.944z m89.888-200.704h-0.096c-9.024 0-17.312-3.232-23.744-8.576l0.064 0.064-100.896-82.976a37.44 37.44 0 1 1 47.68-57.856l-0.064-0.064 100.896 82.976a37.44 37.44 0 0 1-23.808 66.4h-0.064zM512 203.52a37.44 37.44 0 0 1-37.472-37.472V37.44a37.44 37.44 0 1 1 74.944 0v128.608A37.44 37.44 0 0 1 512 203.52z m228.096 88.224h-0.16a37.44 37.44 0 0 1-23.744-66.336l0.064-0.064 100.896-82.976a37.44 37.44 0 0 1 47.68 57.824l-0.064 0.064-100.896 82.976c-6.4 5.312-14.72 8.544-23.776 8.544z m217.472 200.704h-128.8a37.44 37.44 0 1 1 0-74.944h128.8a37.44 37.44 0 1 1 0 74.944z" fill="#003A70" p-id="6225"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6224" width="200" height="200"><path d="M776.416 1024H247.648a41.184 41.184 0 0 1-41.056-41.024V591.808c0-22.56 18.464-41.056 41.056-41.056h49.664v-63.232c0-118.4 96.352-214.688 214.688-214.688s214.688 96.352 214.688 214.688v63.232h49.664c22.56 0 41.056 18.464 41.056 41.056v391.168a41.184 41.184 0 0 1-41.024 41.056z m-237.632-216.416a54.4 54.4 0 0 0-26.688-101.728h-0.128a54.4 54.4 0 0 0-27.072 101.568l0.256 0.128v52.992a26.784 26.784 0 0 0 53.568 0v-52.992z m-118.336-256.832h183.168v-63.232c0-50.464-41.088-91.552-91.552-91.552s-91.552 41.088-91.552 91.552v63.232z m-226.432-58.304H66.432a37.44 37.44 0 1 1 0-74.944h127.584a37.44 37.44 0 1 1 0 74.944z m89.888-200.704h-0.096c-9.024 0-17.312-3.232-23.744-8.576l0.064 0.064-100.896-82.976a37.44 37.44 0 1 1 47.68-57.856l-0.064-0.064 100.896 82.976a37.44 37.44 0 0 1-23.808 66.4h-0.064zM512 203.52a37.44 37.44 0 0 1-37.472-37.472V37.44a37.44 37.44 0 1 1 74.944 0v128.608A37.44 37.44 0 0 1 512 203.52z m228.096 88.224h-0.16a37.44 37.44 0 0 1-23.744-66.336l0.064-0.064 100.896-82.976a37.44 37.44 0 0 1 47.68 57.824l-0.064 0.064-100.896 82.976c-6.4 5.312-14.72 8.544-23.776 8.544z m217.472 200.704h-128.8a37.44 37.44 0 1 1 0-74.944h128.8a37.44 37.44 0 1 1 0 74.944z" fill="#003A70" p-id="6225"></path></svg> diff --git a/ui/public/imgs/providers/acmehttpreq.svg b/ui/public/imgs/providers/acmehttpreq.svg index 88f2d6b2..936ca077 100644 --- a/ui/public/imgs/providers/acmehttpreq.svg +++ b/ui/public/imgs/providers/acmehttpreq.svg @@ -1,2 +1,2 @@ -<svg class="icon" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" height="200" width="200"> +<svg viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" height="200" width="200"> <circle style="fill:#32BEA6;" cx="256" cy="256" r="256"/><g><path style="fill:#FFFFFF;" d="M58.016,202.296h18.168v42.48h0.296c2.192-3.368,5.128-6.152,8.936-8.2 c3.512-2.056,7.76-3.224,12.304-3.224c12.16,0,24.896,8.064,24.896,30.912v42.04H104.6v-39.992c0-10.4-3.808-18.168-13.776-18.168 c-7.032,0-12.008,4.688-13.912,10.112c-0.584,1.472-0.728,3.368-0.728,5.424v42.624H58.016V202.296z"/><path style="fill:#FFFFFF;" d="M161.76,214.6v20.368h17.144v13.48H161.76v31.496c0,8.64,2.344,13.176,9.224,13.176 c3.08,0,5.424-0.44,7.032-0.872l0.296,13.768c-2.64,1.032-7.328,1.768-13.04,1.768c-6.584,0-12.16-2.2-15.52-5.856 c-3.816-4.112-5.568-10.544-5.568-19.92v-33.544h-10.248V234.96h10.248v-16.12L161.76,214.6z"/><path style="fill:#FFFFFF;" d="M213.192,214.6v20.368h17.144v13.48h-17.144v31.496c0,8.64,2.344,13.176,9.224,13.176 c3.08,0,5.424-0.44,7.032-0.872l0.296,13.768c-2.64,1.032-7.328,1.768-13.04,1.768c-6.584,0-12.16-2.2-15.52-5.856 c-3.816-4.112-5.568-10.544-5.568-19.92v-33.544h-10.248V234.96h10.248v-16.12L213.192,214.6z"/><path style="fill:#FFFFFF;" d="M243.984,258.688c0-9.376-0.296-16.992-0.592-23.728h15.832l0.872,10.984h0.296 c5.264-8.056,13.616-12.6,24.464-12.6c16.408,0,30.024,14.064,30.024,36.328c0,25.784-16.256,38.232-32.512,38.232 c-8.936,0-16.408-3.808-20.072-9.512H262v36.904h-18.016V258.688z M262,276.416c0,1.76,0.144,3.368,0.584,4.976 c1.76,7.328,8.2,12.6,15.824,12.6c11.424,0,18.168-9.52,18.168-23.584c0-12.592-6.16-22.848-17.728-22.848 c-7.472,0-14.36,5.424-16.112,13.336c-0.448,1.464-0.736,3.072-0.736,4.536L262,276.416L262,276.416z"/><path style="fill:#FFFFFF;" d="M327.504,247.12c0-6.744,4.688-11.568,11.136-11.568c6.592,0,10.984,4.832,11.136,11.568 c0,6.592-4.392,11.432-11.136,11.432C332.048,258.552,327.504,253.712,327.504,247.12z M327.504,296.488 c0-6.744,4.688-11.576,11.136-11.576c6.592,0,10.984,4.688,11.136,11.576c0,6.448-4.392,11.424-11.136,11.424 C332.048,307.912,327.504,302.936,327.504,296.488z"/><path style="fill:#FFFFFF;" d="M355.8,312.16l35.744-106.2h12.6l-35.752,106.2H355.8z"/><path style="fill:#FFFFFF;" d="M405.176,312.16l35.744-106.2h12.592l-35.728,106.2H405.176z"/></g></svg> diff --git a/ui/public/imgs/providers/aliyun.svg b/ui/public/imgs/providers/aliyun.svg index 7d0b70e0..05bfc6fe 100644 --- a/ui/public/imgs/providers/aliyun.svg +++ b/ui/public/imgs/providers/aliyun.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5262" width="200" height="200"><path d="M512 64a448 448 0 1 1 0 896A448 448 0 0 1 512 64z" fill="#FF6A00" p-id="5263"></path><path d="M324.8 602.624a26.752 26.752 0 0 1-21.312-25.92v-142.72a27.712 27.712 0 0 1 21.376-25.984l132.416-28.672 13.952-56.896H317.312a97.6 97.6 0 0 0-98.24 96.96v169.344c0.384 54.08 44.16 97.856 98.24 98.176h153.92l-13.888-56.512-132.544-27.776zM710.4 322.432c54.016 0.128 97.92 43.584 98.56 97.6v170.176a98.368 98.368 0 0 1-98.56 98.048H555.328l14.08-56.832 132.608-28.736a27.84 27.84 0 0 0 21.376-25.92v-142.72a26.88 26.88 0 0 0-21.376-25.984l-132.544-28.8-14.08-56.832zM570.368 497.92v13.952H457.28v-13.952h113.088z" fill="#FFFFFF" p-id="5264"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5262" width="200" height="200"><path d="M512 64a448 448 0 1 1 0 896A448 448 0 0 1 512 64z" fill="#FF6A00" p-id="5263"></path><path d="M324.8 602.624a26.752 26.752 0 0 1-21.312-25.92v-142.72a27.712 27.712 0 0 1 21.376-25.984l132.416-28.672 13.952-56.896H317.312a97.6 97.6 0 0 0-98.24 96.96v169.344c0.384 54.08 44.16 97.856 98.24 98.176h153.92l-13.888-56.512-132.544-27.776zM710.4 322.432c54.016 0.128 97.92 43.584 98.56 97.6v170.176a98.368 98.368 0 0 1-98.56 98.048H555.328l14.08-56.832 132.608-28.736a27.84 27.84 0 0 0 21.376-25.92v-142.72a26.88 26.88 0 0 0-21.376-25.984l-132.544-28.8-14.08-56.832zM570.368 497.92v13.952H457.28v-13.952h113.088z" fill="#FFFFFF" p-id="5264"></path></svg> diff --git a/ui/public/imgs/providers/aws.svg b/ui/public/imgs/providers/aws.svg index 9f211f19..3b771872 100644 --- a/ui/public/imgs/providers/aws.svg +++ b/ui/public/imgs/providers/aws.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1710 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4277" width="200" height="200"><path d="M486.118681 373.591209c0 20.817582 2.250549 37.696703 6.189011 50.074725 4.501099 12.378022 10.127473 25.881319 18.004396 40.50989 2.813187 4.501099 3.938462 9.002198 3.938461 12.94066 0 5.626374-3.375824 11.252747-10.690109 16.87912L468.114286 517.626374c-5.063736 3.375824-10.127473 5.063736-14.628572 5.063736-5.626374 0-11.252747-2.813187-16.879121-7.876923-7.876923-8.43956-14.628571-17.441758-20.254945-26.443956-5.626374-9.564835-11.252747-20.254945-17.441758-33.195605-43.885714 51.762637-99.024176 77.643956-165.415385 77.643956-47.261538 0-84.958242-13.503297-112.527472-40.50989-27.569231-27.006593-41.635165-63.015385-41.635165-108.026373 0-47.824176 16.879121-86.646154 51.2-115.903297 34.320879-29.257143 79.894505-43.885714 137.846154-43.885714 19.12967 0 38.821978 1.687912 59.63956 4.501099 20.817582 2.813187 42.197802 7.314286 64.703297 12.378022v-41.072528c0-42.76044-9.002198-72.58022-26.443956-90.021978-18.004396-17.441758-48.386813-25.881319-91.70989-25.881319-19.692308 0-39.947253 2.250549-60.764835 7.314286-20.817582 5.063736-41.072527 11.252747-60.764835 19.12967-9.002198 3.938462-15.753846 6.189011-19.692308 7.314286-3.938462 1.125275-6.751648 1.687912-9.002198 1.687912-7.876923 0-11.815385-5.626374-11.815384-17.441758v-27.569231c0-9.002198 1.125275-15.753846 3.938461-19.692307 2.813187-3.938462 7.876923-7.876923 15.753846-11.815385 19.692308-10.127473 43.323077-18.567033 70.892308-25.318681C230.681319 10.69011 259.938462 7.314286 290.883516 7.314286c66.953846 0 115.903297 15.191209 147.410989 45.573626 30.945055 30.382418 46.698901 76.518681 46.698902 138.408791v182.294506zM257.687912 459.112088c18.567033 0 37.696703-3.375824 57.951648-10.127473 20.254945-6.751648 38.259341-19.12967 53.45055-36.008791 9.002198-10.69011 15.753846-22.505495 19.12967-36.008791 3.375824-13.503297 5.626374-29.81978 5.626374-48.949451v-23.630769c-16.316484-3.938462-33.758242-7.314286-51.762638-9.564835-18.004396-2.250549-35.446154-3.375824-52.887912-3.375824-37.696703 0-65.265934 7.314286-83.832967 22.505494-18.567033 15.191209-27.569231 36.571429-27.56923 64.703297 0 26.443956 6.751648 46.136264 20.817582 59.63956 13.503297 14.065934 33.195604 20.817582 59.076923 20.817583z m451.797802 60.764835c-10.127473 0-16.879121-1.687912-21.380219-5.626374-4.501099-3.375824-8.43956-11.252747-11.815385-21.942857L544.07033 57.389011c-3.375824-11.252747-5.063736-18.567033-5.063737-22.505495 0-9.002198 4.501099-14.065934 13.503297-14.065934h55.138462c10.69011 0 18.004396 1.687912 21.942857 5.626374 4.501099 3.375824 7.876923 11.252747 11.252747 21.942857l94.523077 372.465934 87.771429-372.465934c2.813187-11.252747 6.189011-18.567033 10.690109-21.942857 4.501099-3.375824 12.378022-5.626374 22.505495-5.626374h45.010989c10.69011 0 18.004396 1.687912 22.505494 5.626374 4.501099 3.375824 8.43956 11.252747 10.69011 21.942857l88.896704 376.967033 97.336263-376.967033c3.375824-11.252747 7.314286-18.567033 11.252748-21.942857 4.501099-3.375824 11.815385-5.626374 21.942857-5.626374h52.325274c9.002198 0 14.065934 4.501099 14.065935 14.065934 0 2.813187-0.562637 5.626374-1.125275 9.002198-0.562637 3.375824-1.687912 7.876923-3.938462 14.065934l-135.595604 434.918682c-3.375824 11.252747-7.314286 18.567033-11.815385 21.942857-4.501099 3.375824-11.815385 5.626374-21.380219 5.626373h-48.386814c-10.69011 0-18.004396-1.687912-22.505494-5.626373-4.501099-3.938462-8.43956-11.252747-10.69011-22.505495L877.714286 129.406593l-86.646154 362.338462c-2.813187 11.252747-6.189011 18.567033-10.69011 22.505494-4.501099 3.938462-12.378022 5.626374-22.505495 5.626374h-48.386813z m722.989011 15.191209c-29.257143 0-58.514286-3.375824-86.646154-10.127473-28.131868-6.751648-50.074725-14.065934-64.703296-22.505494-9.002198-5.063736-15.191209-10.69011-17.441759-15.753846-2.250549-5.063736-3.375824-10.69011-3.375824-15.753846v-28.694506c0-11.815385 4.501099-17.441758 12.94066-17.441758 3.375824 0 6.751648 0.562637 10.127472 1.687912 3.375824 1.125275 8.43956 3.375824 14.065934 5.626374 19.12967 8.43956 39.947253 15.191209 61.89011 19.692307 22.505495 4.501099 44.448352 6.751648 66.953846 6.751649 35.446154 0 63.015385-6.189011 82.145055-18.567033 19.12967-12.378022 29.257143-30.382418 29.257143-53.45055 0-15.753846-5.063736-28.694505-15.191209-39.384615-10.127473-10.69011-29.257143-20.254945-56.826373-29.257143L1384.087912 292.571429c-41.072527-12.940659-71.454945-32.07033-90.021978-57.389011-18.567033-24.756044-28.131868-52.325275-28.131868-81.582418 0-23.630769 5.063736-44.448352 15.191209-62.452747 10.127473-18.004396 23.630769-33.758242 40.50989-46.136264 16.879121-12.940659 36.008791-22.505495 58.514286-29.257143 22.505495-6.751648 46.136264-9.564835 70.892307-9.564835 12.378022 0 25.318681 0.562637 37.696704 2.250549 12.940659 1.687912 24.756044 3.938462 36.571428 6.189011 11.252747 2.813187 21.942857 5.626374 32.07033 9.002198 10.127473 3.375824 18.004396 6.751648 23.630769 10.127473 7.876923 4.501099 13.503297 9.002198 16.879121 14.065934 3.375824 4.501099 5.063736 10.69011 5.063736 18.567033v26.443956c0 11.815385-4.501099 18.004396-12.940659 18.004395-4.501099 0-11.815385-2.250549-21.38022-6.751648-32.07033-14.628571-68.079121-21.942857-108.026374-21.942857-32.07033 0-57.389011 5.063736-74.830769 15.753846-17.441758 10.69011-26.443956 27.006593-26.443956 50.074725 0 15.753846 5.626374 29.257143 16.879121 39.947253 11.252747 10.69011 32.07033 21.38022 61.89011 30.945055l79.894505 25.318681c40.50989 12.940659 69.767033 30.945055 87.208792 54.013187 17.441758 23.068132 25.881319 49.512088 25.881318 78.769231 0 24.193407-5.063736 46.136264-14.628571 65.265934-10.127473 19.12967-23.630769 36.008791-41.072528 49.512088-17.441758 14.065934-38.259341 24.193407-62.452747 31.507692-25.318681 7.876923-51.762637 11.815385-80.457143 11.815385z" fill="#252F3E" p-id="4278"></path><path d="M1538.813187 808.50989c-185.107692 136.720879-454.048352 209.301099-685.292308 209.301099-324.079121 0-616.087912-119.841758-836.641758-319.015385-17.441758-15.753846-1.687912-37.134066 19.12967-24.756044 238.558242 138.408791 532.817582 222.241758 837.204396 222.241759 205.362637 0 430.98022-42.76044 638.593406-130.531868 30.945055-14.065934 57.389011 20.254945 27.006594 42.760439z" fill="#FF9900" p-id="4279"></path><path d="M1615.894505 720.738462c-23.630769-30.382418-156.413187-14.628571-216.615384-7.314286-18.004396 2.250549-20.817582-13.503297-4.501099-25.318681 105.775824-74.268132 279.630769-52.887912 299.885714-28.131869 20.254945 25.318681-5.626374 199.173626-104.650549 282.443956-15.191209 12.940659-29.81978 6.189011-23.068132-10.690109 22.505495-55.701099 72.58022-181.169231 48.94945-210.989011z" fill="#FF9900" p-id="4280"></path></svg> +<svg viewBox="0 0 1710 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4277" width="200" height="200"><path d="M486.118681 373.591209c0 20.817582 2.250549 37.696703 6.189011 50.074725 4.501099 12.378022 10.127473 25.881319 18.004396 40.50989 2.813187 4.501099 3.938462 9.002198 3.938461 12.94066 0 5.626374-3.375824 11.252747-10.690109 16.87912L468.114286 517.626374c-5.063736 3.375824-10.127473 5.063736-14.628572 5.063736-5.626374 0-11.252747-2.813187-16.879121-7.876923-7.876923-8.43956-14.628571-17.441758-20.254945-26.443956-5.626374-9.564835-11.252747-20.254945-17.441758-33.195605-43.885714 51.762637-99.024176 77.643956-165.415385 77.643956-47.261538 0-84.958242-13.503297-112.527472-40.50989-27.569231-27.006593-41.635165-63.015385-41.635165-108.026373 0-47.824176 16.879121-86.646154 51.2-115.903297 34.320879-29.257143 79.894505-43.885714 137.846154-43.885714 19.12967 0 38.821978 1.687912 59.63956 4.501099 20.817582 2.813187 42.197802 7.314286 64.703297 12.378022v-41.072528c0-42.76044-9.002198-72.58022-26.443956-90.021978-18.004396-17.441758-48.386813-25.881319-91.70989-25.881319-19.692308 0-39.947253 2.250549-60.764835 7.314286-20.817582 5.063736-41.072527 11.252747-60.764835 19.12967-9.002198 3.938462-15.753846 6.189011-19.692308 7.314286-3.938462 1.125275-6.751648 1.687912-9.002198 1.687912-7.876923 0-11.815385-5.626374-11.815384-17.441758v-27.569231c0-9.002198 1.125275-15.753846 3.938461-19.692307 2.813187-3.938462 7.876923-7.876923 15.753846-11.815385 19.692308-10.127473 43.323077-18.567033 70.892308-25.318681C230.681319 10.69011 259.938462 7.314286 290.883516 7.314286c66.953846 0 115.903297 15.191209 147.410989 45.573626 30.945055 30.382418 46.698901 76.518681 46.698902 138.408791v182.294506zM257.687912 459.112088c18.567033 0 37.696703-3.375824 57.951648-10.127473 20.254945-6.751648 38.259341-19.12967 53.45055-36.008791 9.002198-10.69011 15.753846-22.505495 19.12967-36.008791 3.375824-13.503297 5.626374-29.81978 5.626374-48.949451v-23.630769c-16.316484-3.938462-33.758242-7.314286-51.762638-9.564835-18.004396-2.250549-35.446154-3.375824-52.887912-3.375824-37.696703 0-65.265934 7.314286-83.832967 22.505494-18.567033 15.191209-27.569231 36.571429-27.56923 64.703297 0 26.443956 6.751648 46.136264 20.817582 59.63956 13.503297 14.065934 33.195604 20.817582 59.076923 20.817583z m451.797802 60.764835c-10.127473 0-16.879121-1.687912-21.380219-5.626374-4.501099-3.375824-8.43956-11.252747-11.815385-21.942857L544.07033 57.389011c-3.375824-11.252747-5.063736-18.567033-5.063737-22.505495 0-9.002198 4.501099-14.065934 13.503297-14.065934h55.138462c10.69011 0 18.004396 1.687912 21.942857 5.626374 4.501099 3.375824 7.876923 11.252747 11.252747 21.942857l94.523077 372.465934 87.771429-372.465934c2.813187-11.252747 6.189011-18.567033 10.690109-21.942857 4.501099-3.375824 12.378022-5.626374 22.505495-5.626374h45.010989c10.69011 0 18.004396 1.687912 22.505494 5.626374 4.501099 3.375824 8.43956 11.252747 10.69011 21.942857l88.896704 376.967033 97.336263-376.967033c3.375824-11.252747 7.314286-18.567033 11.252748-21.942857 4.501099-3.375824 11.815385-5.626374 21.942857-5.626374h52.325274c9.002198 0 14.065934 4.501099 14.065935 14.065934 0 2.813187-0.562637 5.626374-1.125275 9.002198-0.562637 3.375824-1.687912 7.876923-3.938462 14.065934l-135.595604 434.918682c-3.375824 11.252747-7.314286 18.567033-11.815385 21.942857-4.501099 3.375824-11.815385 5.626374-21.380219 5.626373h-48.386814c-10.69011 0-18.004396-1.687912-22.505494-5.626373-4.501099-3.938462-8.43956-11.252747-10.69011-22.505495L877.714286 129.406593l-86.646154 362.338462c-2.813187 11.252747-6.189011 18.567033-10.69011 22.505494-4.501099 3.938462-12.378022 5.626374-22.505495 5.626374h-48.386813z m722.989011 15.191209c-29.257143 0-58.514286-3.375824-86.646154-10.127473-28.131868-6.751648-50.074725-14.065934-64.703296-22.505494-9.002198-5.063736-15.191209-10.69011-17.441759-15.753846-2.250549-5.063736-3.375824-10.69011-3.375824-15.753846v-28.694506c0-11.815385 4.501099-17.441758 12.94066-17.441758 3.375824 0 6.751648 0.562637 10.127472 1.687912 3.375824 1.125275 8.43956 3.375824 14.065934 5.626374 19.12967 8.43956 39.947253 15.191209 61.89011 19.692307 22.505495 4.501099 44.448352 6.751648 66.953846 6.751649 35.446154 0 63.015385-6.189011 82.145055-18.567033 19.12967-12.378022 29.257143-30.382418 29.257143-53.45055 0-15.753846-5.063736-28.694505-15.191209-39.384615-10.127473-10.69011-29.257143-20.254945-56.826373-29.257143L1384.087912 292.571429c-41.072527-12.940659-71.454945-32.07033-90.021978-57.389011-18.567033-24.756044-28.131868-52.325275-28.131868-81.582418 0-23.630769 5.063736-44.448352 15.191209-62.452747 10.127473-18.004396 23.630769-33.758242 40.50989-46.136264 16.879121-12.940659 36.008791-22.505495 58.514286-29.257143 22.505495-6.751648 46.136264-9.564835 70.892307-9.564835 12.378022 0 25.318681 0.562637 37.696704 2.250549 12.940659 1.687912 24.756044 3.938462 36.571428 6.189011 11.252747 2.813187 21.942857 5.626374 32.07033 9.002198 10.127473 3.375824 18.004396 6.751648 23.630769 10.127473 7.876923 4.501099 13.503297 9.002198 16.879121 14.065934 3.375824 4.501099 5.063736 10.69011 5.063736 18.567033v26.443956c0 11.815385-4.501099 18.004396-12.940659 18.004395-4.501099 0-11.815385-2.250549-21.38022-6.751648-32.07033-14.628571-68.079121-21.942857-108.026374-21.942857-32.07033 0-57.389011 5.063736-74.830769 15.753846-17.441758 10.69011-26.443956 27.006593-26.443956 50.074725 0 15.753846 5.626374 29.257143 16.879121 39.947253 11.252747 10.69011 32.07033 21.38022 61.89011 30.945055l79.894505 25.318681c40.50989 12.940659 69.767033 30.945055 87.208792 54.013187 17.441758 23.068132 25.881319 49.512088 25.881318 78.769231 0 24.193407-5.063736 46.136264-14.628571 65.265934-10.127473 19.12967-23.630769 36.008791-41.072528 49.512088-17.441758 14.065934-38.259341 24.193407-62.452747 31.507692-25.318681 7.876923-51.762637 11.815385-80.457143 11.815385z" fill="#252F3E" p-id="4278"></path><path d="M1538.813187 808.50989c-185.107692 136.720879-454.048352 209.301099-685.292308 209.301099-324.079121 0-616.087912-119.841758-836.641758-319.015385-17.441758-15.753846-1.687912-37.134066 19.12967-24.756044 238.558242 138.408791 532.817582 222.241758 837.204396 222.241759 205.362637 0 430.98022-42.76044 638.593406-130.531868 30.945055-14.065934 57.389011 20.254945 27.006594 42.760439z" fill="#FF9900" p-id="4279"></path><path d="M1615.894505 720.738462c-23.630769-30.382418-156.413187-14.628571-216.615384-7.314286-18.004396 2.250549-20.817582-13.503297-4.501099-25.318681 105.775824-74.268132 279.630769-52.887912 299.885714-28.131869 20.254945 25.318681-5.626374 199.173626-104.650549 282.443956-15.191209 12.940659-29.81978 6.189011-23.068132-10.690109 22.505495-55.701099 72.58022-181.169231 48.94945-210.989011z" fill="#FF9900" p-id="4280"></path></svg> diff --git a/ui/public/imgs/providers/azure.svg b/ui/public/imgs/providers/azure.svg index 01b62bc9..014647a3 100644 --- a/ui/public/imgs/providers/azure.svg +++ b/ui/public/imgs/providers/azure.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M565.76 114.761L233.472 821.833 0 819.2l260.608-448.512L565.76 114.615m35.84 55.808L1024 909.239H242.761l476.16-84.846L469.577 527.8 601.6 170.423z" fill="#1E88E5"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M565.76 114.761L233.472 821.833 0 819.2l260.608-448.512L565.76 114.615m35.84 55.808L1024 909.239H242.761l476.16-84.846L469.577 527.8 601.6 170.423z" fill="#1E88E5"></path></svg> diff --git a/ui/public/imgs/providers/baiducloud.svg b/ui/public/imgs/providers/baiducloud.svg index 25ab747a..e448fd3e 100644 --- a/ui/public/imgs/providers/baiducloud.svg +++ b/ui/public/imgs/providers/baiducloud.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1684" width="200" height="200"><path d="M482.687 74.101c10.109-5.627 19.662-12.497 30.785-15.998 8.506-2.192 16.685 2.323 23.883 6.38 117.253 68.08 235.062 135.312 352.118 203.753-40.338 22.115-79.662 46.063-119.805 68.506-27.677 15.148-62.814 13.74-89.771-2.388-48.289-28.135-96.871-55.78-145.127-84.014-8.997-5.692-21.232-5.889-30.654-1.21-49.728 28.724-99.456 57.58-149.282 86.173-27.056 15.834-61.963 15.867-89.314 0.687a26252.906 26252.906 0 0 1-113.785-65.628c-0.229-1.178-0.72-3.533-0.949-4.744 110.776-63.533 221.256-127.722 331.9-191.517z" fill="#72AF2D" p-id="1685"></path><path d="M115.552 719.744c0.49-135.148-0.622-270.329 0.556-405.477 32.617 19.367 65.595 38.08 98.441 57.088 12.76 7.427 26.27 14.199 36.74 24.864 15.769 16.39 26.042 38.67 25.845 61.637 0.033 54.57 0.131 109.172-0.065 163.774-1.047 12.203 3.304 25.65 14.493 31.963 40.567 23.72 81.396 47.045 122.095 70.6 14.362 8.638 29.771 15.9 42.4 27.057 18.156 17.11 28.756 41.777 29.116 66.707-0.033 44.559-0.196 89.15 0.066 133.709-10.175-3.468-18.877-9.848-28.201-14.984-108.55-62.716-217.167-125.366-325.652-188.148-10.207-5.66-17.143-16.947-15.834-28.79z" fill="#118CCF" p-id="1686"></path><path d="M815.143 367.397c30.753-17.47 61.015-35.824 92.095-52.705 0.196 135.017-0.066 270.035 0.13 405.052 0.819 11.582-5.3 23.163-15.637 28.627-110.416 63.86-220.896 127.558-331.312 191.386-7.328 4.45-14.722 8.8-22.508 12.432-0.098-44.82 0.065-89.64-0.098-134.428-0.426-31.538 17.47-62.16 44.558-78.06 49.074-28.43 98.245-56.664 147.286-85.159 8.245-4.515 15.311-13.053 14.919-22.9 0.13-55.683 0.065-111.398 0-167.08-0.033-23.784 8.048-47.732 24.21-65.398 12.497-14.362 30.36-22.083 46.357-31.767z" fill="#DA4525" p-id="1687"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1684" width="200" height="200"><path d="M482.687 74.101c10.109-5.627 19.662-12.497 30.785-15.998 8.506-2.192 16.685 2.323 23.883 6.38 117.253 68.08 235.062 135.312 352.118 203.753-40.338 22.115-79.662 46.063-119.805 68.506-27.677 15.148-62.814 13.74-89.771-2.388-48.289-28.135-96.871-55.78-145.127-84.014-8.997-5.692-21.232-5.889-30.654-1.21-49.728 28.724-99.456 57.58-149.282 86.173-27.056 15.834-61.963 15.867-89.314 0.687a26252.906 26252.906 0 0 1-113.785-65.628c-0.229-1.178-0.72-3.533-0.949-4.744 110.776-63.533 221.256-127.722 331.9-191.517z" fill="#72AF2D" p-id="1685"></path><path d="M115.552 719.744c0.49-135.148-0.622-270.329 0.556-405.477 32.617 19.367 65.595 38.08 98.441 57.088 12.76 7.427 26.27 14.199 36.74 24.864 15.769 16.39 26.042 38.67 25.845 61.637 0.033 54.57 0.131 109.172-0.065 163.774-1.047 12.203 3.304 25.65 14.493 31.963 40.567 23.72 81.396 47.045 122.095 70.6 14.362 8.638 29.771 15.9 42.4 27.057 18.156 17.11 28.756 41.777 29.116 66.707-0.033 44.559-0.196 89.15 0.066 133.709-10.175-3.468-18.877-9.848-28.201-14.984-108.55-62.716-217.167-125.366-325.652-188.148-10.207-5.66-17.143-16.947-15.834-28.79z" fill="#118CCF" p-id="1686"></path><path d="M815.143 367.397c30.753-17.47 61.015-35.824 92.095-52.705 0.196 135.017-0.066 270.035 0.13 405.052 0.819 11.582-5.3 23.163-15.637 28.627-110.416 63.86-220.896 127.558-331.312 191.386-7.328 4.45-14.722 8.8-22.508 12.432-0.098-44.82 0.065-89.64-0.098-134.428-0.426-31.538 17.47-62.16 44.558-78.06 49.074-28.43 98.245-56.664 147.286-85.159 8.245-4.515 15.311-13.053 14.919-22.9 0.13-55.683 0.065-111.398 0-167.08-0.033-23.784 8.048-47.732 24.21-65.398 12.497-14.362 30.36-22.083 46.357-31.767z" fill="#DA4525" p-id="1687"></path></svg> diff --git a/ui/public/imgs/providers/baishan.png b/ui/public/imgs/providers/baishan.png new file mode 100644 index 00000000..af1e555d Binary files /dev/null and b/ui/public/imgs/providers/baishan.png differ diff --git a/ui/public/imgs/providers/cachefly.png b/ui/public/imgs/providers/cachefly.png new file mode 100644 index 00000000..86a13db5 Binary files /dev/null and b/ui/public/imgs/providers/cachefly.png differ diff --git a/ui/public/imgs/providers/cdnfly.png b/ui/public/imgs/providers/cdnfly.png new file mode 100644 index 00000000..3a8d9843 Binary files /dev/null and b/ui/public/imgs/providers/cdnfly.png differ diff --git a/ui/public/imgs/providers/cloudflare.svg b/ui/public/imgs/providers/cloudflare.svg index b6f07b50..5a6d858b 100644 --- a/ui/public/imgs/providers/cloudflare.svg +++ b/ui/public/imgs/providers/cloudflare.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4261" width="200" height="200"><path d="M704.38432 718.72c6.304-21.632 3.872-41.408-6.624-56.128-9.568-13.504-25.792-21.28-45.28-22.208l-369.472-4.8h-0.096a6.688 6.688 0 0 1-5.568-3.008l-0.032-0.032a8.192 8.192 0 0 1-0.896-6.624 10.24 10.24 0 0 1 8.672-6.656l372.736-4.8c44.16-2.08 92.16-37.824 108.96-81.632l21.28-55.52a11.584 11.584 0 0 0 0.64-7.168 242.4 242.4 0 0 0-236.8-189.664 242.656 242.656 0 0 0-229.856 164.768 110.176 110.176 0 0 0-76.544-21.28 109.28 109.28 0 0 0-94.816 135.648 155.04 155.04 0 0 0-150.656 155.168c0 7.456 0.608 15.008 1.504 22.496a7.456 7.456 0 0 0 7.2 6.304h681.888a9.312 9.312 0 0 0 8.672-6.624l5.12-18.208z m117.6-237.376c-3.296 0-6.88 0-10.176 0.48-2.4 0-4.512 1.76-5.408 4.16l-14.4 50.112c-6.304 21.632-3.904 41.408 6.592 56.16 9.632 13.504 25.824 21.248 45.344 22.176l78.656 4.832c2.368 0 4.512 1.12 5.664 3.008a8.64 8.64 0 0 1 0.928 6.656 10.24 10.24 0 0 1-8.704 6.624l-81.952 4.8c-44.416 2.08-92.096 37.824-108.928 81.664l-5.984 15.296c-1.216 3.04 0.928 6.048 4.192 6.048h281.504a7.36 7.36 0 0 0 7.2-5.376c4.8-17.408 7.488-35.712 7.488-54.624 0-111.04-90.656-201.696-202.016-201.696z" fill="#F38020" p-id="4262"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4261" width="200" height="200"><path d="M704.38432 718.72c6.304-21.632 3.872-41.408-6.624-56.128-9.568-13.504-25.792-21.28-45.28-22.208l-369.472-4.8h-0.096a6.688 6.688 0 0 1-5.568-3.008l-0.032-0.032a8.192 8.192 0 0 1-0.896-6.624 10.24 10.24 0 0 1 8.672-6.656l372.736-4.8c44.16-2.08 92.16-37.824 108.96-81.632l21.28-55.52a11.584 11.584 0 0 0 0.64-7.168 242.4 242.4 0 0 0-236.8-189.664 242.656 242.656 0 0 0-229.856 164.768 110.176 110.176 0 0 0-76.544-21.28 109.28 109.28 0 0 0-94.816 135.648 155.04 155.04 0 0 0-150.656 155.168c0 7.456 0.608 15.008 1.504 22.496a7.456 7.456 0 0 0 7.2 6.304h681.888a9.312 9.312 0 0 0 8.672-6.624l5.12-18.208z m117.6-237.376c-3.296 0-6.88 0-10.176 0.48-2.4 0-4.512 1.76-5.408 4.16l-14.4 50.112c-6.304 21.632-3.904 41.408 6.592 56.16 9.632 13.504 25.824 21.248 45.344 22.176l78.656 4.832c2.368 0 4.512 1.12 5.664 3.008a8.64 8.64 0 0 1 0.928 6.656 10.24 10.24 0 0 1-8.704 6.624l-81.952 4.8c-44.416 2.08-92.096 37.824-108.928 81.664l-5.984 15.296c-1.216 3.04 0.928 6.048 4.192 6.048h281.504a7.36 7.36 0 0 0 7.2-5.376c4.8-17.408 7.488-35.712 7.488-54.624 0-111.04-90.656-201.696-202.016-201.696z" fill="#F38020" p-id="4262"></path></svg> diff --git a/ui/public/imgs/providers/cloudns.png b/ui/public/imgs/providers/cloudns.png new file mode 100644 index 00000000..1ad3068d Binary files /dev/null and b/ui/public/imgs/providers/cloudns.png differ diff --git a/ui/public/imgs/providers/cloudns.svg b/ui/public/imgs/providers/cloudns.svg deleted file mode 100644 index 44cf1ed2..00000000 --- a/ui/public/imgs/providers/cloudns.svg +++ /dev/null @@ -1,99 +0,0 @@ -<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="200" height="200" viewBox="0 0 180 180" enable-background="new 0 0 180 180" xml:space="preserve"> <image width="180" height="180" x="0" y="0" - xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAAIGNIUk0AAHomAACAhAAA+gAAAIDo -AAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGYktHRAD/AP8A/6C9p5MAABRtSURBVHja7Z17WFTV+se/ -wx2cQUWRO4Lt0crHUNTq8SigooiKeQE0y46EpXZU7FjnVFqnUrseK7TfOWnSsYumgqWpKJqZaZ08 -IplPWcIImgiYgpcZkJuzf3+gODN7z8yemT3OsHo/z7OfZK+13sva31mz9pq1dwoAPAiCETxcHQBB -yAkJmmAKEjTBFCRogilI0ARTkKAJpiBBE0xBgiaYggRNMAUJmmAKEjTBFCRogilI0ARTkKAJpiBB -E0xBgiaYggRNMAUJmmAKEjTBFCRogilI0ARTkKAJpiBBE0xBgiaYggRNMAUJmmAKEjTBFCRogilI -0ARTkKAJpiBBE0xBgiaYggRNMAUJmmAKEjTBFCRogilI0ARTkKAJpiBBE0xBgiaYggRNMAUJmmAK -EjTBFCRogilI0ARTkKAJpiBBE0xBgiaYggRNMAUJmmAKEjTBFCRogilI0ARTkKAJpiBBE0xBgiaY -ggRNMIWXqwNgiZRRSYiMCMKAu7uIltdcaMHJ8lrkbyl0dajMogDAuzqIjkzGlLEYNyIMI+KuIKx7 -raQ22mtKHC3tgQPFzVj25seuToEpSNB2kv3nyZg1UYWBvU87ZEd3TYUN+4LxzvuHoNGUuTqtDg8J -2kZSRiXhxbk9MVB9Wla7ukYV1uzohmde+tDVKXZoSNA2sOSph/HXjMtQ+unM1imtCkdVnS9OnjXu -VlWAB/pEtCI8qB5hQeanJkc10Zjx9//SaG0nJGiJbFmThbTBFYLz1XXdsP+nQBwsuYy8Dz+XZCsl -OQmD4yKRMMAbSX2FNnVNSsx+Q4/8z+jm0VZI0BLYsjoLEwaXG52rutQN7xcGYNmKTxyynZKchLSR -MZiedAEq31sjv7ZJidlv6pH/2S5Xp9+hIEFbYct7WZgw6JaYtU1KvPVZFyxbsV5WPynJSVg0IwbD -DUZsbZMSs1eQqG2BBG2Bgn/PxAMDbwmsuCIaMxZ/79T57cK5U/HCdC1UvvUAAG1TJ8x+i0f+5yRq -KXgCeNHVQbgjS/46HbNHnmn7uPPAtpJYpGR9hLq6Oqf6/b74Z5Rf5nB/XxUCfRrg69GMvlww9hY3 -Od03C9BP3yKkT0rFP6ZUAnoAeuClzZFIf+L2Laflf74LyfN+wq81YYAeuDO4CkvmDnF1t3QImJ1y -cJwa45MH4I4IXwzspYfKrwV39qgWrVt5JQjVV5Q4Wu6BU+eakD0K7XXXH47FzKc+clkOe9+5G5Gd -20bmOWu6I+9jaSspf1SYEnRKciKG3xuFcQObcWf3aoftfaWJQUq2a3+aTp+UitVP8Aj0qUfl1SDE -pm1zaTzuDhOCzn54ElLvVWFCvwrHjd3g3NUgjFp0wi1+4Fg4OxNvTGv7gL61pyeeedWxpUKW6dCC -Hp2ciBdnhGNQ5FnZbc/9T3fkfbLV1Sm2s+6NGZg+sALnrgYhdtIXRmUcp0bi/X1F27lTDreDDivo -VS89jNlDTlusc+RcFErOeOLUuSZoG1pwtuYS9nx5oL18dHIiokK7AgD6q5UY3+8qIlR1+OJELNJz -3GsXHMepcTg3FiqfBmwoiQUAqENaMTjC+of5nDYIpRcCUVLB4+ivtSjYutvV6TiNDifo0cmJ+Oej -wejTrUZQpmsOwP5TITh0ohHvrMm32Xbt9tEAgPuerHCLqYYpi3Om4YWxlQ7bqdIGYWOxCnkF/3PL -PB2hQwk6feIYvPfodai8G4zOn9MGIe/bTti086jdF2jh4xl4fVIV1nwfi/kvue8ctXxzGiJU8qxH -a1sCsONECJatPcyMsDvMOvTCxzKwOqsVgV4NUPCAggd0zf5Y830MemVux/LcjQ5dlP6xvlDwQO76 -w65O1SIfHOrUnr+jR6BXA6bfU4HDK3pi1T8ecnVqstAhRuj0iWOw+pFWKL2vtZ8rronEyxtrULTv -gAOWb3H8w8koveiP9EXy7tFwButeeRg1l/UoO9eAyppLFvsg+6GJCAvyAxfmhfiIBvQJOm+27sm6 -EDz9Ua1sfeoK3F7QKSMTsX6uF1QGYn772xg888YGWf00bRuCv20PR+7aAlen7FQ4To1pqQOR2k+P -QSHC+bi2xR9Ld3ftsP3g9oI+/p9J6NOlbVTRtvhj6R75OztlZCLWz/ZC98x9rk73tpIyMhGPpUYg -7Y7TgrKlX0Vh2apNrg7RZtx6Dr1qyXT0CTwP6AFtkz/mrPd2ysgRGdIFxTXBrk73tlO07wDSn9qA -Ce96Yf/Z6Pa9K9ADzyedRcGb010dos24raDTHxiDx+MqAD0PbbMf5nzqjYJtzls/LfvdbbvC6RTt -+wZjFmzE33aFQdvsB+h5QM8jLaYCrz39oKvDswm3vYpPpqjA6wFeDyz9sqtTxQwAx8rrXZ2yy8ld -uwUPrW3FySs92vt+4aDTWDIv09WhScYtBZ09/QEM7Nq2ffP94zHIzdvi6pD+MBTt+waTXzuBk1d6 -tE8/cu67gPQHUlwdmiTcUtBj+vkDeh7FF8Mxf/mnTvd3pb4RV+obXZ2226DRlCHu8W0ovhgO6Hmo -PK7h+VQ/V4clCbdb5UgZmYCtM5uhbfXDjE/0KNr3jatD+sPCcWpsWXQ3eivbVpnWnojF/FedP8A4 -gtuN0ElxbU9pbDoZSmJ2MRpNGaasOHHjRhGYylUjZUSCq8OyyG15WWP2gxMk1x3bqwENrd7ILTgK -4MbWyMF3GdWpPH8ZRV91LLGnjEhAZEgXu9sfOPKLTT/ti/WbNRtibQDg5W888ebwc1B5NuCxEbEo -+kqe3GzNSQpOEzTHqfHqn+ORFFoDpUeNTW2rmrq2J5o46C68m2zcfm2ZsFPdnfH3hmGW2oEHEJK7 -4sDFDLxddN7ihzllRAJmDQ/D+PAKADUCGxvPTEPWyxtF24r1NdDW3xvLe2Faz3KMC6sAx6mh0ZQh -J2syFgyuR7jvJaEvCaztGY/5r8kraKdMOXKyJuO7p8MxPrQCSlwzWrCXctQ2mdyAmNZxq1m/RHjb -+kDsSAw6g60PNmLx3Ayzbv412R/jQyvM2pgWVW6xvWg7Hli+4SiqrnUF9EB2Wjzyl07Fa0MqEe59 -yf6cnHAdZRd0zsxJePX+s1CiAbyet+toblUYKcG0vKMK2t7+MD0W9z+NxXPSBS4Wz0lHmHed1fY5 -99SYmQuL1wffNp/+4CcVeD2Ph7kLGNej3OE83F7QKSMSsHjwRYdHIp43yZRGaMGR0++8QJRcdw9J -bZVoxKDePcTjtDCSLn+vADVNXdDNQytPHu4u6FkJIVCi0eFE65sNpvZiQuioipZR0Eo0YlZCiJEH -TtkkuX3/UIVoiKL1Dfr7+OUusuWga5b/Osp6U5gQVN32VSLC5upeWL75B4l3tUeM+9nUZgfVs1jf -PHc0CrnrxN+1wXFqZI/tj0e5aigVwh9+EoKEr2oQ81FSH4H4TuestjXX3rC/fT2ui9bR8X54ZKuH -jatPR2yoKw3ZRuiMtNFQ8uKj83NHo5D1ymb7l2gYHqF111rNttBoyvDsynzM2xsgPkrzjciemmbQ -T+I+dld4ibbNSBttva9N+lvp0SpaZ+XPIW6xlCrbCB0Y4HsjeWNKm3uYHYEkY2rXjJ6zp6ahf6Qf -+gc1STJ7rM4Xxyobkbdpu6Bs8ewpGBPdYnRu5feNyN++R9RWyvBhWJLc1ejc7t+8sXz1llsx62EX -+dv3YGzfDGSGCJf9uB4GK0JmfGzadxzP3dlZcD4+pjMEjxKLxcib/FukTs3lZvuSkxkZpxxtWw5N -OXapk/x2Tf7kODUWp/dHZnC5TZbjwwGEA2PUU/DsxuNG3yChnTwQ72f8NR0YEGbWVmSPzoL6xzr1 -Mo5ZdDom7dvmWHUrMoOFdZXehqbEr4FGU4bSxjHo7XPB6PzQENNpjHh7Y0HzDuXhbOS7KeTRvuXQ -8NA6+sEVs2tS5bkpccjoVi7qX8qR2vk0Vj3U19StoJ7Fa2YlTjF7Vm0aoLvWIh6/oQ8z1wAAfris -FJwf4Fsl6RpK8ZHC+TosITmQ95dCZ31wefN/Z2eOR2a3Cof9DOt0FjmPTETuR1tv+bDVpoU47bJn -zb7pOQsfkG8rGpHZTXjeKGcJPjRaX8SLaHds59PY9cJElGm9Icap2hboGltxoOSkU1+ZIKugFaJf -V44rWmDXwOafevqJ+7WDP0V4INfAh612LcVpjz2r9k18KCz4yNu8Ayvj4wXn40KMJWDNx481rZga -JO4jIeA3JASYCf7GCqNuQDA2nY/Dyh0/OkXYsgqa1ztuw1a76oBGQXlZSzDWnfJHVV098nfsbT/P -cWokxvcBF+yHrPAqwVKY2s/4qRVb87FW39H+sdqet1znG10UhgUYvzpsgFJrk4/cj7Zi3tLhCPO8 -YlcOndCIR3uUIzMrGPP/G2N0feRAPkGbu4t3dIQWs2tgU+1VKyh/5usGFO0vEpjSaMraRwVNxnjk -xhnPIdWeF4x9SFxdkRKnqD1rNq3ZN/HB63mLKynH6rwwzGSbjNrzAlKGD0PR/oOSr+G7ZZ2xvLd9 -gr6JEo14ZbAv8nc4ZEaAjL8U3rj7NT0cng1Yttm29m1cXrT/oFWrefk7xOM1cCsss6JoS7mL2bNq -08b+5XmLOZWcuSpaPuiOYOk+0DZKv3o6BrrrPuL1JR5histYPGuyowIxQt7NSddFDjmmt5Zsivl0 -JN6b8HbYtRQnD8dildK/Vnzk79yLqtbOgvK4rnrpPm6wfO1nGPpxLT640AslTWGidqUcKSHyPvom -7xzaSUuRluw64tNaW1tty23P5va89TrHGrogLMB4utDf75JdMWo0ZViwyvqNXca4Ucjq549hPsJX -/6o96xzrFBNkXYcW/7qSYw5twaalaYM1LE45bJw+WYvT3HTApjm05f7lrUw5AODHSwrhVz9/BRnj -RjntGubv3IsFBb9AxwunKEq9vCO0vFMOZ20RtGRTdO+BA/HehLfDrqU4eTgWq5T+leDjyCnx7b3x -USppPuxEoylDaUuQqP3RScMcd3ADmefQCsER4mvrRFGCXSs+HYlXsl9b43Qw1rgQX+vtees+9nx9 -EKX67oI6capmx/vTGmbii+oeKJsLWVc5xJ5KSPU8A45Ty2vX4CuwFEGCcimf+Oz0seJPUdz0youV -WZ5zWIpTzJ51m21wnBrpnSpF259vMBiCzT0ZYsKxBqWgzlBUmr2GvAw3RxynhpqvFbWfV7DTYfs3 -kU3QeQWFbXMkkTvZ3Am9kZ0+1n7jFlYGdNe9BeXZ/btaNMdxajwR2yRoV4ZuFv0OCfMxa3NImA+s -rmDYscqRMTYZmyZFQ9naLNr+SEXtrcoSV1KO1+pF6z0So7crRik8m9pXNAcd7+OgZWNkXeUoqI9A -lp9wx1sCKpEQBfzlqZHQ8tZd/tgQgAX//rz9b4XJCKEw+LNe7ykoH6s4g9JFQ7G7QfhG0biABsTr -a4DWi4Iynd7LyIep3UyvCgx4aiQO1Qfi1NW2K31HoCeGdroK9XXhfhLDOMXsAcDfe17FI4vEX7MV -hnqE6S8AZrZMl3l2x56vDV4BzPOiPkzJXf8Flj0ZJzgff118079hHhljkzHvLk+rPoR5iO+3OaaX -962vsgp65d6fkJ7WBSq9+BY7tf6iNEP+obf+beUXuG/rvDGss9BEmF6LLB+tsMD8fnp8p7u1EeH8 -NR4QGTzU+otQ+1wEuhucbBG3d/6a9V8Kw/RahEELe/i/30x2CZn7pU+EEkUo4q9Le/WAYR6B/j6I -bz0nqZ0Uvr3i7bgRA2S9KdRoyvD6+VC7FtiNDtN5n4WvwOXrtkILH4d9auGDvP0/tdvddOhnh21u -OvSz5TwcOAr5aORtKTS2r+clTxm+0/pL9lX6u84peZQoQrB83VbZ9Ac44TUGuRu2Y2F1BKo8lOI3 -GHY83m6t/PULodAqfBx6pL6gMcJo95dGU4YlddF22dUqfLCkLtp4Nxkv32sMChXRmLpS+JSNOR9i -5O3/WVJuhYpo5Bd+afF62HOUegYhe0eF3PJzzotm8rYUYlzh7yhADHQKH5ueBG5b9zS5CCblpnfd -uRu2I0fT2S5f1R4qPH+pJxas3irII3fDdsw87ouDnhGS7R30jMDM477I3WAsOP7mlMOB42asU1eJ -iBm4Na2RsNat0ZQhR9MZ1R4qs/52KaKxeNevwsYO5lGAGEzdWemU7aO35e2j2ZNTofT1Qi+VtM9P -uVaP3E/bLhrHqTF/xN1G5YfO1iN/15eibbMnp4Lr6oMAK/ctvzfyqNG1IO+zXZJi4jg1Eu/hcE93 -8bvy4xebceC4xuxFykhNxtAo+x5HK9fqUXX5mtmcb5LzYJqgjxuuA8/mmf8f3nOcGlOH3IUefgqj -NiVV4n0sdj2k8nsjj03fyf8+O0Pc7nW6RMdnyfMvIiQ0FLlvr0DOk4vw9f6vkDR8RHv52d9+Q1R0 -dPvfO77YhqIiaQOLNW7L20eJPxaPPz4LUZERqNfpMO+J2e3/ffdfq9vrhIeHYfLECUbn5IAETchO -VGQEACApKdHo/OBBAwG0jci1tRcxeeIEzP/LHFl9k6AJp3D4f8W4795BRueOFB91ul8SNCEbKSmp -GHzvfQDaxGsq6NuBJ4AXXd0RBBtwnBoREZE48ctJHPuhBEeKS3Dil5PYXbgTV7S3HkAuPXkSV69e -xeWrOuwqlPehQlrlIJjC7f6nQQThCCRogilI0ARTkKAJpiBBE0xBgiaYggRNMAUJmmAKEjTBFCRo -gilI0ARTkKAJpiBBE0xBgiaYggRNMAUJmmAKEjTBFCRogilI0ARTkKAJpiBBE0xBgiaYggRNMAUJ -mmAKEjTBFCRogilI0ARTkKAJpiBBE0xBgiaYggRNMAUJmmAKEjTBFCRogilI0ARTkKAJpiBBE0xB -giaYggRNMAUJmmAKEjTBFCRogilI0ARTkKAJpiBBE0xBgiaYggRNMAUJmmAKEjTBFCRogilI0ART -kKAJpiBBE0xBgiaYggRNMAUJmmAKEjTBFP8P/Lt7vnXgT9wAAAAldEVYdGRhdGU6Y3JlYXRlADIw -MjUtMDEtMjNUMDk6NDU6MTgrMDA6MDBDI8mIAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDI1LTAxLTIz -VDA5OjQ1OjE4KzAwOjAwMn5xNAAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyNS0wMS0yM1QwOTo0 -NToxOCswMDowMGVrUOsAAAAASUVORK5CYII=" /> -</svg> diff --git a/ui/public/imgs/providers/cmcccloud.svg b/ui/public/imgs/providers/cmcccloud.svg new file mode 100644 index 00000000..0a44fcfa --- /dev/null +++ b/ui/public/imgs/providers/cmcccloud.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M663.48 555.229l-32.39-29.14-15.173-13.607a60.881 60.881 0 0 0-81.64 0l-105.603 95.127c-9.634 8.67-24.324 8.67-34.078 0l-5.177-4.576-26.612-23.962-7.586 36.605 19.266 17.22 13.246 11.8 15.172 13.727a60.881 60.881 0 0 0 81.64 0l101.87-91.755 3.734-3.372c9.633-8.67 24.203-8.67 33.956 0l5.419 4.817 32.632 29.38 1.566-42.024z" fill="#8DC21F"></path><path d="M294.653 383.76c8.79-0.362 16.015-7.226 16.858-16.016 10.355-101.87 96.451-181.464 201.091-181.464 110.179 0 199.646 88.023 202.055 197.6 23.842 0 47.804 4.455 70.442 13.004 0-2.77 0.12-5.66 0.12-8.429 0-150.638-122.1-272.737-272.737-272.737s-272.738 122.1-272.738 272.737v5.419c18.062-6.02 36.365-9.272 54.668-10.115z" fill="#0084CF"></path><path d="M726.457 858.43h2.408c1.927 0 3.733-0.24 5.54-0.36 145.46-10.838 259.13-135.707 251.905-285.502-7.104-146.182-130.287-261.418-276.71-259.13h-1.927a333.306 333.306 0 0 0-214.578 83.085l-0.482 0.482c-1.565 1.445-3.25 2.89-4.816 4.335L352.692 522.837a61.05 61.05 0 0 0 0 90.792l2.409 2.168 32.27 29.14-7.104-6.382a30.465 30.465 0 0 1-0.963-44.433l4.816-4.335 62.254-55.872v0.482l78.751-70.803c45.878-46.841 109.095-76.704 179.176-79.112 85.012-4.094 166.773 46.239 198.683 130.167C942.6 619.048 890.1 735.73 785.821 775.466c-5.298 2.047-10.596 3.732-15.894 5.298-1.686 0.482-3.372 0.963-5.058 1.324l-0.722 0.241-4.576 1.084-0.963 0.24c-1.445 0.362-2.89 0.603-4.335 0.964l-0.963 0.24c-1.445 0.242-2.89 0.603-4.335 0.844h-0.964c-1.445 0.482-3.01 0.722-4.455 0.843h-0.843a34.87 34.87 0 0 1-4.937 0.722h-0.361c-5.419 0.723-10.837 1.084-16.136 1.325h-11.8c-32.15-0.723-63.458-9.152-91.394-24.203-8.43-4.576-18.664-3.492-25.769 2.89l-41.784 37.569c40.34 30.224 89.227 49.49 142.45 53.584 0.723 0 1.445 0 2.168 0.12 0.842 0 1.685 0 2.528 0.12 1.084 0 2.047 0 3.131 0.121h21.675c1.324 0 2.77 0 4.094-0.12z" fill="#0084CF"></path><path d="M665.889 557.396l-2.408-2.167-25.167-22.638a30.525 30.525 0 0 1 0 45.396l-22.517 20.23-44.433 39.977v-0.361l-78.871 70.803c-45.878 46.841-109.095 76.704-179.176 79.112-85.012 4.094-166.773-46.24-198.683-130.168-39.616-104.398 12.884-221.08 117.163-260.816a202.626 202.626 0 0 1 62.615-13.005c8.79-0.361 16.136-7.104 16.978-15.894 1.927-18.905 6.382-36.968 13.126-53.946-6.984-0.482-13.968-0.843-21.073-0.843-150.517 0-272.497 121.86-272.737 272.256-0.241 150.517 122.1 273.099 272.737 273.099h10.115-3.853c1.806 0 3.612 0 5.418-0.241h-1.565c80.677-2.408 154.13-33.234 210.724-82.965l0.482-0.361c1.926-1.686 3.974-3.492 5.9-5.299l134.984-121.497a60.917 60.917 0 0 0 0-90.672z" fill="#0084CF"></path></svg> diff --git a/ui/public/imgs/providers/dnsla.svg b/ui/public/imgs/providers/dnsla.svg new file mode 100644 index 00000000..7908049e --- /dev/null +++ b/ui/public/imgs/providers/dnsla.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 122 24" fill="none"><g><path fill-rule="evenodd" clip-rule="evenodd" d="M12.8182 17.2296L8.47407 17.2679C8.34011 17.2679 8.16309 17.2248 8.23007 16.909L10.5409 5.64677H13.0335C17.3729 5.64677 19.3105 8.13461 18.6599 11.7468C18.0044 15.3541 15.6745 17.2296 12.8182 17.2296ZM0.431641 23.4205H15.4687C19.3105 23.4205 23.1236 18.8563 24.2671 16.6076C25.4823 14.225 25.8938 10.4502 25.4775 7.79014C24.9513 4.43156 21.4539 0.0634766 18.6551 0.0634766H5.20639L0.431641 23.4205Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M48.2748 15.6938L41.1366 0.0634766H36.247L31.4531 23.4205H36.357L39.6008 7.9863L46.6912 23.4396H51.5329L56.3268 0.0634766H51.4803L48.2748 15.6938Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M56.982 10.5046C57.2882 12.8011 59.3263 14.1933 61.5558 14.2077H67.6127C68.4021 14.2555 68.7275 15.2985 68.6318 15.9396C68.3686 17.6954 67.3065 19.724 66.0817 19.724H54.5898L53.8291 23.4414H68.5696C72.6889 23.4414 75.5595 12.4662 71.2679 10.318C70.5359 9.94963 69.469 9.75825 68.2825 9.75825H62.216C60.6228 9.75825 61.9433 4.50029 63.5652 4.50029H75.7508L76.6646 0.0556666H63.7422C58.3072 0.0508823 56.5035 6.87331 56.982 10.5046Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M108.376 13.6639L113.845 4.62634L115.73 13.6639H108.376ZM97.9561 23.3569H102.874L106.19 17.9507H116.567L117.687 23.3569H121.973L117.232 0.0668945H111.725L97.9561 23.3569Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M86.6971 23.3662H96.0743L96.8924 20.6439L92.3378 20.7157C90.4719 20.7396 86.7449 20.754 87.5535 16.8213L90.9886 0.0522461H86.0895L82.5539 17.3475C81.6975 21.5099 83.1184 23.3662 86.6971 23.3662Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M27.8549 12.6305C26.8502 19.6682 20.0756 22.8641 18.5781 23.4239H28.563L33.3569 0.0668945H20.6976C25.0752 1.49262 28.8931 6.36784 27.8549 12.6305Z" fill="#1966F0"/><path fill-rule="evenodd" clip-rule="evenodd" d="M78.9946 23.4237H74.5596L75.5117 18.936H79.918L78.9946 23.4237Z" fill="#1966F0"/></g></svg> diff --git a/ui/public/imgs/providers/dogecloud.png b/ui/public/imgs/providers/dogecloud.png new file mode 100644 index 00000000..1897bc88 Binary files /dev/null and b/ui/public/imgs/providers/dogecloud.png differ diff --git a/ui/public/imgs/providers/dogecloud.svg b/ui/public/imgs/providers/dogecloud.svg deleted file mode 100644 index 253f9ae7..00000000 --- a/ui/public/imgs/providers/dogecloud.svg +++ /dev/null @@ -1 +0,0 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="200" height="200"><image width="100%" height="100%" x="0" y="0" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGYktHRAD/AP8A/6C9p5MAAAJGelRYdFJhdyBwcm9maWxlIHR5cGUgeG1wAAA4jZVVS7LjMAjcc4o5ggwIpOM4kbWbqlnO8afBcV4+fqk3dqVkIUQ3DVLo7+8/9Cseb0pylenNiy0mdrHqysXYqrl122Qwb/NyuUxm2LtpWKpL1SFFhxcV+DbrpM1Xx8YqvupW1TAioAg2McuUjYtcvcnqzbDRRoDZwiXmdrXNJdYoEMBGbQYPWfeFu3sy+QoD2yV26H0Hl9p01EIc5KanSTpvsvAAn4IXqOJhs0UaklLMqgjGzlesscRPeGIUwhCTIQtcVpjACq4I+/LyLUkGF5O1sqraV4KUGe6LkWRzxVtkRVLT8+HN4QRewdsBvEiPN5lwEmEetAfh4eKoUujiDclBqVh/ZgEKKBjKwdZTrw6d4BHrFA4Qm4EHeYPVLu9jRULmd74Jth2FIoi8oYOmDaTSwKcEeYhejoq9htSBHnoLS89xP4VFwHESXFwr9tGZyD8PHi3u0AuBeEY8l9SIsaHGHPLLgR3hNQSuCKJRuxoA066PrUnZmwWnZaaT5FfJdlOgolO41Tx3sQHdINy/IFKvVXv2UZTcgnzVrlm33QkJ9Tym4CMN3VNR+SV/XZfs8UUWjePZhEnxifaKw8AwoNnDnN/yhhxJiNc4idFzj8CUyHqKzGfI3wHTjnxcFk/oNTvYgI4JvqNu5Sh3NnKchZs09GNtgtEHhvStNo/IH7Q5pKETbcr/aHMDnrSrw/PzCX/12m+juxU3KD3fxPvSyd9BjYbMdHi/yekfpkRt/MfGbI8AAB9sSURBVHja7Z15fFTV+f8/n+fOJEPClp19UbAuraCiuFHBloqiFtuS1lbb4AIqi4Ii+LWVQaSittiKVsElKLZW1Nbqt1psqUsLarXWqrjboLJkJQghTJK55/n9kYWZLLMkk1z6+5736zWvzNw595znnvnk3Hue85xzqKqwWLxCvDbA8n8bK0CLp1gBWjzFCtDiKVaAFk+xArR4ihWgxVOsAC2eYgVo8RQrQIunWAFaPMUK0OIpVoAWT7ECtHiKFaDFU6wALZ5iBWjxFCtAi6dYAVo8xQrQ4ilWgBZPsQK0eIoVoMVTrAAtnmIFaPEUK0CLp1gBWjzFCtDiKVaAFk+xArR4ihWgxVOsAC2eYgVo8RQrQIunWAFaPMUK0OIpVoAWT7ECtHiKFaDFU6wALZ5iBWjxFF8qMiHp9XV0muA7mhbYWd1rv0/7OD7JcdTNhZ85VFObFnDeHnZM1rZC0vXazoONVO2wxVRkdLAIMBh83ld39HH93HBdrjom14GTT2E2abLhME/EFJDMo7AfRTMo6EtKf4rJEDCNDoTgVjq8J52Bh4ds61VaWNg58U1fr86A0o99OX18ROY+g3ePCgeDNF7XUaqwAmyH4FPbM2r2py0ksJCCTArQ8iIgLZ8JEQCM+F7wGUSL/XAeWDwh+7N4Zc19RtMzanYUuHBGCXk4KF8hcRiAgRDNI0xf0klrrB8ARJiu2QM/twP8jwAloGwB9G3H8X8UnNJvl9f1lwxWgB1w7f0VfbQvbhBgAQSSgABrKfgdwmbl4dV5b8Vq8a55qDTTpOErQplG8AwlRhHoTbaIrKk+mvJu/RkRx1s+E6SGQL4txFNhwRM3Tc59z+t6jIcVYAyufmLHcMf4lqiwyBGwIwGSeA/Q202DPLr0rJw97eVVVFwS6O/rfaTj6A9BnEPoIJABklGC65oAD3xufG/eVvBxxydP3HB6zhav67M9rADjsOjJXV+BCQdJfqutABES8o+uwS3Lzsx9rb3zr1hf3jt9v/kafTKTwAQQfaJF1J0CbM6Lu0B9StTcccPk/H95XaeRWAEmwP88UTHJEEE6+GqzAEVQqsRqx+g9wan5pa3PKSouCfT3Z04i9CoCXwUYaCuanhJg42elqSOdjQ55848nZf/d63oFrAATQ5XXPbnrSojeQGoWBZ9A+IuwX9bdMjn7i1aJeVVxxRgVzKNwGqFZHYumZwV4IC8tB+UhE264Ozh5wH+8rVorwJhc81Bppq+Pcy4MFtKHY4T6NsCb/aHcJ4KFrI9MO/fhqr5OQ/13VGQ+yS83XhOQEgEaDcFhOYFyCioAVoK6H4QRaAbIbAr7EhgBYAgJQRwxG+gb4nDZW6U5Tz/WSTdRV7ECjMHc9TvzAuK7XKizKcwn8SqUK2rSc55ddRbrItPOfrDiMJ8x10B5PpzGHm3jNaFzAlQNUbgV1FdA/EnVfbXX7lBpcMbIUDy7r3tmb55fQl8WwakQTgNwNAFfu62pQa06eq/r4NbgqXk7erqOrQA7YNH6XcNUw/MhnEFBPwpeVeHNvepynm3d8s15oPR0IReDnEAgkPhts10BbgPxRyofVyOv3VLY+hafPMueKx8dJqaKyLkkjkeTy6eVXRtc4y69YWLByz1Zz1aA7bDgke1DKf5FAlxMYUCE76iYZb3+nfd45ChEUXFJoDcyzyGwmMCxjdeAJJ7bWq7ZGOpWAR9uEPfBX3yn+57LbtpYeYIKLoLiTAiGtbLrRbi69PqJec/3VF1bAbZiYXH5ABPgNaBeJsJMEttIrijbu/f+tRG3v6LikkBv7fMdwF1M8CjGeNaKI8BSAL9GWFff9oP8j3rqOpe9WD6ahleo4vt0mH/g2ZMlgASvn5D1UE/YYQUYwczVu/plZjTMF4dXQdCPwj0k7nB6+VeuOLt/9YGUyjlryr9F4Q0gjm5XcHEFqCFC/mSAX1aFal5Zm8CzXXdw49+qx4iauVRMg2gOQYD6iVCCi0/Jfri7y7cCbKKouCSQ5cucBWIRBQOFCFH4cJh6y+2F+R9Hpp2zpuwMEEGQJ3YouFgCBMohvEdE776tsK0P0QuW/a1iooBBgqc19Z7foaPB607MfaI7y02VAP/r4wH7OxlnKfRiAAMBwBCvKflIa/HNva/yBAivBHlipwpSfRdGrzUSWnGwiA8AfjIh74UGx/8thf4YRBmBL8Nw0YpNFVO9ti0R/qtbwHkPlJ0sDpeA+EbTGO97EK5o6JvzaKS7Zd7qXcNcaVgMYAbJQKPNSKIF1NdUcXO/tPw/RvakL7t75wh/WtphxjVb75qZ96EnlRDB8s3Vp4m6QZATCawnNbjopO4JbPg/3wLOfWBnHijfVjWnAY3PZqr6nCPmuUjxFRWXBMKsP0+V04BG8SXJPxXO8m1p+U9Fim/O6tKvOOJc6bpmeHn/3E+8rg8AuP7krBddSBHBu2Aw1QDzlr9cU+C1XbH4rxRgMKhCOGeDeh4o6QCgis0QPNH69tirPvA1gj8gdGCy5RB4H6p39PPnPBs54nD5vaUnqnA5qMPFNPzLq9GI9vjxKdmfhuqyrlEHSwhM9kvd9722KRb/lQKsGlIxRlWnQTGy8YhWKfBsuF/ePyLTzbqnfDTJ8xUYl3Qhit3qYl1NbfjxyJbvijXlY2lkgRo9lcp/1qTXveN1fbQmOImh607K+TlEbjCqx976j6opXtvUESmZE9IdXP3U9tw0BiYKzBg6fP6ms3L/CgBz7/goXR18lYbjWxJTNgH4a+tbr9NgzgJ4+oExi8RQaIiCx8JhPLpm1uDa5uMzV1cMVNedAcE5Av4L1Ne9csMkwuLx2b+55bXqGnXdQ1Zurhy84OTc7V7b1JqDSoDBoMq+MeUjfZQfwugFImYQiHWq9S3PWOFA3+PExVkkGp9tFFth3Kf7l7z3VmRegf2Zx6uYc0kMTE5+gJBvQvG7VUX5LeUWFWuAdWWFIKdTETDEx3C0xxzQnWXR8VlPrfhH1cn1guEArAA7YtEjO0fs91fNEiNFKhjQ2LPmFhjz5+XnDPoUAGauft1PkfGAOe7AQCxeBc2mYHBSuDmvC28rzRSaiQoe3wlT9qnhhvoajYq7S68rPV0g3wcwUIldVP3nry4uKImXWTCosnv47r4NEs5PEy2go/lQ9lGgtxC9AU0D6QcACutJbQCxTyjVCrdCHZYZJ7Ctzxt9dnV2UtPiE3I299DPmDSeC3D++s+zHaSfp+BcQMdENlaq+i8DtEQC+8KDRkF0PICcJv2VwehLv5gx8P3IPHtn4iQlvwGgT/IW6ctw9M+/ml1Q03xk5updwxwT/rZSjwcAAjuVugOgAo0iqx1amVkHDIKDL8Ho0UoeJcQh1awYRoNcPxvrWk1j/F/LdbbEA7LpWONfhYIQ0ACO1qFuTF3Ndb+v+BDCt6HmfYV+4Pj9byyfmv2p179hV/BMgDNXv+7vkzVsPFxeTdEpSgRa3Sl3Avjbim8ecCirzzlBVE9o/vVU8YaK83qzEACgKFgSUPIUKo5L9tYLaIjA5n37wlHh745bf6qCp6JZHUYrHfgqgMbpl1VfbB/oIm2E47AfjKaR/JhqtkIkQKI3RPtBmQVoHoT5gA4COARA34Qta4yEORaqx5Js9L0aF9c/XfE+gBcI3eg67os3nzWwwqvftDN4IsCi4pJA77Q+heqGF5NyhIJttKLAG6J4o/nz3HurhrhuwylKDm8MDjZ1BF8P1UX3QjMGZo4zRicA6JWsXQTfMeq+FtnxuPjOHcNVOZHEIS22ieysR30pADS5YLY1vZJi7sNVfQN0DzGOHgLlSJBHCcw4AKMAJmr/4SQOB3mZD/5dP3m26mWqu07q/BuC52XtTvFPl3J6XIALi8sHhH2YDePOIiWv3VZK0QDqOw3gB82HGsJmFAWHscl1pJQSJbZEimXuHZoedismEDg++dYPAPCmqXffjKogn+8oNWYsQF9zllTd7XP9u7taF6suyNkD4M2mVwvXPlnRx63TY3yOnKzQsaSOBzgifo6aDcVUUKaaXu6nwecqnnSNr3jZlKx/d9XW7qJHBTjvwV3DXCd8LRQzIMzoIJkLYpOo+fOK8wbsbT4ocEfDcDScpgOKNx3wzcgT67hjpAP/0Uji1oaW7Ey1MbJl1SWDt+PSxmPT16vDivIjDGQ0D9zloUBtXYD7uquebp2WtxfAS00vBIMq+4/ddQRNuBCUQgCHJ5DNcFVeSQnPDD5XuUHJW5ZOznmlu2zuLD0mwKuKd44Aw9cYgyISHYkPBDYYhJeuOG9gi1P5irvKB6jqsRAMamqFQlD94AtnX9QDOMU/RtUdSybvXyflExr3Y0QoLbeybLgqxwi1f1RiZWjv52V1yZbRWZp6v1sALAGw5Lonq44UutMATgcwNta5ouwFYhqJCcGNlY9BuSr49Zx3e8r2ePTISMj8tZWDFbxaVS8GkNleGkOoGv2LunrzrRHiA4CwmuGgGR6ReivA9yKdwNPXaxpoRoMyrDM2UvEfA7SKaJYvAfql1qaC8HSNl5un5by7/Nz8n9aluacqZYoCTwO6P+ZJqjlUXCbUp5ZurJwbfL66v5fX0Ey3C3D2g3tyXDVzVKUoVjCAo3wVwM9vKcxvM++VlBGK5mE3AJQPSURFn+TsLDuMyjFAx61rLJSobPCxMvKY65qhIId0dx11lp+dMWDfT8/O2bD87LxzKXIawCdBNMS8TuBQEitF3ftv/HP5MV5fQ7cKcObq1/1w9xeq4gIQvTtKR+rbLvXOin37Xmg3gTH5VMlrSQ+UNTRIeVQefo4CMbozdhJuPRU7BvynoEWAc+/QdHFkOIABbepMNT1rYJbnPtRIlp2Z+9qys3LPcyCng/os4NZ3mFjhA/At+uX+Zc9XTlNVzyKKu1WA6b6hk2hwIYAOWxF1UauKZ4yGnm1vXLWouCRAwSAFspsqb79R/ueey3KipiIaV4dC0anWSuHsUrIycqShNrwtE4re2k4dUdgLnWxpu5vgmdl/D4XwXYVzqUJj934Vx0Bw1/KXdl295JWqpDtuqaDbBDh7TcVhavCjeBHIIthgVB+5vXBou8uTOdW+DEAy0LKQAKtIUxnZWZh7R1VfCkYCyOmkubVqWBt1JD09W6HZHaTPwEEqQKCxF71sSu5D6vBSpT4TM7FiEFxd6tSbq6/ZUJqZYBEpo1sEWFRcElCac0BOhsb0yH2oysdWfq+gw/9UR6Q3cOD2rdBtGjZRTt8aX0Ougp0VHwDsgSBqdSw2aMciUx3kuM6g7qi7VLLs67mvpYm5CMQtIDqepyzIgPK6rF6+m5e/XNajAazdIsDebsZxUExR1bwOE6nbANUXfD5nU6y81JcWUJgDowKK3erI7sg0fuP0BzSr8xZrvcBEPTP5xAQ67jRpFkX6d0fdpZrrv15QlmnCy0T1JoWWdZROAD+AH8KV2cHNn2cnUUSXSLkAL7ytNNMN6yQoToid0vexKv96S2Hs1Ugd100jGqNFAIDgbse4uyPTEDoA2qazkAxGqVGuFRVxFNpBR0MHAxic6rrrLhaeMWCf8eXeKcCtUC2PkbQfDOf63YwLe8q2lAswszfGkzIZjDsa8S+4THrNO4Wpdf2B6FEIdfswRi87kXqgRnuvXaNKdOTvY56KDp65Wv2JZH4wEJzEUE0N7qXDB0wsVw3RH9C5yzftuqAn7EqpAIuCJQGonKiq8fxLFYDZvPJH8WeSNbZMEXFwlL0I1dREJRL2hbJf5y1nmoGkRR7xoVW50enTRTE4rb4yL5HcDxZunZa3l/XhNQ7wm5i1AR4KNYU3vbz72O62KaUCTBvYZwTFHI14cXjUT1zi40TyrA8zpNAD7hnVOtfxRQ+DGQmoamdmvDUTIEzU+Yq0GlXT4XivAoeYND0kftYHFz85Y0CJUfyBZEwXjYBTHOP+6LZNe/O7056UCtBRd5yC8ScAqXwshglNZQwEAtVU3d18JsCG6p3V4cg0FOMj2WnHMIEATXT4lhrWEKiJcdpRFHNkKuuvp9Cq0j8CWKeKDlfmV8AP4rv1CH27O21JmQCLgiUBdXEY1AyNk9QA+ll1eF9C8XP7y6v3g9Lso1MltG2GFO3CtSiQr2DU7bTa7KsAEOuBfaAqDp/7sDcO3K4QLPxyvUA2gvhbnIopcChTb95U2ZmpDQmRMgEGCnqNAnEkWz1LtYZAOcHPEp1NtnbJiDqFVgHYC0Co8GUOz0xpy01FGoje04PvHLB9yBBXyXoqwjFOHWtC7thU2tJTXD8p600KnwewO07Sb5A8d+Xmz5MO8E2E1P2QLkcDGBUvmRK1hq1GHWJBKsDPQbMdAFQ1TUzvqN4njYZJbUg4z7Y2+aCanT9yWMuz62OFrCdZoap7OjqP4IkwelLK6rCHESMvUvFizLppvBWfXqcZE7rFhtRlZbIAxHcGq9aKuokLEACJHVDuAAAh0jP3haM7HIJaKPcnk2fbQjBKTG1UMAOpH1IYq6fei+Toy+/aHu+x46DkzYr+byv5BhCzlQfA40lM7o4QrpQJUEXyAI3fY1KGVZxwAlm2sK+3eYPgGwCg0Aymp0fdDqgMgejqBPG+IKOe50w4/CnIrXHOO0PSnTNSVY89yWOFdNW476NVaFtb1E/ipMyAm/wKE3FIiQAvu7s0n4ph0AQW/yEEmly56344YB+F22iwG5SCelMXNerhqn6h8Z9lYmP0MAfR4Vz1VfVbafAxYrQQCgwhnePn3F9x0I8Nt0uas0UVcSOk1bjjXMrBKcBwQzgNyrQEk2eo0eQjSci3IPoWgQxfqyABB7qHyj1J5xlVE8xVcMjcOzS9+dDa4MiQOvohiJjDhap6miq+moq67GlMw55PmIhPVpx0Usff+o/ysaksPyUCpKRnKE2ioTyZ6CAsPxa1aTUvg7JZ1R3g+pyoiA3VtHKF6fp8WOHhTp+KI6KuTeVdQGO2EAS+ROqZl99f+l/nmA5OGhlSshxE/H9gV48wxpfIhKiESYkAWwcMxEKhWYDmIMko3LUzRoag5hWKVIu6IyK/qwG2Afy8yxeiZpRSD4085AvvfoeQtxM4e5qA07psgweo0SpCK+On5GiIHjF9vTrx0yZGSgRoxA1DmVDHgmCARMFVa3cnPXZbm75/A6EvQiQ/0gGcjew6gLWI25uLyyhAjwg+fyAKZtW80XVGzKtsNXe3DYq+JKfMua/slFTUaU8iIlWqjL9fsUBoMHDcqMqUxQymRICu+MNKTfjHV+iR4qtPehhr7YyRIQX/SsOtui/U8tC/ah7rSJQB2sXNYRigyJhdn5dF3WaU+15QIu4eHIQ5DcLpl91d2q3jp6mG0DAS/OclMRT1nZt52B6puQXvb9hNtIzXJnDBHK8i4xNNH0ne9vyNFHlVJC36OVL0I5ApWC6NJ4hKVCzjmlmHfkHV9wHE3hJLJY3gt3xpck7X7eg5jOsms8r3MBIp83umRID1NfU1oOwBEp4v25dqvrRwfXnSQaTBIE2dKx+EjBPdcwuZD9Aoki6iw0CMmX9fdFSwG+ZGABsTOH+oCObMe6Dym123pWcgpZfCJOSZUCAT1JTNHUmJANcGR4ZUUU4k0JM6cNVnuA3o1NKxa2Zlf7FmVvRebCZjwHZD+UgVXV8yQ3EKA4GTo8qcV/AJRF5CYos8jlWaH8xeu/OoLtvSI0iAkITC2Uj0A7oSe9mq5FRlROUnCk1mtfgRBKZc/ZvKlHTr18xiA6DvCtH12zAx1hg9sai4JDpG0ISeJfBMYllgug/ONVcV7xyRiuvrTpSar0BCz62ugU9TuKRLygQo1G0Ak1uijPpNUL83f31qIi1UuRXA1hRk5YA4rb/0OjXy4D2XD92uov8LHlg2LiYGF0B8185fW3nQzh8JPl8+ADBHAOifUMUIwuy6t6GFlAnQcfPeAzTJTVEYAPRSRwMpmX9A32dbAL7TXsxgsih0HISntm4F9/v3P6fEH0DED6gQ+BTuReqYhVf+pmenOyZcZypHAZKwR0JVQyRTtjB7ygS4ah7r1MEriOcva1MDGARiwTW/rfheV21YM2tcgxHzelyfXUIVwwDAr2X7+0T59dbOGBmCI48S+lSCOaUjjNkMy43zHtyeMvdFKgg+XxIgcSKhY5M4rQYxpiokS0oDO5XuJgr/nvyJOJyiNy16vLLLLaGYupdU+VIqrofESUbNuVf/Zk9u5PG7L877QClPoHHJtPg48AE6UyRt1VXrKrp9ok+i+EzGsaL4KjSZtbRZ6SpTtgxwSgW4ZtbgSqi+xjbLnCV0YYeqmpWLHiv/n0Xrd3W6l3X3FcOrQXyMxijqruIIMNU1+89s/UXYfPYHQtYyiSgcQs8ldOXV6yompsC2LnHTxh3D1XEugGiyQRRbjaspWxg95fOCQ1/I7xX6GGCSj1Am8yBcTr95ZtHvy8/s7JijGNmoCfnsErEJhwo5tXVvfc2scQ3MkDUg1gBM/FqJ01TM2vm/Lp1/VXFJ/5TYmCQ3bawcbMQ/B8D5msz+eWQDyBJ/Zrjr4+5NpFyADyzK2yuQZ4BO3Ipb0JMd5e9Gp1f98fqnK8659smKpLZb+NXsnPcdkZfJLsYINkHoNwH3/Na99VUX5OxRw/uh5vEkcxxOyEonPeOhhb/tuVty8PmSwI1/Lj9GieVQzIEm1vNtRlU/UeO+tXDMgJQ9A3bbdq1XrKm4lNQgiUGUxu1PW/5GvIc0nh/1fdt0H0HwgoCbjGs2l+/4bOuaWeNitjpX3FU+Fj5dAmBai3nNe3Ig2e1aG20gGfzZ+XltJnVffm/Z0Y4gSPC8hPKM/lwK4AGFc8/K83NS1rI0EwyqBE6p7lNH91gAPwA5lcCA6F3gI+xi014UbMde4TqGG5Zee0rBJwf9julFxRrIbKi4hoJFFPTuogBBQYuAKBoiWU7RShJ7SRa7E3PWBRm9ksEV95ReCWIJyazmX7wLAgSIR6ES/Pn3c9sM+c1ZU3YGG/cuPinpMhp/9G2gbmspU1qli0CcVvk0pW9+Iy35qg9kBoBcCrMI9bcvuAQEaPRDiCz75+dZjzxWSDdVAuy2VT7XzmDo8geqHvQZdziBi5DKUZfG55ZhAJvcGnScF6q2A/hLZDo17p/o840HcH4qyjVAoSO6+6onKpb+4tt5OyO/u3NmwYY5D5SBRBBIfld2JYYQTcsBNwtOI96zrRAjKiTqtKiaas5DNcktG6MIw+HftaF+c6q3pu3WFVLvvijncwPfTUo81J3lEGYciNNbr/J59xWDP6DibwDKOpl1FAIQan7kq3cvaW9hojsvKtggiiCgB912CF2Df1c1666bMKAT3o3YdPsi5Xddkv2pA3epgPfRTd0QTiTaGOQ6LS3cNrghzQk8Cuj61JXGAOhc1q9v5SXt9dJ/eVHBBlVZTOCF7rjWnobAFqh7b4n/001dz60tPbJNwy9mDNyqYfNjdXArofEjbzvHEXB16rIXq0dGHrz9kn67jDobAL6euqJ0EBzMH2Yq2x29WXVR3ouGsgjAs910rT3FDqO6JlRX87s148Z1euJ/LHpsx/RfXlpQtqNX3g0wuARIcDA/WajTHYantz7sq9v9F5JPUbVrk9cjUYwmccPCRyvafb5cVZT7D4FcSuoaJB4nedCgwAcgfhJgzr3BSd23KXePCRBonAh9e1HB7+G4P1ToPSASmAiTDOylwuk/fani7Mijq+aNrvP5fOsgTOGtGCBxGIjZ164vm9ze97cX5W5noP4qQ7MAGiea+mDBNfUA/1dULqvrm/3wgpO7uOJEHLrNDZMICx4pO1nABRBOJtE3UTeMNLkc2nXrECB5v+NDcNGJOVHhYVfcW/pNoSwBcEwn3TARnxnZQ30fosHbvp3/aEfXOn9d+RSFBkmOb7fM5v2CY7lHEnLvsJV7p/F9bLdL418ltgm02KFz3+IJ2fHmQndZN0APt4CtWXl+weafnZ//HbhmAoBfEvysy5kCIPF9YzDztn9HbzuQ36/gWQAPMkW94pbygMMF/NnCJyou6SjN7Rfm/8mh/7sA7wG6vIxIqiml6n0O8Z0G950b44kvlXjaArZm/vovsgNSdyKE51LMFFCGd6oFFALCEsAs/Z+Tch+MLOOyu0vz0/xcosLLI03vSgvIA+dVElztGnPnbYX5pe1fpXL+ryvPJnUJ0biptjctoIZIblGah4y6T1Xtr9m56qzRCW/AeNCPhHSV6evVGe2rPoJ+91RAj6PgSDoykkCBCCSeAJuOvwpg6eKTcqJ6o3NXlx0KH5eAuDClAmTjposO8DSIG1ecl/fPjq5vwSNVQ2EaZlN4Ecm8bhagAbWWilIIt5D4C3zYaMzeks52MP6/F2BHLHmmqq+R8EiSg8RhPwL9xEFvkukU9YEQQBpbSQEIvJqmtS8sOHlo1MP03PvKj1HhRFHTr/GMJpreRK2Z3873EGlzTCLOEeGb6XBeiLdr+cLfVhyrwEQl+rbJL6q8tja0+z1hQNaDJkQj1Y7oZ+rD9vSQv+LQ2n57ClM0knFQCdBi6SyedkIsFitAi6dYAVo8xQrQ4ilWgBZPsQK0eIoVoMVTrAAtnmIFaPEUK0CLp1gBWjzFCtDiKVaAFk+xArR4ihWgxVOsAC2eYgVo8RQrQIunWAFaPMUK0OIpVoAWT7ECtHiKFaDFU6wALZ5iBWjxFCtAi6dYAVo8xQrQ4ilWgBZPsQK0eIoVoMVTrAAtnmIFaPEUK0CLp1gBWjzFCtDiKVaAFk+xArR4ihWgxVOsAC2eYgVo8ZT/B3UNw+ppWIYKAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI0LTExLTA0VDAyOjI0OjE0KzAwOjAwP7eKFgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNC0xMS0wNFQwMjoyNDoxNCswMDowME7qMqoAAAAodEVYdGRhdGU6dGltZXN0YW1wADIwMjQtMTEtMDRUMDI6MjQ6MTYrMDA6MDCOYAJcAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAABJRU5ErkJggg==" /></svg> diff --git a/ui/public/imgs/providers/gcore.png b/ui/public/imgs/providers/gcore.png new file mode 100644 index 00000000..3808329a Binary files /dev/null and b/ui/public/imgs/providers/gcore.png differ diff --git a/ui/public/imgs/providers/gname.png b/ui/public/imgs/providers/gname.png new file mode 100644 index 00000000..ef3772f5 Binary files /dev/null and b/ui/public/imgs/providers/gname.png differ diff --git a/ui/public/imgs/providers/gname.svg b/ui/public/imgs/providers/gname.svg deleted file mode 100644 index ec409ca5..00000000 --- a/ui/public/imgs/providers/gname.svg +++ /dev/null @@ -1 +0,0 @@ -<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="2000" viewBox="0 0 81 81" enable-background="new 0 0 81 81" xml:space="preserve"><image width="81" height="81" x="0" y="0" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAMAAACdUboEAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAIoUExURf///wAAAPD1/5i7/5e6/5W7/4+2/5O4/5a5/5W6/5S4/5e7/5G3/5O5/5S5/46y/5K3/0N9/4av/3il/4au/4St/9zq/4Cq/3qn/32o/3mn/3mm/3Oi/2Wa/26f/2ib/2eZ/1uR/2GW/16U/1mR/////0+L/1CK/0eE/ily/0aC/0WC/0CA/z1+/i1z/S1y/i5z/i1y/i1z/h9o/Rto/xNi/xJf/Axb/gta/ghY/wBT/ABK/wBS/QBO/QBK/QBI/wBV/wBI/QBK/wBI/QBD/wBI/QBA/QA+/QA9/AA//AA//QBA/wA9/QA8/QA7/QA7/ZK3/5C2/4+1/42z/o20/4qy/4iw/4ev/oWu/4Ks/4Gs/n+q/3yo/3yo/nmm/3ak/3Sj/3Ki/3Gg/m6f/2ud/2mb/2aZ/mOX/mCV/l2T/l2T/avG/s3d/t/p/u70/vj6/vz9/vT3/uTt/tPi/sja/rPM/luS/lqR/Zi6/sHW/unw/v///1iP/trn/qHA/lWN/lOM/lKL/k6I/lCK/kqG/kmF/keE/nOg/brR/kWC/kSC/UGA/j9+/jx9/jt8/jl6/Td5/jV3/TF1/TB0/S5z/Stx/Sxx/cTX/pS3/Shv/SVt/SZu/SNr/Xum/SJr/R9p/Rxm/Rlk/Rdj/RVi/RVh/BJg/Q9e/Q9d/Axc/Qla/Apa/QZY/QNV/QFU/ABS/ABQ/ABN/ABL/ABI/ABG/ABE/ABB/AA//AA9/KpbnBIAAABQdFJOUwAACD5njrLW3+z5jjJ/wR98D5JR5+cFzC3lPfPqH8yS5zL8taz+fdnBCfk/Z4610t/s+Y5nOsF92SCxL/yS5lEGzB/vO/aR6rms/iB92flnU7z0NAAAAAFiS0dEAIgFHUgAAAAHdElNRQfpARcTJwjHT4ECAAACQ3pUWHRSYXcgcHJvZmlsZSB0eXBlIHhtcAAAOI2VVUuy4zAI3HOKOYIMCKTjOJG1m6pZzvGnwXF+zy81L6pEtgR00yCF/v7+Q7/i401JrjK9ebHFxC5WXbkYWzW3bpsM5m1eLpfJjPVuGivVpeqQosOLCmybddLmq8Oxiq+6VTXMCCgCJ2aZsnGRqzdZvRkcbQSYLVzi3a62ucQeBQLYqM3gIeu+cTdPJo8wWLuEh949uNSmoxbiIDc9l6TzJgsP8CkYQBWPNVukISnFWxXB3PmKPZb4Ck/MQpjiZcgCkxVLYAVThH0bfEuSwcVkrayq9kiQMsN9M5JsrhhFViQ1PT+8OYzAK3g7gBfpMZIJJxHmQXsQHi6OKoUu3pAclIr9VxaggIKhHGw99erQCRaxT2EAsRl4kDdY7fI+VyRk/so3wbajUASRN3TQtIFUGviUIA/Ry1Gx95A60ENfwtJr3E9hEXCcBBfXCj86E/n/g0eLO/RCIJ4RzyU1YjjUeIf8cmBHeA2BK4Jo1K4GwLTrc2tS9mbBaZlpJPlUst0UqOgUbjXPXTigG4T7AyL1WrVnH0XJLchX7Zp1242QUM9juirLgh3F3CQfpKugnzAU9BSdT/jBera7Z6tZOjVp8HtHjiTEa5zE6LlnYEpkPUXWM+TvgGlHPi6LF/SaHWxAxwueo27lKHc2cpyFmzT0A22WTwzpW22ekT9oc0hDJ8j8E21uwJN2dXh+PuHvVvttdF/FDUqvN/G+dfJ3UKMhMx3eb3L6B6VQbf3a9cBCAAAFuklEQVRYw9WZ+1cTRxSAXbWgFEWgRVQQUUHxiQ9EifKMgApYCDXFahWUKPJIwiuBCIUQlWcgCQjFColSqaBECcK/19mdmd3J7uwmIT94+p05mTt37/1OQjaT3bBly/8CRomt27b/EBG5Y2dU1M4dkRE/bt+2lQmAojF61+6oX0RE7d4VvUljzJ7ISpaqyiowKsk4ck9M6MboCE11dbWmWgNGNQcZV2siokMzRkf8GhA5J80YG3dHewcMrZadWdiYxT8fFxukMf6n34Lk5/hgjAlxNTU1d2vugkGfWdiY5W5cQkBjzN7fQ2JvTABj4r57LPfv3QfDf2ahxPsSFY37D/wRMgf2Kxj3H3iwCURK0piY9FBE7cNaDhBI8kIuKVHGmJxUW1dbJwEkJXkyB4KkZKox4eCjTXMwgWZMeRwGKRRjfH1YxEuMsYd0YXEoVmxMeRImKSJj6uGnYXI41d94pIHjWcMzMBpI6jWNTc0ter3B2NrW3lFVJ6nD8RE/Y2qnydRp6uQwoRg8PDF3dVtEGJvMOlqtqTOVNB59LqWzqklvoZLW09tAaThKGJP/lPCow2hRwNjxWNqTLBjT+xD9ff0cfb0GSwAM5n5YDx4Q6YLxmHXAyjFgHeCw6gMJLZYWtm4A9sD+Y7wx1Soh4FO0WPRWm81q8+tKxcb0F7YXNjBeCLPIaOhu7Wlra+4mn7rBBiH60rHx+EsJZGuzWWfD+f5X5sFWlJd2HUfGjBNDw0PDYAxxM4fwRhvMQ0SeQ9fLStNQDveBhxMZ0HhyREoLb7wzQkPXYTRQ0ieh8dSoFPzKLIOjMrzUUpKnoPH02Jh9zM4DFmP2Zvyan8P1GHGMiDE4dxoaz4xz2MftYMB4vB0ZmyjHZGBrzkDj2YkJx4SDBywmHI3I2AuPsTkWcczXo/gsNGY6/HA6nE6nBhk1TrCEuRFtV6uxpa2x3gFzNDKh8ZzL5XQ5OUAAYx0ymp0YDX+K9rwS6sgewDloPD8pxYn6G9Ha1UF+hHpMk3TOI+PU1Oup12BMccB4GvWi3Ix443ERtcKMjOf+oqBFvWPcalSyFzVN0brQq86cnX0z+waMWYJxtFfMcCutWGhJmySrcT96Z87+TWMQ7YJT7GLy6duZNj9jF7UJnT1n5ubm5+bFPEOtb3FiblQjfNotJr4HBHyMzvALbve8ex4MYWZpQk/SgdaA1/jEt7TBhLjvAjRe9HC4PW4/nqPmaeKYB790HZFzC/0XoTHLw/PO806gC3VXCsfwO9T+TqgFAU8W2nE97xfeL3CAgIvZ+b0Tb7szOP8gDX3F2FEd2QPwoB2XufQPnUf4r9brYZce/jTXyDRcwt8z2R8WPyzygAW3Bg+D/MduZHHR1sOf3Qu4DoP6s7Ex618ZZvmt3NDYmIbj7lm5+iz+CuDyx6WPEpY+Li25iDMQo7cvLZH1bB1cXxauKbKX5RiXXP3o+2SLswXjlU8ilj8tg8FGwyKlcRjmKSxfIa72cj7L4mwmhd1O+coc8vpRtSLPwrQgnHYrFKr8rppzVla8K14w4EEcc/MnDdrZDG8/gzULWYfXOf7X4aqrXgUc3K7R7lKquaoS3X1c8yryqrW5XrnimuQOKfcLgfeLFwxhJnP0OFdyh8TkfQmLPMqd5rWvYcC/ZtKYX7B5YUE+9Y69MDdk0+rXVTC+5hbSfwNgVEWrm6JIxcgYGfX1VZ9v1SfU+tDaR+R9oprV62pG1sioi3whU+QvFP+CpMpdw/jWYAcI+DUb4xmRq2IUjUxhwVpIFBQyAYxMfnEowuJ8JqARfHpKvgVJSR7DBGNkYovXUMv6t3Uw6Lr1teJYJkgjeINK1wNSqmKY4I3AeeMm6NpY3+AAARezM8vGzRsyPqVfr2+VlW/IUF52i2FCNwLUZaUVYltFaZmaUSLQfwEy1LeLS8vLSyoqSsrLS4tvqzOYAHzvf2p8H/4DdgKFYpWWqwYAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjUtMDEtMjNUMTk6Mzk6MDcrMDA6MDDXya2EAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDI1LTAxLTIzVDE5OjM5OjA3KzAwOjAwppQVOAAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyNS0wMS0yM1QxOTozOTowOCswMDowMAfJRA4AAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAAAAElFTkSuQmCC" /></svg> diff --git a/ui/public/imgs/providers/godaddy.svg b/ui/public/imgs/providers/godaddy.svg index a859a7ae..f9471283 100644 --- a/ui/public/imgs/providers/godaddy.svg +++ b/ui/public/imgs/providers/godaddy.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5245" width="200" height="200"><path d="M683.52 924.16c69.632-32.768 165.888-91.648 245.76-194.56 20.48-26.112 37.376-52.224 51.2-76.8 12.8-31.232 27.648-76.8 35.84-133.12 18.432-127.488-12.288-222.208-20.48-245.76-12.8-37.376-29.696-80.896-71.68-122.88-65.536-65.536-145.408-78.336-168.96-81.92-57.856-8.192-103.424 2.048-138.24 10.24-23.552 5.632-68.096 16.384-117.76 46.08-39.424 24.064-64.512 48.64-81.92 66.56-48.64 49.152-73.216 95.232-107.52 158.72-20.48 37.376-37.376 70.144-51.2 117.76-2.56 9.216-9.216 32.768-15.36 76.8-6.144 41.984-11.776 101.376-10.24 175.104-50.176-82.432-79.36-154.112-97.28-205.824-15.872-46.08-22.016-74.752-25.6-102.4-7.168-56.832-13.824-110.08 15.36-158.72 38.912-64.512 116.224-78.848 138.752-83.456 95.232-17.408 169.984 29.696 188.928 42.496 27.136-24.064 54.784-47.616 81.92-71.68-31.744-25.088-89.088-62.464-168.96-76.8-16.896-3.072-57.856-9.216-109.056-4.096-39.424 4.096-96.768 9.728-152.064 50.176-11.776 8.704-46.08 35.328-71.68 81.92-38.912 71.168-32.768 142.848-25.6 230.4 3.584 44.032 10.24 79.872 15.36 102.4 34.816 125.44 86.528 210.432 122.88 261.12 29.184 39.936 51.2 61.952 57.856 68.608 27.648 27.136 95.232 91.136 203.264 115.712 31.744 7.168 98.304 21.504 179.2-1.536 27.136-7.68 99.84-29.184 155.648-96.256 76.288-91.136 69.12-202.752 64-270.848-4.608-71.68-24.576-115.2-30.72-128-20.992-43.52-47.616-73.728-66.56-92.16-80.384 32.256-160.256 65.024-240.64 97.28l34.816 86.528 164.864-71.168c13.824 24.064 34.304 67.072 35.84 122.88 2.56 92.16-46.592 203.264-143.36 240.64-50.688 19.456-131.584 25.088-179.2-20.48-28.672-27.136-33.28-61.44-40.96-117.76-4.096-28.16-12.8-111.104 15.36-215.04 10.24-37.888 34.816-113.152 92.16-189.44 33.28-44.032 62.976-69.632 71.68-76.8 27.648-23.04 57.344-47.616 100.864-61.44 16.896-5.12 99.328-28.672 178.688 17.92 67.584 39.424 90.112 104.96 99.328 130.048 13.824 38.912 14.336 70.656 15.36 97.28 0.512 26.624 2.048 84.992-25.6 153.6-24.064 59.392-58.368 99.84-81.92 122.88-40.448 75.776-81.408 150.528-122.368 225.792z" fill="#13EAE4" p-id="5246"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5245" width="200" height="200"><path d="M683.52 924.16c69.632-32.768 165.888-91.648 245.76-194.56 20.48-26.112 37.376-52.224 51.2-76.8 12.8-31.232 27.648-76.8 35.84-133.12 18.432-127.488-12.288-222.208-20.48-245.76-12.8-37.376-29.696-80.896-71.68-122.88-65.536-65.536-145.408-78.336-168.96-81.92-57.856-8.192-103.424 2.048-138.24 10.24-23.552 5.632-68.096 16.384-117.76 46.08-39.424 24.064-64.512 48.64-81.92 66.56-48.64 49.152-73.216 95.232-107.52 158.72-20.48 37.376-37.376 70.144-51.2 117.76-2.56 9.216-9.216 32.768-15.36 76.8-6.144 41.984-11.776 101.376-10.24 175.104-50.176-82.432-79.36-154.112-97.28-205.824-15.872-46.08-22.016-74.752-25.6-102.4-7.168-56.832-13.824-110.08 15.36-158.72 38.912-64.512 116.224-78.848 138.752-83.456 95.232-17.408 169.984 29.696 188.928 42.496 27.136-24.064 54.784-47.616 81.92-71.68-31.744-25.088-89.088-62.464-168.96-76.8-16.896-3.072-57.856-9.216-109.056-4.096-39.424 4.096-96.768 9.728-152.064 50.176-11.776 8.704-46.08 35.328-71.68 81.92-38.912 71.168-32.768 142.848-25.6 230.4 3.584 44.032 10.24 79.872 15.36 102.4 34.816 125.44 86.528 210.432 122.88 261.12 29.184 39.936 51.2 61.952 57.856 68.608 27.648 27.136 95.232 91.136 203.264 115.712 31.744 7.168 98.304 21.504 179.2-1.536 27.136-7.68 99.84-29.184 155.648-96.256 76.288-91.136 69.12-202.752 64-270.848-4.608-71.68-24.576-115.2-30.72-128-20.992-43.52-47.616-73.728-66.56-92.16-80.384 32.256-160.256 65.024-240.64 97.28l34.816 86.528 164.864-71.168c13.824 24.064 34.304 67.072 35.84 122.88 2.56 92.16-46.592 203.264-143.36 240.64-50.688 19.456-131.584 25.088-179.2-20.48-28.672-27.136-33.28-61.44-40.96-117.76-4.096-28.16-12.8-111.104 15.36-215.04 10.24-37.888 34.816-113.152 92.16-189.44 33.28-44.032 62.976-69.632 71.68-76.8 27.648-23.04 57.344-47.616 100.864-61.44 16.896-5.12 99.328-28.672 178.688 17.92 67.584 39.424 90.112 104.96 99.328 130.048 13.824 38.912 14.336 70.656 15.36 97.28 0.512 26.624 2.048 84.992-25.6 153.6-24.064 59.392-58.368 99.84-81.92 122.88-40.448 75.776-81.408 150.528-122.368 225.792z" fill="#13EAE4" p-id="5246"></path></svg> diff --git a/ui/public/imgs/providers/huaweicloud.svg b/ui/public/imgs/providers/huaweicloud.svg index 552e59e7..45a42dd2 100644 --- a/ui/public/imgs/providers/huaweicloud.svg +++ b/ui/public/imgs/providers/huaweicloud.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1027 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4252" width="200" height="200"><path d="M378.88 143.36c20.48-6.826667 40.96-10.24 61.44-13.653333 23.893333 30.72 30.72 71.68 40.96 109.226666 6.826667 40.96 13.653333 81.92 13.653333 122.88 6.826667 27.306667 3.413333 58.026667 3.413334 85.333334s-3.413333 51.2 0 78.506666c0 37.546667-3.413333 71.68-3.413334 109.226667-6.826667 23.893333 0 47.786667-6.826666 71.68-10.24-3.413333-13.653333-13.653333-17.066667-20.48-40.96-61.44-78.506667-122.88-112.64-187.733333-34.133333-68.266667-68.266667-136.533333-71.68-215.04-6.826667-61.44 34.133333-119.466667 92.16-139.946667z m211.626667-10.24c6.826667-3.413333 10.24 0 17.066666 0 27.306667 6.826667 58.026667 10.24 81.92 27.306667s44.373333 40.96 51.2 68.266666c10.24 34.133333 6.826667 71.68 0 105.813334-10.24 44.373333-30.72 85.333333-51.2 126.293333-6.826667 13.653333-13.653333 23.893333-20.48 37.546667-10.24 23.893333-27.306667 47.786667-40.96 71.68-23.893333 40.96-47.786667 75.093333-71.68 116.053333-3.413333 3.413333-6.826667 13.653333-13.653333 6.826667-3.413333-40.96-6.826667-81.92-10.24-126.293334-6.826667-54.613333-3.413333-105.813333-3.413333-160.426666 3.413333-34.133333 3.413333-71.68 6.826666-105.813334 6.826667-40.96 13.653333-85.333333 27.306667-126.293333 13.653333-10.24 13.653333-30.72 27.306667-40.96zM160.426667 266.24c3.413333 0 6.826667 6.826667 10.24 10.24 98.986667 129.706667 187.733333 266.24 259.413333 413.013333 6.826667 10.24 13.653333 23.893333 13.653333 37.546667-13.653333-3.413333-23.893333-10.24-34.133333-17.066667-64.853333-34.133333-129.706667-71.68-194.56-109.226666-23.893333-17.066667-47.786667-34.133333-68.266667-51.2-40.96-27.306667-68.266667-78.506667-64.853333-129.706667 3.413333-61.44 37.546667-112.64 78.506667-153.6z m706.56 0h6.826666c17.066667 23.893333 40.96 47.786667 54.613334 75.093333 13.653333 23.893333 20.48 54.613333 23.893333 81.92 0 30.72-10.24 64.853333-34.133333 88.746667-13.653333 13.653333-23.893333 27.306667-40.96 37.546667-78.506667 61.44-163.84 109.226667-252.586667 153.6-13.653333 6.826667-23.893333 17.066667-40.96 17.066666 3.413333-17.066667 13.653333-34.133333 20.48-47.786666 58.026667-119.466667 129.706667-232.106667 208.213333-341.333334 17.066667-17.066667 37.546667-40.96 54.613334-64.853333z m-856.746667 273.066667c3.413333-3.413333 0-10.24 6.826667-13.653334 10.24 3.413333 20.48 10.24 27.306666 13.653334 122.88 68.266667 245.76 136.533333 365.226667 211.626666 3.413333 3.413333 6.826667 6.826667 6.826667 10.24H180.906667c-47.786667 0-92.16-20.48-126.293334-54.613333-27.306667-30.72-51.2-71.68-54.613333-112.64 6.826667-17.066667 3.413333-34.133333 10.24-54.613333z m983.04-3.413334c6.826667-3.413333 17.066667-10.24 23.893333-6.826666 0 17.066667 6.826667 37.546667 6.826667 54.613333-3.413333 23.893333-3.413333 44.373333-13.653333 64.853333-6.826667 17.066667-17.066667 37.546667-30.72 51.2-17.066667 13.653333-27.306667 30.72-47.786667 40.96-20.48 17.066667-51.2 20.48-75.093333 23.893334h-245.76c3.413333-3.413333 3.413333-6.826667 6.826666-10.24 122.88-78.506667 249.173333-150.186667 375.466667-218.453334zM184.32 798.72c44.373333-3.413333 88.746667 0 133.12-6.826667 30.72 0 64.853333-3.413333 95.573333 0-6.826667 13.653333-23.893333 20.48-34.133333 27.306667-34.133333 23.893333-68.266667 44.373333-105.813333 61.44s-81.92 10.24-112.64-13.653333c-23.893333-17.066667-44.373333-44.373333-58.026667-68.266667h81.92z m433.493333-6.826667c30.72-3.413333 61.44 0 95.573334 0 40.96 3.413333 85.333333 0 129.706666 6.826667 30.72 3.413333 61.44 0 88.746667 3.413333-10.24 20.48-27.306667 40.96-44.373333 58.026667-27.306667 27.306667-68.266667 40.96-105.813334 34.133333-34.133333-10.24-61.44-30.72-92.16-47.786666-17.066667-10.24-30.72-20.48-47.786666-30.72-10.24-10.24-17.066667-13.653333-23.893334-23.893334z" fill="#C71F1E" p-id="4253"></path></svg> +<svg viewBox="0 0 1027 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4252" width="200" height="200"><path d="M378.88 143.36c20.48-6.826667 40.96-10.24 61.44-13.653333 23.893333 30.72 30.72 71.68 40.96 109.226666 6.826667 40.96 13.653333 81.92 13.653333 122.88 6.826667 27.306667 3.413333 58.026667 3.413334 85.333334s-3.413333 51.2 0 78.506666c0 37.546667-3.413333 71.68-3.413334 109.226667-6.826667 23.893333 0 47.786667-6.826666 71.68-10.24-3.413333-13.653333-13.653333-17.066667-20.48-40.96-61.44-78.506667-122.88-112.64-187.733333-34.133333-68.266667-68.266667-136.533333-71.68-215.04-6.826667-61.44 34.133333-119.466667 92.16-139.946667z m211.626667-10.24c6.826667-3.413333 10.24 0 17.066666 0 27.306667 6.826667 58.026667 10.24 81.92 27.306667s44.373333 40.96 51.2 68.266666c10.24 34.133333 6.826667 71.68 0 105.813334-10.24 44.373333-30.72 85.333333-51.2 126.293333-6.826667 13.653333-13.653333 23.893333-20.48 37.546667-10.24 23.893333-27.306667 47.786667-40.96 71.68-23.893333 40.96-47.786667 75.093333-71.68 116.053333-3.413333 3.413333-6.826667 13.653333-13.653333 6.826667-3.413333-40.96-6.826667-81.92-10.24-126.293334-6.826667-54.613333-3.413333-105.813333-3.413333-160.426666 3.413333-34.133333 3.413333-71.68 6.826666-105.813334 6.826667-40.96 13.653333-85.333333 27.306667-126.293333 13.653333-10.24 13.653333-30.72 27.306667-40.96zM160.426667 266.24c3.413333 0 6.826667 6.826667 10.24 10.24 98.986667 129.706667 187.733333 266.24 259.413333 413.013333 6.826667 10.24 13.653333 23.893333 13.653333 37.546667-13.653333-3.413333-23.893333-10.24-34.133333-17.066667-64.853333-34.133333-129.706667-71.68-194.56-109.226666-23.893333-17.066667-47.786667-34.133333-68.266667-51.2-40.96-27.306667-68.266667-78.506667-64.853333-129.706667 3.413333-61.44 37.546667-112.64 78.506667-153.6z m706.56 0h6.826666c17.066667 23.893333 40.96 47.786667 54.613334 75.093333 13.653333 23.893333 20.48 54.613333 23.893333 81.92 0 30.72-10.24 64.853333-34.133333 88.746667-13.653333 13.653333-23.893333 27.306667-40.96 37.546667-78.506667 61.44-163.84 109.226667-252.586667 153.6-13.653333 6.826667-23.893333 17.066667-40.96 17.066666 3.413333-17.066667 13.653333-34.133333 20.48-47.786666 58.026667-119.466667 129.706667-232.106667 208.213333-341.333334 17.066667-17.066667 37.546667-40.96 54.613334-64.853333z m-856.746667 273.066667c3.413333-3.413333 0-10.24 6.826667-13.653334 10.24 3.413333 20.48 10.24 27.306666 13.653334 122.88 68.266667 245.76 136.533333 365.226667 211.626666 3.413333 3.413333 6.826667 6.826667 6.826667 10.24H180.906667c-47.786667 0-92.16-20.48-126.293334-54.613333-27.306667-30.72-51.2-71.68-54.613333-112.64 6.826667-17.066667 3.413333-34.133333 10.24-54.613333z m983.04-3.413334c6.826667-3.413333 17.066667-10.24 23.893333-6.826666 0 17.066667 6.826667 37.546667 6.826667 54.613333-3.413333 23.893333-3.413333 44.373333-13.653333 64.853333-6.826667 17.066667-17.066667 37.546667-30.72 51.2-17.066667 13.653333-27.306667 30.72-47.786667 40.96-20.48 17.066667-51.2 20.48-75.093333 23.893334h-245.76c3.413333-3.413333 3.413333-6.826667 6.826666-10.24 122.88-78.506667 249.173333-150.186667 375.466667-218.453334zM184.32 798.72c44.373333-3.413333 88.746667 0 133.12-6.826667 30.72 0 64.853333-3.413333 95.573333 0-6.826667 13.653333-23.893333 20.48-34.133333 27.306667-34.133333 23.893333-68.266667 44.373333-105.813333 61.44s-81.92 10.24-112.64-13.653333c-23.893333-17.066667-44.373333-44.373333-58.026667-68.266667h81.92z m433.493333-6.826667c30.72-3.413333 61.44 0 95.573334 0 40.96 3.413333 85.333333 0 129.706666 6.826667 30.72 3.413333 61.44 0 88.746667 3.413333-10.24 20.48-27.306667 40.96-44.373333 58.026667-27.306667 27.306667-68.266667 40.96-105.813334 34.133333-34.133333-10.24-61.44-30.72-92.16-47.786666-17.066667-10.24-30.72-20.48-47.786666-30.72-10.24-10.24-17.066667-13.653333-23.893334-23.893334z" fill="#C71F1E" p-id="4253"></path></svg> diff --git a/ui/public/imgs/providers/jdcloud.svg b/ui/public/imgs/providers/jdcloud.svg new file mode 100644 index 00000000..720dbf4d --- /dev/null +++ b/ui/public/imgs/providers/jdcloud.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 2030 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M1669.09384945 432.48813594c15.51186469 21.69491531 27.11864437 51.1457625 25.81694906 112.86779625-17.46440719 193.78983094-181.96610156 316.14915281-345.76271156 316.14915281-177.24745781 0-302.04745781-127.56610125-377.111865-270.10169531C954.30062883 557.72203344 941.44639195 520.13559312 929.35147664 487.59322063c-23.701695 32.21694937-73.6542375 84.77288156-99.09152531 109.23389812A629.15254219 629.15254219 0 0 0 890.24639133 720.81355906c20.01355969 31.18644094 70.508475 101.74915219 142.69830562 157.61355938 65.62711875 50.60338969 128.59661062 81.68135625 199.81016906 99.41694937a487.81016906 487.81016906 0 0 0 354.65762719-50.00677968c128.81355937-73.81694906 219.60678-209.78983031 236.04067781-350.96949188-31.72881375-52.61016937-100.501695-114.76610156-154.35932156-144.37966031zM585.37859508 381.12542375a238.96949156 238.96949156 0 0 1 126.96949125 36.39322031c30.53559281-32.5423725 57.54576281-66.16949156 85.58644125-100.06779656A370.54915219 370.54915219 0 0 0 214.66673039 621.01694937a373.09830469 373.09830469 0 0 0 4.39322062 57.00339c10.3593225 12.79999969 114.71186438 4.12203375 130.98305063-10.52203406a242.49491531 242.49491531 0 0 1-4.55593219-46.48135594 239.89152563 239.89152563 0 0 1 239.89152563-239.89152562zM585.37859508 992a370.6576275 370.6576275 0 0 1-320.97627094-185.38305094c4.98983062-6.61694906 121.60000031-19.57966125 149.42372812-18.00678a239.07796594 239.07796594 0 0 0 171.55254282 72.29830594s147.52542375 11.33559281 285.39661031-97.62711937c0 0 51.85084781 73.87118625 91.66101656 101.64067781 0 0-130.00677938 127.07796656-377.05762687 127.07796656z" fill="#E1251B"></path><path d="M1545.05317101 74.305085C1369.9209682 0.70508469 1199.0735107 22.18305125 1030.23283195 146.27796594 881.35147664 255.67457656 822.1243582 365.88474594 726.88367976 460.47457625c-113.89830469 113.13898313-199.32203344 151.21355906-267.06440718 176.81355938-69.96610125 26.46779625-167.59322062 44.52881344-240.75932157 40.51525406a371.41694906 371.41694906 0 0 0 45.34237313 128.59661062c186.79322063-3.19999969 318.48135562-74.68474594 403.41694875-126.48135656 74.2508475-45.28813594 185.97966094-159.34915219 244.61016937-233.22033844 94.21016906-118.56271219 216.94915219-231.97288125 329.16610219-267.55254281 156.7457625-49.62711844 309.1525425 19.79661 383.02372875 117.85762688 6.61694906 8.62372875 145.13898281 58.3593225 188.691525 95.78305125C1797.31079883 324.88135625 1721.8124932 148.61016969 1545.05317101 74.305085z" fill="#E1251B"></path><path d="M1809.24300164 377.81694875A497.35593188 497.35593188 0 0 0 1526.39554383 286.91525469c-122.63050875 0-245.42372906 47.78305125-331.82372813 132.88135594-62.86101656 61.83050812-93.4508475 112.00000031-163.14576281 195.57966093a570.35932219 570.35932219 0 0 0 92.63728781 108.47457563c9.00339-13.12542375 59.66101688-84.82711875 97.62711844-135.21355875 48.81355969-64.10847469 98.38644094-115.36271156 173.5593225-145.03050844a350.04745781 350.04745781 0 0 1 427.498305 139.3355925A486.02033906 486.02033906 0 0 0 1809.24300164 377.81694875z" fill="#E1251B"></path></svg> diff --git a/ui/public/imgs/providers/kubernetes.svg b/ui/public/imgs/providers/kubernetes.svg index b7f555f7..88125935 100644 --- a/ui/public/imgs/providers/kubernetes.svg +++ b/ui/public/imgs/providers/kubernetes.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1055 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4254" width="200" height="200"><path d="M448.633165 615.302071l0.296804 0.428717-43.92594 106.088828a228.574007 228.574007 0 0 1-90.722014-112.623666l-0.494673-1.582953 113.345064-19.226295 0.164891 0.230848a19.331412 19.331412 0 0 1 22.885844 19.032547 18.962468 18.962468 0 0 1-1.615932 7.749878l0.032978-0.131912z m-36.609931-93.591118a19.374696 19.374696 0 0 0 7.650944-33.242031l-0.032978-0.032979 0.103057-0.494673-86.236978-77.134993a223.736517 223.736517 0 0 0-33.472879 118.357751 229.961153 229.961153 0 0 0 1.451041 25.788957l-0.103057-1.022324 110.547069-31.889926z m50.323712-87.061433a19.340687 19.340687 0 0 0 30.735688-14.774237v-0.032978l0.428717-0.230847 6.595641-115.191843a226.003768 226.003768 0 0 0-132.373488 63.449038l0.032978-0.032979 94.415573 66.977706z m33.439901 120.897073l31.790991 15.334865 31.758012-15.268909 7.91477-34.297334-21.996464-27.404889h-35.348514l-21.996464 27.404889z m65.956412-136.067048a19.346871 19.346871 0 0 0 30.834623 14.737136l-0.065956 0.032978 0.36276 0.131913 93.821966-66.516011a227.423892 227.423892 0 0 0-130.427775-63.284147l-1.121259-0.131913 6.492584 114.993974z m478.215936 262.503428l-253.82913 315.766323a70.270374 70.270374 0 0 1-54.709813 26.11874h-0.164891l-407.211797 0.131912h-0.065956a70.181745 70.181745 0 0 1-54.676835-26.085761l-0.103057-0.131912L15.238734 682.081908a69.615962 69.615962 0 0 1-13.389151-59.393749l-0.103057 0.461695L92.34075 229.395226a69.556189 69.556189 0 0 1 37.957915-47.190782L497.111128 6.893331a70.697029 70.697029 0 0 1 61.305455 0.19787l-0.428717-0.19787 366.949529 175.211148a69.626268 69.626268 0 0 1 37.957916 47.191813l90.656058 393.755658a69.254233 69.254233 0 0 1-13.554043 58.931023z m-144.607373-90.458188c-1.84678-0.428717-4.518014-1.154237-6.364793-1.484019-7.650944-1.451041-13.850847-1.088281-21.073074-1.681889-15.400822-1.615932-28.064453-2.93506-39.343-6.492584-4.616949-1.751967-7.914769-7.255205-9.497723-9.497724l-8.838159-2.605278a276.357367 276.357367 0 0 0 2.671234-38.716414 284.359735 284.359735 0 0 0-7.650944-65.757512l0.395739 1.912736a284.83998 284.83998 0 0 0-41.783387-96.097462l0.626586 0.989346c2.275496-2.077627 6.595641-5.837142 7.782856-6.991379 0.36276-3.957385 0.032978-8.046682 4.122276-12.399806 8.673268-8.141495 19.523098-14.873171 32.681402-22.952831 6.232881-3.693559 12.004067-6.035012 18.241071-10.651961 1.418063-1.055303 3.330799-2.737191 4.843674-3.924406 10.553026-8.409443 12.960435-22.853897 5.408425-32.351621s-22.260289-10.388135-32.747358-1.978692c-1.484019 1.187215-3.528668 2.737191-4.880775 3.85845-5.903099 5.111622-9.530702 10.124309-14.51041 15.400822-10.820974 10.981743-19.786924 20.149684-29.577329 26.778304-4.254189 2.473365-10.520048 1.615932-13.323195 1.451041l-8.347609 5.936077a287.21338 287.21338 0 0 0-181.147224-87.984823l-1.154238-0.103057-0.527651-9.790405c-2.869104-2.737191-6.298837-5.049788-7.156271-10.981743-0.956368-11.77322 0.659564-24.502807 2.506344-39.804694 1.022324-7.156271 2.671235-13.088226 3.001017-20.875205 0.032978-1.751967-0.032978-4.353123-0.032978-6.232881 0-13.455108-9.860484-24.403872-21.996464-24.403872-12.103002 0-21.930507 10.948764-21.930507 24.403872l0.032978 0.626586c0 1.813801-0.103057 4.056319 0 5.639273 0.263826 7.782857 1.945714 13.718934 2.935061 20.875205 1.84678 15.301888 3.429733 27.998497 2.473365 39.837673a24.065846 24.065846 0 0 1-7.090314 11.311524l-0.032978 0.032979-0.527652 9.266875a282.443907 282.443907 0 0 0-183.05996 87.918867l-0.131913 0.131913a349.351534 349.351534 0 0 1-8.937094-6.331816l1.022324 0.721399c-3.957385 0.527651-7.914769 1.751967-13.059369-1.28615-9.790405-6.595641-18.764599-15.730604-29.577329-26.745325-4.979709-5.276513-8.574334-10.2892-14.477432-15.334866-1.319128-1.154237-3.400877-2.737191-4.880775-3.85845a25.943542 25.943542 0 0 0-15.235931-5.804165h-0.065956c-0.329782-0.032978-0.721398-0.032978-1.121259-0.032978a21.126663 21.126663 0 0 0-16.35719 7.7169l-0.032979 0.032979c-7.552009 9.497723-5.1446 24.012256 5.408426 32.417576l0.296804 0.230848 4.583971 3.66058c6.232881 4.616949 11.971089 6.99138 18.203969 10.651961 13.158304 8.141495 24.012256 14.873171 32.681403 22.952831 3.330799 3.594624 3.957385 9.92644 4.386101 12.663632l7.024358 6.298837a281.617391 281.617391 0 0 0-47.751412 157.666742 285.194495 285.194495 0 0 0 3.099951 42.047213l-0.197869-1.545854-9.134963 2.638257c-2.407409 3.165908-5.837142 8.07966-9.464745 9.530701-11.311525 3.561646-24.012256 4.843674-39.343 6.463729-7.222227 0.626586-13.42213 0.263826-21.106052 1.714866-1.615932 0.296804-3.957385 0.890412-5.837142 1.319129l-0.164891 0.103056-0.296804 0.103057c-12.960435 3.13293-21.270943 15.038062-18.599708 26.745326 2.671235 11.740241 15.334866 18.859412 28.361257 16.060386l0.296804-0.032978 0.428716-0.131913 5.66813-1.28615c7.486053-2.011671 12.927457-4.979709 19.683866-7.552009 14.510411-5.177578 26.547456-9.530702 38.254719-11.245569 4.913753-0.395738 10.124309 3.033995 12.663632 4.452058l9.530701-1.615932c22.29842 67.521846 67.114772 122.532586 125.354284 157.387458l1.28615 0.721398-3.957385 9.596658c1.451041 3.693559 3.033995 8.739225 1.945714 12.399806-4.255219 11.0848-11.572259 22.730229-19.885858 35.754558-3.990363 5.969055-8.141495 10.651961-11.77322 17.544406-0.890412 1.615932-1.978692 4.188232-2.803147 5.903099-5.639273 12.103002-1.484019 25.986826 9.36581 31.226239 10.915786 5.276513 24.436851-0.296804 30.33995-12.399806v-0.103057c0.890412-1.714867 2.011671-3.957385 2.737191-5.573316 3.066973-7.123293 4.122276-13.224261 6.331815-20.149684 5.804164-14.609345 9.00305-29.911233 17.016755-39.441935 2.20954-2.638256 5.70523-3.594624 9.464745-4.616949l4.979709-9.00305c30.273993 12.004067 65.328796 18.962468 102.033539 18.962469a282.97465 282.97465 0 0 0 102.56119-19.094382l-1.945714 0.659564 4.649927 8.442421c3.792494 1.220194 7.914769 1.84678 11.245568 6.826489 5.969055 10.190266 10.058353 22.293267 15.038062 36.935591 2.20954 6.859467 3.264842 12.960435 6.364794 20.083727 0.692542 1.615932 1.879758 3.957385 2.737191 5.668129 5.837142 12.13598 19.424163 17.676318 30.33995 12.399806 10.84983-5.177578 15.005084-19.12736 9.36581-31.226239-0.890412-1.714867-1.978692-4.225333-2.869104-5.903099-3.660581-6.859467-7.782857-11.476416-11.773219-17.511427-8.347608-13.026391-15.202953-23.777287-19.49012-34.857964-1.751967-5.70523 0.296804-9.233898 1.681889-12.927457-0.791477-0.956368-2.605278-6.331816-3.660581-8.871137 59.722501-35.84731 104.572861-91.282644 126.074651-157.238026l0.560629-2.01167c2.803148 0.428717 7.749878 1.319128 9.365811 1.681888 3.297821-2.20954 6.331816-5.012687 12.300871-4.58397 11.707263 1.714867 23.744308 6.06799 38.254719 11.245568 6.760532 2.638256 12.168958 5.639273 19.683866 7.617965 1.582954 0.428717 3.85845 0.824455 5.70523 1.220194l0.395739 0.131913 0.296803 0.032978c13.05937 2.803148 25.690023-4.320145 28.361258-16.060386 2.638256-11.707263-5.639273-23.612396-18.599709-26.745325zM721.096012 410.905211l-85.743336 76.777386v0.230848a19.34584 19.34584 0 0 0 7.486053 33.242032l0.131913 0.032978 0.131913 0.428716 111.069567 32.015655a221.825842 221.825842 0 0 0 1.088281-22.293267 229.224296 229.224296 0 0 0-6.133946-52.896012l0.296804 1.545853a228.754357 228.754357 0 0 0-28.888909-70.078688l0.56063 0.956368z m-176.464319 234.145263a19.241753 19.241753 0 0 0-16.983776-10.223243 6.896567 6.896567 0 0 0-0.824456 0.032978h0.032979a19.280914 19.280914 0 0 0-16.291234 10.157287l-0.065957 0.103057h-0.103056l-55.765116 100.780367a222.317423 222.317423 0 0 0 73.309521 12.168958 227.451718 227.451718 0 0 0 74.531776-12.505954l-1.582953 0.494673-55.831073-100.945258z m83.005114-56.853396a19.360268 19.360268 0 0 0-3.49569-0.329782 19.168582 19.168582 0 0 0-8.475399 1.945714l0.103057-0.065957a19.329351 19.329351 0 0 0-9.365811 25.261306l-0.032978-0.131913-0.131913 0.164891 44.417522 107.211118a227.543438 227.543438 0 0 0 90.98584-113.41102l0.494673-1.582954-114.33441-19.361299z" fill="#326CE5" p-id="4255"></path></svg> +<svg viewBox="0 0 1055 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4254" width="200" height="200"><path d="M448.633165 615.302071l0.296804 0.428717-43.92594 106.088828a228.574007 228.574007 0 0 1-90.722014-112.623666l-0.494673-1.582953 113.345064-19.226295 0.164891 0.230848a19.331412 19.331412 0 0 1 22.885844 19.032547 18.962468 18.962468 0 0 1-1.615932 7.749878l0.032978-0.131912z m-36.609931-93.591118a19.374696 19.374696 0 0 0 7.650944-33.242031l-0.032978-0.032979 0.103057-0.494673-86.236978-77.134993a223.736517 223.736517 0 0 0-33.472879 118.357751 229.961153 229.961153 0 0 0 1.451041 25.788957l-0.103057-1.022324 110.547069-31.889926z m50.323712-87.061433a19.340687 19.340687 0 0 0 30.735688-14.774237v-0.032978l0.428717-0.230847 6.595641-115.191843a226.003768 226.003768 0 0 0-132.373488 63.449038l0.032978-0.032979 94.415573 66.977706z m33.439901 120.897073l31.790991 15.334865 31.758012-15.268909 7.91477-34.297334-21.996464-27.404889h-35.348514l-21.996464 27.404889z m65.956412-136.067048a19.346871 19.346871 0 0 0 30.834623 14.737136l-0.065956 0.032978 0.36276 0.131913 93.821966-66.516011a227.423892 227.423892 0 0 0-130.427775-63.284147l-1.121259-0.131913 6.492584 114.993974z m478.215936 262.503428l-253.82913 315.766323a70.270374 70.270374 0 0 1-54.709813 26.11874h-0.164891l-407.211797 0.131912h-0.065956a70.181745 70.181745 0 0 1-54.676835-26.085761l-0.103057-0.131912L15.238734 682.081908a69.615962 69.615962 0 0 1-13.389151-59.393749l-0.103057 0.461695L92.34075 229.395226a69.556189 69.556189 0 0 1 37.957915-47.190782L497.111128 6.893331a70.697029 70.697029 0 0 1 61.305455 0.19787l-0.428717-0.19787 366.949529 175.211148a69.626268 69.626268 0 0 1 37.957916 47.191813l90.656058 393.755658a69.254233 69.254233 0 0 1-13.554043 58.931023z m-144.607373-90.458188c-1.84678-0.428717-4.518014-1.154237-6.364793-1.484019-7.650944-1.451041-13.850847-1.088281-21.073074-1.681889-15.400822-1.615932-28.064453-2.93506-39.343-6.492584-4.616949-1.751967-7.914769-7.255205-9.497723-9.497724l-8.838159-2.605278a276.357367 276.357367 0 0 0 2.671234-38.716414 284.359735 284.359735 0 0 0-7.650944-65.757512l0.395739 1.912736a284.83998 284.83998 0 0 0-41.783387-96.097462l0.626586 0.989346c2.275496-2.077627 6.595641-5.837142 7.782856-6.991379 0.36276-3.957385 0.032978-8.046682 4.122276-12.399806 8.673268-8.141495 19.523098-14.873171 32.681402-22.952831 6.232881-3.693559 12.004067-6.035012 18.241071-10.651961 1.418063-1.055303 3.330799-2.737191 4.843674-3.924406 10.553026-8.409443 12.960435-22.853897 5.408425-32.351621s-22.260289-10.388135-32.747358-1.978692c-1.484019 1.187215-3.528668 2.737191-4.880775 3.85845-5.903099 5.111622-9.530702 10.124309-14.51041 15.400822-10.820974 10.981743-19.786924 20.149684-29.577329 26.778304-4.254189 2.473365-10.520048 1.615932-13.323195 1.451041l-8.347609 5.936077a287.21338 287.21338 0 0 0-181.147224-87.984823l-1.154238-0.103057-0.527651-9.790405c-2.869104-2.737191-6.298837-5.049788-7.156271-10.981743-0.956368-11.77322 0.659564-24.502807 2.506344-39.804694 1.022324-7.156271 2.671235-13.088226 3.001017-20.875205 0.032978-1.751967-0.032978-4.353123-0.032978-6.232881 0-13.455108-9.860484-24.403872-21.996464-24.403872-12.103002 0-21.930507 10.948764-21.930507 24.403872l0.032978 0.626586c0 1.813801-0.103057 4.056319 0 5.639273 0.263826 7.782857 1.945714 13.718934 2.935061 20.875205 1.84678 15.301888 3.429733 27.998497 2.473365 39.837673a24.065846 24.065846 0 0 1-7.090314 11.311524l-0.032978 0.032979-0.527652 9.266875a282.443907 282.443907 0 0 0-183.05996 87.918867l-0.131913 0.131913a349.351534 349.351534 0 0 1-8.937094-6.331816l1.022324 0.721399c-3.957385 0.527651-7.914769 1.751967-13.059369-1.28615-9.790405-6.595641-18.764599-15.730604-29.577329-26.745325-4.979709-5.276513-8.574334-10.2892-14.477432-15.334866-1.319128-1.154237-3.400877-2.737191-4.880775-3.85845a25.943542 25.943542 0 0 0-15.235931-5.804165h-0.065956c-0.329782-0.032978-0.721398-0.032978-1.121259-0.032978a21.126663 21.126663 0 0 0-16.35719 7.7169l-0.032979 0.032979c-7.552009 9.497723-5.1446 24.012256 5.408426 32.417576l0.296804 0.230848 4.583971 3.66058c6.232881 4.616949 11.971089 6.99138 18.203969 10.651961 13.158304 8.141495 24.012256 14.873171 32.681403 22.952831 3.330799 3.594624 3.957385 9.92644 4.386101 12.663632l7.024358 6.298837a281.617391 281.617391 0 0 0-47.751412 157.666742 285.194495 285.194495 0 0 0 3.099951 42.047213l-0.197869-1.545854-9.134963 2.638257c-2.407409 3.165908-5.837142 8.07966-9.464745 9.530701-11.311525 3.561646-24.012256 4.843674-39.343 6.463729-7.222227 0.626586-13.42213 0.263826-21.106052 1.714866-1.615932 0.296804-3.957385 0.890412-5.837142 1.319129l-0.164891 0.103056-0.296804 0.103057c-12.960435 3.13293-21.270943 15.038062-18.599708 26.745326 2.671235 11.740241 15.334866 18.859412 28.361257 16.060386l0.296804-0.032978 0.428716-0.131913 5.66813-1.28615c7.486053-2.011671 12.927457-4.979709 19.683866-7.552009 14.510411-5.177578 26.547456-9.530702 38.254719-11.245569 4.913753-0.395738 10.124309 3.033995 12.663632 4.452058l9.530701-1.615932c22.29842 67.521846 67.114772 122.532586 125.354284 157.387458l1.28615 0.721398-3.957385 9.596658c1.451041 3.693559 3.033995 8.739225 1.945714 12.399806-4.255219 11.0848-11.572259 22.730229-19.885858 35.754558-3.990363 5.969055-8.141495 10.651961-11.77322 17.544406-0.890412 1.615932-1.978692 4.188232-2.803147 5.903099-5.639273 12.103002-1.484019 25.986826 9.36581 31.226239 10.915786 5.276513 24.436851-0.296804 30.33995-12.399806v-0.103057c0.890412-1.714867 2.011671-3.957385 2.737191-5.573316 3.066973-7.123293 4.122276-13.224261 6.331815-20.149684 5.804164-14.609345 9.00305-29.911233 17.016755-39.441935 2.20954-2.638256 5.70523-3.594624 9.464745-4.616949l4.979709-9.00305c30.273993 12.004067 65.328796 18.962468 102.033539 18.962469a282.97465 282.97465 0 0 0 102.56119-19.094382l-1.945714 0.659564 4.649927 8.442421c3.792494 1.220194 7.914769 1.84678 11.245568 6.826489 5.969055 10.190266 10.058353 22.293267 15.038062 36.935591 2.20954 6.859467 3.264842 12.960435 6.364794 20.083727 0.692542 1.615932 1.879758 3.957385 2.737191 5.668129 5.837142 12.13598 19.424163 17.676318 30.33995 12.399806 10.84983-5.177578 15.005084-19.12736 9.36581-31.226239-0.890412-1.714867-1.978692-4.225333-2.869104-5.903099-3.660581-6.859467-7.782857-11.476416-11.773219-17.511427-8.347608-13.026391-15.202953-23.777287-19.49012-34.857964-1.751967-5.70523 0.296804-9.233898 1.681889-12.927457-0.791477-0.956368-2.605278-6.331816-3.660581-8.871137 59.722501-35.84731 104.572861-91.282644 126.074651-157.238026l0.560629-2.01167c2.803148 0.428717 7.749878 1.319128 9.365811 1.681888 3.297821-2.20954 6.331816-5.012687 12.300871-4.58397 11.707263 1.714867 23.744308 6.06799 38.254719 11.245568 6.760532 2.638256 12.168958 5.639273 19.683866 7.617965 1.582954 0.428717 3.85845 0.824455 5.70523 1.220194l0.395739 0.131913 0.296803 0.032978c13.05937 2.803148 25.690023-4.320145 28.361258-16.060386 2.638256-11.707263-5.639273-23.612396-18.599709-26.745325zM721.096012 410.905211l-85.743336 76.777386v0.230848a19.34584 19.34584 0 0 0 7.486053 33.242032l0.131913 0.032978 0.131913 0.428716 111.069567 32.015655a221.825842 221.825842 0 0 0 1.088281-22.293267 229.224296 229.224296 0 0 0-6.133946-52.896012l0.296804 1.545853a228.754357 228.754357 0 0 0-28.888909-70.078688l0.56063 0.956368z m-176.464319 234.145263a19.241753 19.241753 0 0 0-16.983776-10.223243 6.896567 6.896567 0 0 0-0.824456 0.032978h0.032979a19.280914 19.280914 0 0 0-16.291234 10.157287l-0.065957 0.103057h-0.103056l-55.765116 100.780367a222.317423 222.317423 0 0 0 73.309521 12.168958 227.451718 227.451718 0 0 0 74.531776-12.505954l-1.582953 0.494673-55.831073-100.945258z m83.005114-56.853396a19.360268 19.360268 0 0 0-3.49569-0.329782 19.168582 19.168582 0 0 0-8.475399 1.945714l0.103057-0.065957a19.329351 19.329351 0 0 0-9.365811 25.261306l-0.032978-0.131913-0.131913 0.164891 44.417522 107.211118a227.543438 227.543438 0 0 0 90.98584-113.41102l0.494673-1.582954-114.33441-19.361299z" fill="#326CE5" p-id="4255"></path></svg> diff --git a/ui/public/imgs/providers/local.svg b/ui/public/imgs/providers/local.svg index 2f59af07..46974fa7 100644 --- a/ui/public/imgs/providers/local.svg +++ b/ui/public/imgs/providers/local.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="26535" width="200" height="200"><path d="M339.008 128a64 64 0 0 1 41.6 15.36L512 256h384a64 64 0 0 1 64 64v512a64 64 0 0 1-64 64H128a64 64 0 0 1-64-64V192a64 64 0 0 1 64-64h211.008zM883.2 486.4H140.8v332.8h742.4V486.4zM334.208 204.736L288 204.8H140.8v204.8h742.4V332.8H483.584l-21.568-18.496L334.208 204.8z" fill="#525962" p-id="26536"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="26535" width="200" height="200"><path d="M339.008 128a64 64 0 0 1 41.6 15.36L512 256h384a64 64 0 0 1 64 64v512a64 64 0 0 1-64 64H128a64 64 0 0 1-64-64V192a64 64 0 0 1 64-64h211.008zM883.2 486.4H140.8v332.8h742.4V486.4zM334.208 204.736L288 204.8H140.8v204.8h742.4V332.8H483.584l-21.568-18.496L334.208 204.8z" fill="#525962" p-id="26536"></path></svg> diff --git a/ui/public/imgs/providers/namecheap.svg b/ui/public/imgs/providers/namecheap.svg new file mode 100644 index 00000000..e90dbfe9 --- /dev/null +++ b/ui/public/imgs/providers/namecheap.svg @@ -0,0 +1 @@ +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" viewBox="0 -57 256 256" preserveAspectRatio="xMidYMid"><defs><linearGradient x1="13.3220455%" y1="94.9449067%" x2="82.6195455%" y2="1.13156385%"><stop stop-color="#D4202C" offset="0%"></stop><stop stop-color="#D82D2B" stop-opacity="0.9583" offset="4.166156%"></stop><stop stop-color="#E25226" stop-opacity="0.824" offset="17.6%"></stop><stop stop-color="#EB7123" stop-opacity="0.6833" offset="31.67%"></stop><stop stop-color="#F28920" stop-opacity="0.5365" offset="46.35%"></stop><stop stop-color="#F69A1E" stop-opacity="0.3812" offset="61.88%"></stop><stop stop-color="#F9A41D" stop-opacity="0.2114" offset="78.86%"></stop><stop stop-color="#FAA71D" stop-opacity="0" offset="100%"></stop></linearGradient><linearGradient x1="86.6243182%" y1="5.04045911%" x2="17.3261364%" y2="98.8545194%"><stop stop-color="#D4202C" offset="0%"></stop><stop stop-color="#D82D2B" stop-opacity="0.9583" offset="4.166156%"></stop><stop stop-color="#E25226" stop-opacity="0.824" offset="17.6%"></stop><stop stop-color="#EB7123" stop-opacity="0.6833" offset="31.67%"></stop><stop stop-color="#F28920" stop-opacity="0.5365" offset="46.35%"></stop><stop stop-color="#F69A1E" stop-opacity="0.3812" offset="61.88%"></stop><stop stop-color="#F9A41D" stop-opacity="0.2114" offset="78.86%"></stop><stop stop-color="#FAA71D" stop-opacity="0" offset="100%"></stop></linearGradient></defs><g><path d="M232,0 C223,0 215.2,5 211.1,12.3 L210.6,13.3 L191.8,50.3 L168,97.2 L183.6,127.9 L184.5,129.6 C186.9,133.8 190.5,137.3 194.9,139.4 C199.3,137.2 202.9,133.8 205.3,129.6 L206.2,127.9 L252.9,35.9 L254,33.7 C255.3,30.7 256,27.5 256,24 C256,10.7 245.3,0 232,0 L232,0 Z" fill="#FF5000"></path><path d="M87.9,44.6 L72.4,14 L71.5,12.3 C69.1,8.1 65.5,4.6 61.1,2.5 C56.7,4.7 53.1,8.1 50.7,12.3 L49.9,14 L3.2,106 L2.1,108.2 C0.8,111.2 0.1,114.4 0.1,117.9 C0.1,131.1 10.8,141.9 24.1,141.9 C33.1,141.9 40.9,136.9 45,129.6 L45.5,128.6 L64.3,91.6 L88,44.7 L87.9,44.6 L87.9,44.6 Z" fill="#FF5000"></path><path d="M232,0 C223,0 215.1,5 211.1,12.3 L210.6,13.3 L191.8,50.3 L168,97.2 L183.6,127.9 L184.5,129.6 C186.9,133.8 190.5,137.3 194.9,139.4 C199.3,137.2 202.9,133.8 205.3,129.6 L206.2,127.9 L252.9,35.9 L254,33.7 C255.3,30.7 256,27.5 256,24 C256,10.7 245.2,0 232,0 L232,0 Z" fill="url(#linearGradient-1)"></path><path d="M24,141.9 C33,141.9 40.9,136.9 44.9,129.6 L45.4,128.6 L64.2,91.6 L88,44.7 L72.4,14 L71.5,12.3 C69.1,8.1 65.5,4.6 61.1,2.5 C56.7,4.7 53.1,8.1 50.7,12.3 L49.9,14 L3.2,106 L2,108.3 C0.7,111.3 0,114.5 0,118 C0,131.2 10.7,141.9 24,141.9 L24,141.9 Z" fill="url(#linearGradient-2)"></path><path d="M87.9,44.6 L72.4,14 L71.5,12.3 C69.1,8.1 65.5,4.6 61.1,2.5 C62.5,1.8 64.1,1.2 65.6,0.8 C67.5,0.3 69.6,0 71.6,0 L71.6,0 L104,0 L104.2,0 L104.4,0 C113.4,0.1 121.2,5 125.3,12.3 L126,14 L168.1,97.3 L183.6,127.9 L184.5,129.6 C186.9,133.8 190.5,137.3 194.9,139.4 C193.5,140.1 191.9,140.7 190.4,141.1 C188.5,141.6 186.4,141.9 184.3,141.9 L184.3,141.9 L152.1,141.9 L151.9,141.9 L151.7,141.9 C142.7,141.8 134.9,136.9 130.8,129.6 L129.9,127.9 L87.9,44.6 L87.9,44.6 Z" fill="#FF8C44"></path></g></svg> diff --git a/ui/public/imgs/providers/namesilo.svg b/ui/public/imgs/providers/namesilo.svg index e0cc8da4..4284796d 100644 --- a/ui/public/imgs/providers/namesilo.svg +++ b/ui/public/imgs/providers/namesilo.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7279" width="200" height="200"><path d="M799.8 943.7H220c-59.5-4.6-99-37.8-126-88.8C81.8 832 81.8 806.8 81.7 782c-0.4-180.3 1.5-360.7-1-541-1.2-83.1 69.1-160.3 156.5-159.6 184.7 1.6 369.4 1.1 554.1 0.2 76.9-0.4 152.1 67.6 151.6 155.1-1 186 0.3 372.1-0.6 558.1-0.4 80.6-57.4 139.3-138.7 147-1.4 0.2-2.6 1.2-3.8 1.9z" fill="#031B4E" p-id="7280"></path><path d="M500.9 435.5c-47.8-0.3-94.7-5.6-140.2-21.4-17.7-6.2-23.7-16.3-25.3-35.9-2.6-30.7 8.4-47.2 35-61.3 41.9-22.2 81.6-48.7 121.7-74 10.2-6.4 17.9-7 28.4-0.1 44.9 29.8 90.3 58.9 135.9 87.7 8.2 5.1 11.3 10.5 11.5 20.3 1.2 57.7 1.1 58-55.2 72.5-36.7 9.3-74.1 12.3-111.8 12.2zM336.3 543.5c111.7 39.3 220.4 39.5 331.2 0V628c0 9.6-7 13-14 16.8-20.4 10.9-42.6 15.3-65 18.6-73 10.8-145.6 10.5-217-10.4-34.5-10.1-35.2-12.1-35.2-47.2v-62.3z" fill="#FEFEFE" p-id="7281"></path><path d="M667.6 423.1c0 26-1.5 50.3 0.5 74.4 1.7 20.6-8 29.6-25.2 35.6-38.7 13.5-78.8 17.9-119.2 19.4-53.4 2-106.2-2-157.7-18.1-20.9-6.5-32.4-16.8-30-41.1 2.2-22.5 0.5-45.4 0.5-69.9 52.9 27.5 109.2 31.7 165.8 31.7 55.8-0.1 111.6-3.7 165.3-32zM336.4 661.3c111.6 38.1 220.2 39 331.2-0.2 0 26.2-0.1 54.9 0 83.6 0.1 11.8-8.9 15.4-17.2 19.2-25.5 11.6-52.8 16.4-80.2 19.3-63.8 6.8-127.4 6.4-189.9-10.3-10.8-2.9-21.3-7.3-31.6-11.8-8.4-3.7-12.7-10.2-12.5-20.2 0.5-26.9 0.2-53.9 0.2-79.6z" fill="#FEFEFE" p-id="7282"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7279" width="200" height="200"><path d="M799.8 943.7H220c-59.5-4.6-99-37.8-126-88.8C81.8 832 81.8 806.8 81.7 782c-0.4-180.3 1.5-360.7-1-541-1.2-83.1 69.1-160.3 156.5-159.6 184.7 1.6 369.4 1.1 554.1 0.2 76.9-0.4 152.1 67.6 151.6 155.1-1 186 0.3 372.1-0.6 558.1-0.4 80.6-57.4 139.3-138.7 147-1.4 0.2-2.6 1.2-3.8 1.9z" fill="#031B4E" p-id="7280"></path><path d="M500.9 435.5c-47.8-0.3-94.7-5.6-140.2-21.4-17.7-6.2-23.7-16.3-25.3-35.9-2.6-30.7 8.4-47.2 35-61.3 41.9-22.2 81.6-48.7 121.7-74 10.2-6.4 17.9-7 28.4-0.1 44.9 29.8 90.3 58.9 135.9 87.7 8.2 5.1 11.3 10.5 11.5 20.3 1.2 57.7 1.1 58-55.2 72.5-36.7 9.3-74.1 12.3-111.8 12.2zM336.3 543.5c111.7 39.3 220.4 39.5 331.2 0V628c0 9.6-7 13-14 16.8-20.4 10.9-42.6 15.3-65 18.6-73 10.8-145.6 10.5-217-10.4-34.5-10.1-35.2-12.1-35.2-47.2v-62.3z" fill="#FEFEFE" p-id="7281"></path><path d="M667.6 423.1c0 26-1.5 50.3 0.5 74.4 1.7 20.6-8 29.6-25.2 35.6-38.7 13.5-78.8 17.9-119.2 19.4-53.4 2-106.2-2-157.7-18.1-20.9-6.5-32.4-16.8-30-41.1 2.2-22.5 0.5-45.4 0.5-69.9 52.9 27.5 109.2 31.7 165.8 31.7 55.8-0.1 111.6-3.7 165.3-32zM336.4 661.3c111.6 38.1 220.2 39 331.2-0.2 0 26.2-0.1 54.9 0 83.6 0.1 11.8-8.9 15.4-17.2 19.2-25.5 11.6-52.8 16.4-80.2 19.3-63.8 6.8-127.4 6.4-189.9-10.3-10.8-2.9-21.3-7.3-31.6-11.8-8.4-3.7-12.7-10.2-12.5-20.2 0.5-26.9 0.2-53.9 0.2-79.6z" fill="#FEFEFE" p-id="7282"></path></svg> diff --git a/ui/public/imgs/providers/powerdns.svg b/ui/public/imgs/providers/powerdns.svg index 192466ed..768bbacf 100644 --- a/ui/public/imgs/providers/powerdns.svg +++ b/ui/public/imgs/providers/powerdns.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" version="1.1" width="200" height="200"><path d="M18.97 21.14c0 5.293-4.248 9.585-9.487 9.585S0 26.432 0 21.14s4.245-9.585 9.485-9.585 9.485 4.293 9.485 9.585z" fill="#e38000"/><path d="M18.97 42.865c0 5.29-4.248 9.58-9.487 9.58S0 48.156 0 42.86s4.245-9.585 9.485-9.585 9.485 4.293 9.485 9.585zM41.488 21.14c0 5.293-4.25 9.585-9.49 9.585s-9.485-4.29-9.485-9.585 4.248-9.585 9.485-9.585 9.487 4.293 9.487 9.585zm0 21.726c0 5.29-4.25 9.58-9.49 9.58s-9.485-4.29-9.485-9.585 4.248-9.585 9.485-9.585 9.487 4.293 9.487 9.585zM64 21.14c0 5.293-4.245 9.585-9.485 9.585s-9.485-4.29-9.485-9.585 4.245-9.585 9.485-9.585S64 15.848 64 21.14z" fill="#e17f03"/><path d="M64 42.865c0 5.29-4.245 9.58-9.485 9.58s-9.485-4.29-9.485-9.585 4.245-9.585 9.485-9.585S64 37.57 64 42.86z" fill="#e38000"/></svg> +<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" version="1.1" width="200" height="200"><path d="M18.97 21.14c0 5.293-4.248 9.585-9.487 9.585S0 26.432 0 21.14s4.245-9.585 9.485-9.585 9.485 4.293 9.485 9.585z" fill="#e38000"/><path d="M18.97 42.865c0 5.29-4.248 9.58-9.487 9.58S0 48.156 0 42.86s4.245-9.585 9.485-9.585 9.485 4.293 9.485 9.585zM41.488 21.14c0 5.293-4.25 9.585-9.49 9.585s-9.485-4.29-9.485-9.585 4.248-9.585 9.485-9.585 9.487 4.293 9.487 9.585zm0 21.726c0 5.29-4.25 9.58-9.49 9.58s-9.485-4.29-9.485-9.585 4.248-9.585 9.485-9.585 9.487 4.293 9.487 9.585zM64 21.14c0 5.293-4.245 9.585-9.485 9.585s-9.485-4.29-9.485-9.585 4.245-9.585 9.485-9.585S64 15.848 64 21.14z" fill="#e17f03"/><path d="M64 42.865c0 5.29-4.245 9.58-9.485 9.58s-9.485-4.29-9.485-9.585 4.245-9.585 9.485-9.585S64 37.57 64 42.86z" fill="#e38000"/></svg> diff --git a/ui/public/imgs/providers/qiniu.svg b/ui/public/imgs/providers/qiniu.svg index d3b98877..20a88367 100644 --- a/ui/public/imgs/providers/qiniu.svg +++ b/ui/public/imgs/providers/qiniu.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8269" width="200" height="200"><path d="M998.4 191.488c-1.536-3.072-5.12-6.144-11.776-4.608-12.8 3.072-159.232 246.784-474.624 240.128-59.904 1.024-113.664-6.656-161.792-19.968l-25.6-87.04s-5.12-21.504-26.112-32.768c-14.336-7.68-23.04-5.12-24.576-3.072-1.536 2.048-1.536 4.096-1.536 4.096l12.8 96.256C124.928 315.904 46.08 189.44 36.864 187.392c-6.656-1.536-10.24 1.536-11.776 4.608-2.56 5.12 0 12.288 0 12.288 45.568 134.144 143.36 240.128 269.824 296.448l34.816 232.448c2.56 66.56 47.104 104.448 104.96 104.448h173.568c57.856 0 101.376-41.472 104.96-104.448l31.744-192.512s0.512-2.56-1.024-3.584c-2.048-1.024-16.896-1.536-47.616 20.992-30.72 22.528-40.96 55.296-40.96 55.296s-33.28 79.872-41.984 114.176c-9.216 35.84-49.664 32.768-49.664 32.768h-92.16c-31.232 0-34.304-27.648-34.304-27.648L378.88 529.408c42.496 10.752 86.528 16.384 133.12 15.872 228.352 1.024 417.792-137.728 486.912-341.504 0 0 2.56-7.168-0.512-12.288" fill="#00AAE7" p-id="8270"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8269" width="200" height="200"><path d="M998.4 191.488c-1.536-3.072-5.12-6.144-11.776-4.608-12.8 3.072-159.232 246.784-474.624 240.128-59.904 1.024-113.664-6.656-161.792-19.968l-25.6-87.04s-5.12-21.504-26.112-32.768c-14.336-7.68-23.04-5.12-24.576-3.072-1.536 2.048-1.536 4.096-1.536 4.096l12.8 96.256C124.928 315.904 46.08 189.44 36.864 187.392c-6.656-1.536-10.24 1.536-11.776 4.608-2.56 5.12 0 12.288 0 12.288 45.568 134.144 143.36 240.128 269.824 296.448l34.816 232.448c2.56 66.56 47.104 104.448 104.96 104.448h173.568c57.856 0 101.376-41.472 104.96-104.448l31.744-192.512s0.512-2.56-1.024-3.584c-2.048-1.024-16.896-1.536-47.616 20.992-30.72 22.528-40.96 55.296-40.96 55.296s-33.28 79.872-41.984 114.176c-9.216 35.84-49.664 32.768-49.664 32.768h-92.16c-31.232 0-34.304-27.648-34.304-27.648L378.88 529.408c42.496 10.752 86.528 16.384 133.12 15.872 228.352 1.024 417.792-137.728 486.912-341.504 0 0 2.56-7.168-0.512-12.288" fill="#00AAE7" p-id="8270"></path></svg> diff --git a/ui/public/imgs/providers/safeline.svg b/ui/public/imgs/providers/safeline.svg new file mode 100644 index 00000000..9a0db247 --- /dev/null +++ b/ui/public/imgs/providers/safeline.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M461.750857 72.850286l-325.193143 282.331428L378.148571 580.096h125.659429L266.24 358.4 512 153.526857l289.645714 246.418286v69.339428l86.162286 56.685715V357.888L562.761143 74.532571 466.358857 72.850286z" fill="#45495A"></path><path d="M542.646857 446.317714h-22.820571l239.177143 222.354286-243.565715 209.334857-295.862857-246.710857V553.398857l-83.382857-53.394286v163.108572l336.457143 288.036571h90.331428l320.804572-282.404571-242.102857-222.427429z" fill="#45495A"></path></svg> diff --git a/ui/public/imgs/providers/ssh.svg b/ui/public/imgs/providers/ssh.svg index 8dea9e89..d673cafa 100644 --- a/ui/public/imgs/providers/ssh.svg +++ b/ui/public/imgs/providers/ssh.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="23812" width="200" height="200"><path d="M128 128h768a42.667 42.667 0 0 1 42.667 42.667v682.666A42.667 42.667 0 0 1 896 896H128a42.667 42.667 0 0 1-42.667-42.667V170.667A42.667 42.667 0 0 1 128 128z m384 512v85.333h256V640H512zM358.997 512L238.336 632.661l60.33 60.374L479.702 512 298.667 330.965l-60.331 60.374L358.997 512z" p-id="23813"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="23812" width="200" height="200"><path d="M128 128h768a42.667 42.667 0 0 1 42.667 42.667v682.666A42.667 42.667 0 0 1 896 896H128a42.667 42.667 0 0 1-42.667-42.667V170.667A42.667 42.667 0 0 1 128 128z m384 512v85.333h256V640H512zM358.997 512L238.336 632.661l60.33 60.374L479.702 512 298.667 330.965l-60.331 60.374L358.997 512z" p-id="23813"></path></svg> diff --git a/ui/public/imgs/providers/tencentcloud.svg b/ui/public/imgs/providers/tencentcloud.svg index 76e54dbb..1fb8f4b6 100644 --- a/ui/public/imgs/providers/tencentcloud.svg +++ b/ui/public/imgs/providers/tencentcloud.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17958" width="200" height="200"><path d="M512 170.666667c130.474667 0 240.938667 83.797333 277.930667 199.296a198.826667 198.826667 0 0 0-41.557334-0.597334 222.293333 222.293333 0 0 0-49.706666 10.624C668.202667 309.333333 596.096 259.754667 512 259.754667c-100.266667 0-183.466667 70.528-199.381333 163.029333a279.04 279.04 0 0 0-89.429334-3.84C241.28 278.954667 363.690667 170.666667 512 170.666667z" fill="#006DFE" p-id="17959"></path><path d="M258.474667 417.322667c54.442667 0 104.192 20.181333 142.165333 53.418666 16.085333 14.08 45.226667 39.68 87.381333 76.8l-7.381333-6.528-61.568 60.885334-54.4-54.4c-34.218667-34.261333-66.090667-47.957333-106.197333-47.957334a133.589333 133.589333 0 0 0 0 267.221334c10.666667 0 29.312 0.768 56.064 2.346666l-90.453334 77.141334A215.893333 215.893333 0 0 1 258.432 417.28z" fill="#00CDD8" p-id="17960"></path><path d="M674.346667 434.474667a215.808 215.808 0 0 1 168.618666 397.354666c-15.36 6.485333-38.186667 15.957333-63.146666 16.213334-72.106667 0.597333-244.181333 0.896-516.352 0.938666h-42.666667a206248.106667 206248.106667 0 0 0 397.013333-380.714666c18.261333-17.578667 41.130667-27.264 56.533334-33.792z m41.856 80.554666c-9.258667 3.925333-23.04 9.770667-34.048 20.352-30.165333 29.098667-109.952 105.642667-239.445334 229.632h53.418667c148.181333 0 242.773333-0.213333 283.733333-0.554666 15.061333-0.128 28.842667-5.845333 38.101334-9.813334a130.133333 130.133333 0 0 0-101.76-239.616z" fill="#00A2FF" p-id="17961"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17958" width="200" height="200"><path d="M512 170.666667c130.474667 0 240.938667 83.797333 277.930667 199.296a198.826667 198.826667 0 0 0-41.557334-0.597334 222.293333 222.293333 0 0 0-49.706666 10.624C668.202667 309.333333 596.096 259.754667 512 259.754667c-100.266667 0-183.466667 70.528-199.381333 163.029333a279.04 279.04 0 0 0-89.429334-3.84C241.28 278.954667 363.690667 170.666667 512 170.666667z" fill="#006DFE" p-id="17959"></path><path d="M258.474667 417.322667c54.442667 0 104.192 20.181333 142.165333 53.418666 16.085333 14.08 45.226667 39.68 87.381333 76.8l-7.381333-6.528-61.568 60.885334-54.4-54.4c-34.218667-34.261333-66.090667-47.957333-106.197333-47.957334a133.589333 133.589333 0 0 0 0 267.221334c10.666667 0 29.312 0.768 56.064 2.346666l-90.453334 77.141334A215.893333 215.893333 0 0 1 258.432 417.28z" fill="#00CDD8" p-id="17960"></path><path d="M674.346667 434.474667a215.808 215.808 0 0 1 168.618666 397.354666c-15.36 6.485333-38.186667 15.957333-63.146666 16.213334-72.106667 0.597333-244.181333 0.896-516.352 0.938666h-42.666667a206248.106667 206248.106667 0 0 0 397.013333-380.714666c18.261333-17.578667 41.130667-27.264 56.533334-33.792z m41.856 80.554666c-9.258667 3.925333-23.04 9.770667-34.048 20.352-30.165333 29.098667-109.952 105.642667-239.445334 229.632h53.418667c148.181333 0 242.773333-0.213333 283.733333-0.554666 15.061333-0.128 28.842667-5.845333 38.101334-9.813334a130.133333 130.133333 0 0 0-101.76-239.616z" fill="#00A2FF" p-id="17961"></path></svg> diff --git a/ui/public/imgs/providers/ucloud.svg b/ui/public/imgs/providers/ucloud.svg index 9e246c65..3000023f 100644 --- a/ui/public/imgs/providers/ucloud.svg +++ b/ui/public/imgs/providers/ucloud.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M912.192 1024H111.808A111.808 111.808 0 0 1 0 912.192V111.808C0 50.048 50.048 0 111.808 0h800.384C973.952 0 1024 50.048 1024 111.808v800.384c0 61.76-50.048 111.808-111.808 111.808" fill="#3860F4"></path><path d="M822.208 435.2c0 64.128 0.128 128.32 0 192.448-0.256 134.592-81.216 229.376-214.336 252.416a462.08 462.08 0 0 1-187.136-5.632c-110.528-26.048-187.392-117.76-189.632-230.912-1.536-79.04 0.64-158.144-1.024-237.184-0.512-24.576 9.728-30.272 30.144-26.88 30.336 5.056 42.56-6.848 37.248-37.248-4.288-24.704 7.68-33.408 30.912-29.952 25.984 3.968 38.848-5.056 34.24-33.152-3.008-18.432 3.712-26.304 24.128-26.368 20.416-0.064 24.576 7.68 24.448 26.24-0.64 120.32-0.384 240.64-0.192 360.96 0 30.016 2.304 59.84 19.968 85.568 24 35.008 78.464 50.688 131.072 36.736 42.368-11.2 69.312-37.696 76.16-82.88 5.824-37.76 3.328-75.52 3.456-113.216 0.448-116.864 0.896-233.728-0.384-350.592-0.256-24.32 6.72-31.488 30.72-30.4 41.088 1.92 82.368 1.6 123.52 0.064 20.736-0.704 27.648 4.736 27.2 26.56-1.344 74.432-0.512 148.928-0.512 223.36" fill="#FFFFFF"></path><path d="M249.152 318.976c16.768-2.304 24 2.88 24.064 19.84 0 16.768-4.032 24.448-22.528 24.128-16.768-0.256-21.12-7.168-20.672-22.4 0.384-14.528 2.304-25.472 19.2-21.568M384.64 227.904c-15.808 2.304-22.784-3.392-22.656-20.352 0.128-15.488 3.84-22.656 20.928-22.4 15.168 0.32 22.912 3.52 22.4 20.8-0.448 14.976-3.072 24.832-20.736 21.952M317.76 253.44c16-2.368 22.72 3.648 22.72 20.672s-7.04 21.504-22.656 21.248c-14.272-0.192-20.8-4.48-20.864-19.648-0.128-15.168 3.008-25.088 20.8-22.272M319.36 192.64c13.312-1.536 21.056 1.984 20.928 18.112-0.128 16.896-9.856 17.28-22.208 17.6-13.504 0.32-21.12-2.88-21.056-18.752 0-17.024 9.856-17.92 22.4-16.96M272.192 272.896c2.176 15.36-7.424 15.744-18.944 16.192-14.976 0.64-16.128-7.552-16.448-19.328-0.384-14.848 7.68-16.192 19.52-16.512 14.912-0.448 17.216 7.68 15.872 19.648M190.848 288.896c-12.288 1.664-15.616-4.992-15.168-16 0.32-8.64 1.728-15.616 12.608-16 10.816-0.32 17.152 3.392 17.472 15.232 0.256 10.624-2.24 17.92-14.912 16.768M333.888 150.016c0.128 10.368-6.144 13.44-15.296 13.312-10.432-0.128-17.728-4.032-17.024-15.744 0.64-11.52 9.664-10.304 17.472-10.56 8.96-0.32 15.744 1.92 14.848 12.992M252.8 220.928c-8.64 0.448-15.616-1.152-15.616-12.096 0-8.192 0.128-15.808 11.52-16.128 9.728-0.256 17.6 0.64 17.28 13.696-0.256 9.28-2.816 15.04-13.184 14.528M266.112 150.272c-0.32 7.68-3.712 12.544-12.032 12.032-8.32-0.512-10.88-5.952-11.008-13.44-0.064-8.32 4.352-11.52 12.288-11.392 8.576 0.192 11.328 4.992 10.752 12.8M186.496 198.336c6.976 0 13.376 1.024 14.208 9.856 0.768 8.064-3.2 12.352-11.008 12.992-7.168 0.512-13.248-0.64-13.632-9.792-0.384-7.424 1.28-13.184 10.432-13.056M191.36 143.552c2.752 2.432 6.784 4.288 6.784 6.208 0 5.504-4.48 7.68-9.216 6.336-2.24-0.64-3.584-4.48-5.312-6.912l7.68-5.632" fill="#FFFFFF"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M912.192 1024H111.808A111.808 111.808 0 0 1 0 912.192V111.808C0 50.048 50.048 0 111.808 0h800.384C973.952 0 1024 50.048 1024 111.808v800.384c0 61.76-50.048 111.808-111.808 111.808" fill="#3860F4"></path><path d="M822.208 435.2c0 64.128 0.128 128.32 0 192.448-0.256 134.592-81.216 229.376-214.336 252.416a462.08 462.08 0 0 1-187.136-5.632c-110.528-26.048-187.392-117.76-189.632-230.912-1.536-79.04 0.64-158.144-1.024-237.184-0.512-24.576 9.728-30.272 30.144-26.88 30.336 5.056 42.56-6.848 37.248-37.248-4.288-24.704 7.68-33.408 30.912-29.952 25.984 3.968 38.848-5.056 34.24-33.152-3.008-18.432 3.712-26.304 24.128-26.368 20.416-0.064 24.576 7.68 24.448 26.24-0.64 120.32-0.384 240.64-0.192 360.96 0 30.016 2.304 59.84 19.968 85.568 24 35.008 78.464 50.688 131.072 36.736 42.368-11.2 69.312-37.696 76.16-82.88 5.824-37.76 3.328-75.52 3.456-113.216 0.448-116.864 0.896-233.728-0.384-350.592-0.256-24.32 6.72-31.488 30.72-30.4 41.088 1.92 82.368 1.6 123.52 0.064 20.736-0.704 27.648 4.736 27.2 26.56-1.344 74.432-0.512 148.928-0.512 223.36" fill="#FFFFFF"></path><path d="M249.152 318.976c16.768-2.304 24 2.88 24.064 19.84 0 16.768-4.032 24.448-22.528 24.128-16.768-0.256-21.12-7.168-20.672-22.4 0.384-14.528 2.304-25.472 19.2-21.568M384.64 227.904c-15.808 2.304-22.784-3.392-22.656-20.352 0.128-15.488 3.84-22.656 20.928-22.4 15.168 0.32 22.912 3.52 22.4 20.8-0.448 14.976-3.072 24.832-20.736 21.952M317.76 253.44c16-2.368 22.72 3.648 22.72 20.672s-7.04 21.504-22.656 21.248c-14.272-0.192-20.8-4.48-20.864-19.648-0.128-15.168 3.008-25.088 20.8-22.272M319.36 192.64c13.312-1.536 21.056 1.984 20.928 18.112-0.128 16.896-9.856 17.28-22.208 17.6-13.504 0.32-21.12-2.88-21.056-18.752 0-17.024 9.856-17.92 22.4-16.96M272.192 272.896c2.176 15.36-7.424 15.744-18.944 16.192-14.976 0.64-16.128-7.552-16.448-19.328-0.384-14.848 7.68-16.192 19.52-16.512 14.912-0.448 17.216 7.68 15.872 19.648M190.848 288.896c-12.288 1.664-15.616-4.992-15.168-16 0.32-8.64 1.728-15.616 12.608-16 10.816-0.32 17.152 3.392 17.472 15.232 0.256 10.624-2.24 17.92-14.912 16.768M333.888 150.016c0.128 10.368-6.144 13.44-15.296 13.312-10.432-0.128-17.728-4.032-17.024-15.744 0.64-11.52 9.664-10.304 17.472-10.56 8.96-0.32 15.744 1.92 14.848 12.992M252.8 220.928c-8.64 0.448-15.616-1.152-15.616-12.096 0-8.192 0.128-15.808 11.52-16.128 9.728-0.256 17.6 0.64 17.28 13.696-0.256 9.28-2.816 15.04-13.184 14.528M266.112 150.272c-0.32 7.68-3.712 12.544-12.032 12.032-8.32-0.512-10.88-5.952-11.008-13.44-0.064-8.32 4.352-11.52 12.288-11.392 8.576 0.192 11.328 4.992 10.752 12.8M186.496 198.336c6.976 0 13.376 1.024 14.208 9.856 0.768 8.064-3.2 12.352-11.008 12.992-7.168 0.512-13.248-0.64-13.632-9.792-0.384-7.424 1.28-13.184 10.432-13.056M191.36 143.552c2.752 2.432 6.784 4.288 6.784 6.208 0 5.504-4.48 7.68-9.216 6.336-2.24-0.64-3.584-4.48-5.312-6.912l7.68-5.632" fill="#FFFFFF"></path></svg> diff --git a/ui/public/imgs/providers/webhook.svg b/ui/public/imgs/providers/webhook.svg index 2ca5bff3..6c5eaee1 100644 --- a/ui/public/imgs/providers/webhook.svg +++ b/ui/public/imgs/providers/webhook.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="19933" width="200" height="200"><path d="M749.1 515.5c-23.8 0.1-47.3 4.5-69.6 12.9l-80.1-159.5c28.6-31.1 31.5-78 6.9-112.3-24.6-34.4-69.9-46.8-108.5-29.7s-60.1 58.8-51.3 100.2c8.7 41.3 45.3 70.9 87.5 70.8 3.3 0.2 6.6 0.2 9.8 0l107.6 214.5 27.8-13.9c49.1-28.4 110.6-24.4 155.6 10.2 45 34.6 64.7 92.9 49.9 147.7-14.8 54.8-61.2 95.2-117.5 102.4-56.3 7.2-111.4-20.3-139.5-69.6l-54.3 31.6c49.4 85.6 153.2 123.4 246.1 89.5 92.9-33.9 148-129.6 130.6-227S848 514.9 749.1 515.2v0.3z" fill="#3296FA" p-id="19934"></path><path d="M404.3 463.6L295.7 630.2c-6.8-1.4-13.7-2-20.7-2-41.6-0.1-77.8 28.2-87.7 68.6-9.8 40.4 9.3 82.3 46.3 101.3s82.2 10.2 109.3-21.3 29.1-77.5 4.8-111.2l142.5-218.9-26.1-17c-49.7-28.2-77.4-83.7-70.1-140.3 7.3-56.7 48-103.3 103.2-118.1 55.2-14.8 113.8 5.2 148.5 50.6 34.6 45.4 38.4 107.3 9.5 156.6l54.3 31.2c18.1-30.9 27.6-66 27.5-101.8 1-94.9-63.7-177.9-156.1-200.1-92.3-22.2-187.7 22.4-229.9 107.4s-20.1 188 53.4 248.1v0.3z" fill="#3296FA" p-id="19935"></path><path d="M665.3 749.3c15.1 40.6 57.2 64.6 99.8 57 42.7-7.7 73.7-44.8 73.7-88.2 0-43.4-31.1-80.5-73.7-88.2s-84.7 16.3-99.8 57H415.2v31.2c0 77.6-62.9 140.5-140.5 140.5s-140.5-62.9-140.5-140.5 62.9-140.5 140.5-140.5v-63.1c-108.6-0.6-198.6 84.2-204.4 192.7s74.5 202.4 182.5 213.5c108 11.1 205.8-64.6 222.1-172l190.4 0.6z" fill="#3296FA" p-id="19936"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="19933" width="200" height="200"><path d="M749.1 515.5c-23.8 0.1-47.3 4.5-69.6 12.9l-80.1-159.5c28.6-31.1 31.5-78 6.9-112.3-24.6-34.4-69.9-46.8-108.5-29.7s-60.1 58.8-51.3 100.2c8.7 41.3 45.3 70.9 87.5 70.8 3.3 0.2 6.6 0.2 9.8 0l107.6 214.5 27.8-13.9c49.1-28.4 110.6-24.4 155.6 10.2 45 34.6 64.7 92.9 49.9 147.7-14.8 54.8-61.2 95.2-117.5 102.4-56.3 7.2-111.4-20.3-139.5-69.6l-54.3 31.6c49.4 85.6 153.2 123.4 246.1 89.5 92.9-33.9 148-129.6 130.6-227S848 514.9 749.1 515.2v0.3z" fill="#3296FA" p-id="19934"></path><path d="M404.3 463.6L295.7 630.2c-6.8-1.4-13.7-2-20.7-2-41.6-0.1-77.8 28.2-87.7 68.6-9.8 40.4 9.3 82.3 46.3 101.3s82.2 10.2 109.3-21.3 29.1-77.5 4.8-111.2l142.5-218.9-26.1-17c-49.7-28.2-77.4-83.7-70.1-140.3 7.3-56.7 48-103.3 103.2-118.1 55.2-14.8 113.8 5.2 148.5 50.6 34.6 45.4 38.4 107.3 9.5 156.6l54.3 31.2c18.1-30.9 27.6-66 27.5-101.8 1-94.9-63.7-177.9-156.1-200.1-92.3-22.2-187.7 22.4-229.9 107.4s-20.1 188 53.4 248.1v0.3z" fill="#3296FA" p-id="19935"></path><path d="M665.3 749.3c15.1 40.6 57.2 64.6 99.8 57 42.7-7.7 73.7-44.8 73.7-88.2 0-43.4-31.1-80.5-73.7-88.2s-84.7 16.3-99.8 57H415.2v31.2c0 77.6-62.9 140.5-140.5 140.5s-140.5-62.9-140.5-140.5 62.9-140.5 140.5-140.5v-63.1c-108.6-0.6-198.6 84.2-204.4 192.7s74.5 202.4 182.5 213.5c108 11.1 205.8-64.6 222.1-172l190.4 0.6z" fill="#3296FA" p-id="19936"></path></svg> diff --git a/ui/public/imgs/providers/westcn.svg b/ui/public/imgs/providers/westcn.svg index 9ea5a83c..2ad4fa58 100644 --- a/ui/public/imgs/providers/westcn.svg +++ b/ui/public/imgs/providers/westcn.svg @@ -1 +1 @@ -<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M735.899826 924.582957a467.656348 467.656348 0 0 1-223.944348 56.765217c-81.029565 0-157.295304-20.569043-223.855304-56.765217H735.944348z m111.081739-83.878957c-15.582609 15.894261-32.322783 30.675478-49.997913 44.210087H227.06087a467.166609 467.166609 0 0 1-49.997913-44.210087h669.96313zM912.517565 756.869565c-9.349565 15.36-19.589565 30.052174-30.675478 44.165565H142.157913a475.225043 475.225043 0 0 1-30.675478-44.165565h801.03513z m66.159305-294.956522a467.033043 467.033043 0 0 1-44.432696 255.198609H89.755826a467.033043 467.033043 0 0 1-44.477217-254.664348l138.106434 2.715826 180.313044 201.19374 147.945739-173.100522 183.563131 179.734261 156.850086-211.033044h126.619827zM512 42.651826c204.176696 0 377.856 130.359652 442.412522 312.409044H824.987826l-131.650783 157.161739-177.730782-164.062609-156.493913 165.888L235.52 356.173913H69.186783C133.431652 173.545739 307.46713 42.651826 512 42.651826z" fill="#0070B6"></path></svg> +<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M735.899826 924.582957a467.656348 467.656348 0 0 1-223.944348 56.765217c-81.029565 0-157.295304-20.569043-223.855304-56.765217H735.944348z m111.081739-83.878957c-15.582609 15.894261-32.322783 30.675478-49.997913 44.210087H227.06087a467.166609 467.166609 0 0 1-49.997913-44.210087h669.96313zM912.517565 756.869565c-9.349565 15.36-19.589565 30.052174-30.675478 44.165565H142.157913a475.225043 475.225043 0 0 1-30.675478-44.165565h801.03513z m66.159305-294.956522a467.033043 467.033043 0 0 1-44.432696 255.198609H89.755826a467.033043 467.033043 0 0 1-44.477217-254.664348l138.106434 2.715826 180.313044 201.19374 147.945739-173.100522 183.563131 179.734261 156.850086-211.033044h126.619827zM512 42.651826c204.176696 0 377.856 130.359652 442.412522 312.409044H824.987826l-131.650783 157.161739-177.730782-164.062609-156.493913 165.888L235.52 356.173913H69.186783C133.431652 173.545739 307.46713 42.651826 512 42.651826z" fill="#0070B6"></path></svg> diff --git a/ui/src/components/Version.tsx b/ui/src/components/Version.tsx index 4832cce8..e9245b1c 100644 --- a/ui/src/components/Version.tsx +++ b/ui/src/components/Version.tsx @@ -1,8 +1,10 @@ +import { memo } from "react"; import { useTranslation } from "react-i18next"; import { ReadOutlined as ReadOutlinedIcon } from "@ant-design/icons"; -import { Divider, Space, Typography } from "antd"; +import { Badge, Divider, Space, Typography } from "antd"; import { version } from "@/domain/version"; +import { useVersionChecker } from "@/hooks"; export type VersionProps = { className?: string; @@ -12,6 +14,8 @@ export type VersionProps = { const Version = ({ className, style }: VersionProps) => { const { t } = useTranslation(); + const { hasNewVersion } = useVersionChecker(); + return ( <Space className={className} style={style} size={4}> <Typography.Link type="secondary" href="https://docs.certimate.me" target="_blank"> @@ -20,12 +24,16 @@ const Version = ({ className, style }: VersionProps) => { <span>{t("common.menu.document")}</span> </div> </Typography.Link> + <Divider type="vertical" /> - <Typography.Link type="secondary" href="https://github.com/usual2970/certimate/releases" target="_blank"> - {version} - </Typography.Link> + + <Badge styles={{ indicator: { transform: "scale(0.75) translate(50%, -50%)" } }} count={hasNewVersion ? "NEW" : undefined}> + <Typography.Link type="secondary" href="https://github.com/usual2970/certimate/releases" target="_blank"> + {version} + </Typography.Link> + </Badge> </Space> ); }; -export default Version; +export default memo(Version); diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index d9632f28..47c6d8c6 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -14,23 +14,32 @@ import AccessFormAliyunConfig from "./AccessFormAliyunConfig"; import AccessFormAWSConfig from "./AccessFormAWSConfig"; import AccessFormAzureConfig from "./AccessFormAzureConfig"; import AccessFormBaiduCloudConfig from "./AccessFormBaiduCloudConfig"; +import AccessFormBaishanConfig from "./AccessFormBaishanConfig"; import AccessFormBaotaPanelConfig from "./AccessFormBaotaPanelConfig"; import AccessFormBytePlusConfig from "./AccessFormBytePlusConfig"; +import AccessFormCacheFlyConfig from "./AccessFormCacheFlyConfig"; +import AccessFormCdnflyConfig from "./AccessFormCdnflyConfig"; import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig"; import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig"; +import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig"; +import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig"; import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; import AccessFormEdgioConfig from "./AccessFormEdgioConfig"; +import AccessFormGcoreConfig from "./AccessFormGcoreConfig"; import AccessFormGnameConfig from "./AccessFormGnameConfig"; import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; +import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; import AccessFormLocalConfig from "./AccessFormLocalConfig"; +import AccessFormNamecheapConfig from "./AccessFormNamecheapConfig"; import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig"; import AccessFormNameSiloConfig from "./AccessFormNameSiloConfig"; import AccessFormNS1Config from "./AccessFormNS1Config"; import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig"; import AccessFormQiniuConfig from "./AccessFormQiniuConfig"; import AccessFormRainYunConfig from "./AccessFormRainYunConfig"; +import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig"; import AccessFormSSHConfig from "./AccessFormSSHConfig"; import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig"; import AccessFormUCloudConfig from "./AccessFormUCloudConfig"; @@ -100,16 +109,28 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className, return <AccessFormAzureConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.BAIDUCLOUD: return <AccessFormBaiduCloudConfig {...nestedFormProps} />; + case ACCESS_PROVIDERS.BAISHAN: + return <AccessFormBaishanConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.BAOTAPANEL: return <AccessFormBaotaPanelConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.BYTEPLUS: return <AccessFormBytePlusConfig {...nestedFormProps} />; + case ACCESS_PROVIDERS.CACHEFLY: + return <AccessFormCacheFlyConfig {...nestedFormProps} />; + case ACCESS_PROVIDERS.CDNFLY: + return <AccessFormCdnflyConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.CLOUDFLARE: return <AccessFormCloudflareConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.CLOUDNS: return <AccessFormClouDNSConfig {...nestedFormProps} />; + case ACCESS_PROVIDERS.CMCCCLOUD: + return <AccessFormCMCCCloudConfig {...nestedFormProps} />; + case ACCESS_PROVIDERS.DNSLA: + return <AccessFormDNSLAConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.DOGECLOUD: return <AccessFormDogeCloudConfig {...nestedFormProps} />; + case ACCESS_PROVIDERS.GCORE: + return <AccessFormGcoreConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.GNAME: return <AccessFormGnameConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.GODADDY: @@ -118,10 +139,14 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className, return <AccessFormEdgioConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.HUAWEICLOUD: return <AccessFormHuaweiCloudConfig {...nestedFormProps} />; + case ACCESS_PROVIDERS.JDCLOUD: + return <AccessFormJDCloudConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.KUBERNETES: return <AccessFormKubernetesConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.LOCAL: return <AccessFormLocalConfig {...nestedFormProps} />; + case ACCESS_PROVIDERS.NAMECHEAP: + return <AccessFormNamecheapConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.NAMEDOTCOM: return <AccessFormNameDotComConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.NAMESILO: @@ -134,6 +159,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className, return <AccessFormQiniuConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.RAINYUN: return <AccessFormRainYunConfig {...nestedFormProps} />; + case ACCESS_PROVIDERS.SAFELINE: + return <AccessFormSafeLineConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.SSH: return <AccessFormSSHConfig {...nestedFormProps} />; case ACCESS_PROVIDERS.TENCENTCLOUD: diff --git a/ui/src/components/access/AccessFormBaishanConfig.tsx b/ui/src/components/access/AccessFormBaishanConfig.tsx new file mode 100644 index 00000000..0450d9cd --- /dev/null +++ b/ui/src/components/access/AccessFormBaishanConfig.tsx @@ -0,0 +1,56 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForBaishan } from "@/domain/access"; + +type AccessFormBaishanConfigFieldValues = Nullish<AccessConfigForBaishan>; + +export type AccessFormBaishanConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormBaishanConfigFieldValues; + onValuesChange?: (values: AccessFormBaishanConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormBaishanConfigFieldValues => { + return { + apiToken: "", + }; +}; + +const AccessFormBaishanConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormBaishanConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiToken: z + .string() + .min(1, t("access.form.baishan_api_token.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item name="apiToken" label={t("access.form.baishan_api_token.label")} rules={[formRule]}> + <Input.Password autoComplete="new-password" placeholder={t("access.form.baishan_api_token.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default AccessFormBaishanConfig; diff --git a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx index 4f619c1c..accd80d0 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: "", + apiUrl: "http://<your-ipaddr>:8888/", apiKey: "", }; }; diff --git a/ui/src/components/access/AccessFormCMCCCloudConfig.tsx b/ui/src/components/access/AccessFormCMCCCloudConfig.tsx new file mode 100644 index 00000000..9bc6e615 --- /dev/null +++ b/ui/src/components/access/AccessFormCMCCCloudConfig.tsx @@ -0,0 +1,75 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; +import { type AccessConfigForCMCCCloud } from "@/domain/access"; + +type AccessFormCMCCCloudConfigFieldValues = Nullish<AccessConfigForCMCCCloud>; + +export type AccessFormCMCCCloudConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormCMCCCloudConfigFieldValues; + onValuesChange?: (values: AccessFormCMCCCloudConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormCMCCCloudConfigFieldValues => { + return { + accessKeyId: "", + accessKeySecret: "", + }; +}; + +const AccessFormCMCCCloudConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange: onValuesChange }: AccessFormCMCCCloudConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + accessKeyId: z + .string() + .min(1, t("access.form.cmcccloud_access_key_id.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + accessKeySecret: z + .string() + .min(1, t("access.form.cmcccloud_access_key_secret.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="accessKeyId" + label={t("access.form.cmcccloud_access_key_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.cmcccloud_access_key_id.tooltip") }}></span>} + > + <Input autoComplete="new-password" placeholder={t("access.form.cmcccloud_access_key_id.placeholder")} /> + </Form.Item> + + <Form.Item + name="accessKeySecret" + label={t("access.form.cmcccloud_access_key_secret.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.cmcccloud_access_key_secret.tooltip") }}></span>} + > + <Input.Password autoComplete="new-password" placeholder={t("access.form.cmcccloud_access_key_secret.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default AccessFormCMCCCloudConfig; diff --git a/ui/src/components/access/AccessFormCacheFlyConfig.tsx b/ui/src/components/access/AccessFormCacheFlyConfig.tsx new file mode 100644 index 00000000..b3172785 --- /dev/null +++ b/ui/src/components/access/AccessFormCacheFlyConfig.tsx @@ -0,0 +1,56 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForCacheFly } from "@/domain/access"; + +type AccessFormCacheFlyConfigFieldValues = Nullish<AccessConfigForCacheFly>; + +export type AccessFormCacheFlyConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormCacheFlyConfigFieldValues; + onValuesChange?: (values: AccessFormCacheFlyConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormCacheFlyConfigFieldValues => { + return { + apiToken: "", + }; +}; + +const AccessFormCacheFlyConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormCacheFlyConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiToken: z + .string() + .min(1, t("access.form.cachefly_api_token.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item name="apiToken" label={t("access.form.cachefly_api_token.label")} rules={[formRule]}> + <Input.Password autoComplete="new-password" placeholder={t("access.form.cachefly_api_token.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default AccessFormCacheFlyConfig; diff --git a/ui/src/components/access/AccessFormCdnflyConfig.tsx b/ui/src/components/access/AccessFormCdnflyConfig.tsx new file mode 100644 index 00000000..a9dd1139 --- /dev/null +++ b/ui/src/components/access/AccessFormCdnflyConfig.tsx @@ -0,0 +1,87 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForCdnfly } from "@/domain/access"; + +type AccessFormCdnflyConfigFieldValues = Nullish<AccessConfigForCdnfly>; + +export type AccessFormCdnflyConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormCdnflyConfigFieldValues; + onValuesChange?: (values: AccessFormCdnflyConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormCdnflyConfigFieldValues => { + return { + apiUrl: "http://<your-ipaddr>:88/", + apiKey: "", + apiSecret: "", + }; +}; + +const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormCdnflyConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiUrl: z.string().url(t("common.errmsg.url_invalid")), + apiKey: z + .string() + .min(1, t("access.form.cdnfly_api_key.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + apiSecret: z + .string() + .min(1, t("access.form.cdnfly_api_secret.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="apiUrl" + label={t("access.form.cdnfly_api_url.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.cdnfly_api_url.tooltip") }}></span>} + > + <Input placeholder={t("access.form.cdnfly_api_url.placeholder")} /> + </Form.Item> + + <Form.Item + name="apiKey" + label={t("access.form.cdnfly_api_key.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.cdnfly_api_key.tooltip") }}></span>} + > + <Input autoComplete="new-password" placeholder={t("access.form.cdnfly_api_key.placeholder")} /> + </Form.Item> + + <Form.Item + name="apiSecret" + label={t("access.form.cdnfly_api_secret.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.cdnfly_api_secret.tooltip") }}></span>} + > + <Input.Password autoComplete="new-password" placeholder={t("access.form.cdnfly_api_secret.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default AccessFormCdnflyConfig; diff --git a/ui/src/components/access/AccessFormDNSLAConfig.tsx b/ui/src/components/access/AccessFormDNSLAConfig.tsx new file mode 100644 index 00000000..df8403ea --- /dev/null +++ b/ui/src/components/access/AccessFormDNSLAConfig.tsx @@ -0,0 +1,76 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForDNSLA } from "@/domain/access"; + +type AccessFormDNSLAConfigFieldValues = Nullish<AccessConfigForDNSLA>; + +export type AccessFormDNSLAConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormDNSLAConfigFieldValues; + onValuesChange?: (values: AccessFormDNSLAConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormDNSLAConfigFieldValues => { + return { + apiId: "", + apiSecret: "", + }; +}; + +const AccessFormDNSLAConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange: onValuesChange }: AccessFormDNSLAConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiId: z + .string() + .min(1, t("access.form.dnsla_api_id.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + apiSecret: z + .string() + .min(1, t("access.form.dnsla_api_secret.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="apiId" + label={t("access.form.dnsla_api_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.dnsla_api_id.tooltip") }}></span>} + > + <Input autoComplete="new-password" placeholder={t("access.form.dnsla_api_id.placeholder")} /> + </Form.Item> + + <Form.Item + name="apiSecret" + label={t("access.form.dnsla_api_secret.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.dnsla_api_secret.tooltip") }}></span>} + > + <Input.Password autoComplete="new-password" placeholder={t("access.form.dnsla_api_secret.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default AccessFormDNSLAConfig; diff --git a/ui/src/components/access/AccessFormGcoreConfig.tsx b/ui/src/components/access/AccessFormGcoreConfig.tsx new file mode 100644 index 00000000..8619f7fe --- /dev/null +++ b/ui/src/components/access/AccessFormGcoreConfig.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"; + +import { type AccessConfigForGcore } from "@/domain/access"; + +type AccessFormGcoreConfigFieldValues = Nullish<AccessConfigForGcore>; + +export type AccessFormGcoreConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormGcoreConfigFieldValues; + onValuesChange?: (values: AccessFormGcoreConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormGcoreConfigFieldValues => { + return { + apiToken: "", + }; +}; + +const AccessFormGcoreConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormGcoreConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiToken: z + .string() + .min(1, t("access.form.gcore_api_token.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="apiToken" + label={t("access.form.gcore_api_token.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.gcore_api_token.tooltip") }}></span>} + > + <Input.Password autoComplete="new-password" placeholder={t("access.form.gcore_api_token.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default AccessFormGcoreConfig; diff --git a/ui/src/components/access/AccessFormJDCloudConfig.tsx b/ui/src/components/access/AccessFormJDCloudConfig.tsx new file mode 100644 index 00000000..7ab6b167 --- /dev/null +++ b/ui/src/components/access/AccessFormJDCloudConfig.tsx @@ -0,0 +1,76 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForJDCloud } from "@/domain/access"; + +type AccessFormJDCloudConfigFieldValues = Nullish<AccessConfigForJDCloud>; + +export type AccessFormJDCloudConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormJDCloudConfigFieldValues; + onValuesChange?: (values: AccessFormJDCloudConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormJDCloudConfigFieldValues => { + return { + accessKeyId: "", + accessKeySecret: "", + }; +}; + +const AccessFormJDCloudConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange: onValuesChange }: AccessFormJDCloudConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + accessKeyId: z + .string() + .min(1, t("access.form.jdcloud_access_key_id.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + accessKeySecret: z + .string() + .min(1, t("access.form.jdcloud_access_key_secret.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="accessKeyId" + label={t("access.form.jdcloud_access_key_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.jdcloud_access_key_id.tooltip") }}></span>} + > + <Input autoComplete="new-password" placeholder={t("access.form.jdcloud_access_key_id.placeholder")} /> + </Form.Item> + + <Form.Item + name="accessKeySecret" + label={t("access.form.jdcloud_access_key_secret.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.jdcloud_access_key_secret.tooltip") }}></span>} + > + <Input.Password autoComplete="new-password" placeholder={t("access.form.jdcloud_access_key_secret.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default AccessFormJDCloudConfig; diff --git a/ui/src/components/access/AccessFormNamecheapConfig.tsx b/ui/src/components/access/AccessFormNamecheapConfig.tsx new file mode 100644 index 00000000..d6a79f2a --- /dev/null +++ b/ui/src/components/access/AccessFormNamecheapConfig.tsx @@ -0,0 +1,76 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForNamecheap } from "@/domain/access"; + +type AccessFormNamecheapConfigFieldValues = Nullish<AccessConfigForNamecheap>; + +export type AccessFormNamecheapConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormNamecheapConfigFieldValues; + onValuesChange?: (values: AccessFormNamecheapConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormNamecheapConfigFieldValues => { + return { + username: "", + apiKey: "", + }; +}; + +const AccessFormNamecheapConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormNamecheapConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + username: z + .string() + .min(1, t("access.form.namecheap_username.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + apiKey: z + .string() + .min(1, t("access.form.namecheap_api_key.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="username" + label={t("access.form.namecheap_username.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.namecheap_username.tooltip") }}></span>} + > + <Input autoComplete="new-password" placeholder={t("access.form.namecheap_username.placeholder")} /> + </Form.Item> + + <Form.Item + name="apiKey" + label={t("access.form.namecheap_api_key.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.namecheap_api_key.tooltip") }}></span>} + > + <Input.Password autoComplete="new-password" placeholder={t("access.form.namecheap_api_key.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default AccessFormNamecheapConfig; diff --git a/ui/src/components/access/AccessFormPowerDNSConfig.tsx b/ui/src/components/access/AccessFormPowerDNSConfig.tsx index 83a22b9b..e93980c7 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: "", + apiUrl: "http://<your-ipaddr>:8082/", apiKey: "", }; }; diff --git a/ui/src/components/access/AccessFormSafeLineConfig.tsx b/ui/src/components/access/AccessFormSafeLineConfig.tsx new file mode 100644 index 00000000..b9de115c --- /dev/null +++ b/ui/src/components/access/AccessFormSafeLineConfig.tsx @@ -0,0 +1,72 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForSafeLine } from "@/domain/access"; + +type AccessFormSafeLineConfigFieldValues = Nullish<AccessConfigForSafeLine>; + +export type AccessFormSafeLineConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormSafeLineConfigFieldValues; + onValuesChange?: (values: AccessFormSafeLineConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormSafeLineConfigFieldValues => { + return { + apiUrl: "http://<your-ipaddr>:9443/", + apiToken: "", + }; +}; + +const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormSafeLineConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiUrl: z.string().url(t("common.errmsg.url_invalid")), + apiToken: z + .string() + .min(1, t("access.form.safeline_api_token.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="apiUrl" + label={t("access.form.safeline_api_url.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.safeline_api_url.tooltip") }}></span>} + > + <Input placeholder={t("access.form.safeline_api_url.placeholder")} /> + </Form.Item> + + <Form.Item + name="apiToken" + label={t("access.form.safeline_api_token.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.safeline_api_token.tooltip") }}></span>} + > + <Input.Password autoComplete="new-password" placeholder={t("access.form.safeline_api_token.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default AccessFormSafeLineConfig; diff --git a/ui/src/components/provider/AccessProviderSelect.tsx b/ui/src/components/provider/AccessProviderSelect.tsx index 04b859ac..97cd35fd 100644 --- a/ui/src/components/provider/AccessProviderSelect.tsx +++ b/ui/src/components/provider/AccessProviderSelect.tsx @@ -47,12 +47,18 @@ const AccessProviderSelect = (props: AccessProviderSelectProps) => { return ( <Select {...props} + filterOption={(inputValue, option) => { + if (!option) return false; + + const value = inputValue.toLowerCase(); + return option.value.toLowerCase().includes(value) || option.label.toLowerCase().includes(value); + }} labelRender={({ label, value }) => { - if (label) { - return renderOption(value as string); + if (!label) { + return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>; } - return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>; + return renderOption(value as string); }} options={options} optionFilterProp={undefined} diff --git a/ui/src/components/provider/ApplyDNSProviderSelect.tsx b/ui/src/components/provider/ApplyDNSProviderSelect.tsx index 3b76feff..c88cfebf 100644 --- a/ui/src/components/provider/ApplyDNSProviderSelect.tsx +++ b/ui/src/components/provider/ApplyDNSProviderSelect.tsx @@ -33,12 +33,18 @@ const ApplyDNSProviderSelect = (props: ApplyDNSProviderSelectProps) => { return ( <Select {...props} + filterOption={(inputValue, option) => { + if (!option) return false; + + const value = inputValue.toLowerCase(); + return option.value.toLowerCase().includes(value) || option.label.toLowerCase().includes(value); + }} labelRender={({ label, value }) => { - if (label) { - return renderOption(value as string); + if (!label) { + return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>; } - return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>; + return renderOption(value as string); }} options={options} optionFilterProp={undefined} diff --git a/ui/src/components/provider/DeployProviderPicker.tsx b/ui/src/components/provider/DeployProviderPicker.tsx index af373255..57ba39a6 100644 --- a/ui/src/components/provider/DeployProviderPicker.tsx +++ b/ui/src/components/provider/DeployProviderPicker.tsx @@ -63,7 +63,7 @@ const DeployProviderPicker = ({ className, style, autoFocus, placeholder, onSele DEPLOY_CATEGORIES.STORAGE, DEPLOY_CATEGORIES.LOADBALANCE, DEPLOY_CATEGORIES.FIREWALL, - DEPLOY_CATEGORIES.LIVE, + DEPLOY_CATEGORIES.AV, DEPLOY_CATEGORIES.WEBSITE, DEPLOY_CATEGORIES.OTHER, ].map((key) => ({ diff --git a/ui/src/components/provider/DeployProviderSelect.tsx b/ui/src/components/provider/DeployProviderSelect.tsx index 54e3643b..cc2b13ea 100644 --- a/ui/src/components/provider/DeployProviderSelect.tsx +++ b/ui/src/components/provider/DeployProviderSelect.tsx @@ -33,12 +33,18 @@ const DeployProviderSelect = (props: DeployProviderSelectProps) => { return ( <Select {...props} + filterOption={(inputValue, option) => { + if (!option) return false; + + const value = inputValue.toLowerCase(); + return option.value.toLowerCase().includes(value) || option.label.toLowerCase().includes(value); + }} labelRender={({ label, value }) => { - if (label) { - return renderOption(value as string); + if (!label) { + return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>; } - return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>; + return renderOption(value as string); }} options={options} optionFilterProp={undefined} diff --git a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx index 5a0472dd..c714e197 100644 --- a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx @@ -35,6 +35,7 @@ import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/val import ApplyNodeConfigFormAWSRoute53Config from "./ApplyNodeConfigFormAWSRoute53Config"; import ApplyNodeConfigFormHuaweiCloudDNSConfig from "./ApplyNodeConfigFormHuaweiCloudDNSConfig"; +import ApplyNodeConfigFormJDCloudDNSConfig from "./ApplyNodeConfigFormJDCloudDNSConfig"; type ApplyNodeConfigFormFieldValues = Partial<WorkflowNodeConfigForApply>; @@ -70,6 +71,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon const formSchema = z.object({ domains: z.string({ message: t("workflow_node.apply.form.domains.placeholder") }).refine((v) => { + if (!v) return false; return String(v) .split(MULTIPLE_INPUT_DELIMITER) .every((e) => validDomainName(e, { allowWildcard: true })); @@ -144,6 +146,9 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon case APPLY_DNS_PROVIDERS.HUAWEICLOUD: case APPLY_DNS_PROVIDERS.HUAWEICLOUD_DNS: return <ApplyNodeConfigFormHuaweiCloudDNSConfig {...nestedFormProps} />; + case APPLY_DNS_PROVIDERS.JDCLOUD: + case APPLY_DNS_PROVIDERS.JDCLOUD_DNS: + return <ApplyNodeConfigFormJDCloudDNSConfig {...nestedFormProps} />; } }, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]); diff --git a/ui/src/components/workflow/node/ApplyNodeConfigFormJDCloudDNSConfig.tsx b/ui/src/components/workflow/node/ApplyNodeConfigFormJDCloudDNSConfig.tsx new file mode 100644 index 00000000..dba0b56c --- /dev/null +++ b/ui/src/components/workflow/node/ApplyNodeConfigFormJDCloudDNSConfig.tsx @@ -0,0 +1,66 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type ApplyNodeConfigFormJDCloudDNSConfigFieldValues = Nullish<{ + regionId: string; +}>; + +export type ApplyNodeConfigFormJDCloudDNSConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: ApplyNodeConfigFormJDCloudDNSConfigFieldValues; + onValuesChange?: (values: ApplyNodeConfigFormJDCloudDNSConfigFieldValues) => void; +}; + +const initFormModel = (): ApplyNodeConfigFormJDCloudDNSConfigFieldValues => { + return { + regionId: "cn-north-1", + }; +}; + +const ApplyNodeConfigFormJDCloudDNSConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: ApplyNodeConfigFormJDCloudDNSConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + regionId: z + .string({ message: t("workflow_node.apply.form.jdcloud_dns_region_id.placeholder") }) + .nonempty(t("workflow_node.apply.form.jdcloud_dns_region_id.placeholder")) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="regionId" + label={t("workflow_node.apply.form.jdcloud_dns_region_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.jdcloud_dns_region_id.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.apply.form.jdcloud_dns_region_id.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default ApplyNodeConfigFormJDCloudDNSConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index 78d1b99d..b9f65b08 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -24,19 +24,30 @@ import DeployNodeConfigFormAliyunESAConfig from "./DeployNodeConfigFormAliyunESA import DeployNodeConfigFormAliyunLiveConfig from "./DeployNodeConfigFormAliyunLiveConfig"; import DeployNodeConfigFormAliyunNLBConfig from "./DeployNodeConfigFormAliyunNLBConfig"; import DeployNodeConfigFormAliyunOSSConfig from "./DeployNodeConfigFormAliyunOSSConfig"; +import DeployNodeConfigFormAliyunVODConfig from "./DeployNodeConfigFormAliyunVODConfig"; import DeployNodeConfigFormAliyunWAFConfig from "./DeployNodeConfigFormAliyunWAFConfig"; import DeployNodeConfigFormAWSCloudFrontConfig from "./DeployNodeConfigFormAWSCloudFrontConfig"; import DeployNodeConfigFormBaiduCloudCDNConfig from "./DeployNodeConfigFormBaiduCloudCDNConfig"; +import DeployNodeConfigFormBaishanCDNConfig from "./DeployNodeConfigFormBaishanCDNConfig"; +import DeployNodeConfigFormBaotaPanelConsoleConfig from "./DeployNodeConfigFormBaotaPanelConsoleConfig"; import DeployNodeConfigFormBaotaPanelSiteConfig from "./DeployNodeConfigFormBaotaPanelSiteConfig"; import DeployNodeConfigFormBytePlusCDNConfig from "./DeployNodeConfigFormBytePlusCDNConfig"; +import DeployNodeConfigFormCdnflyConfig from "./DeployNodeConfigFormCdnflyConfig"; import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig"; import DeployNodeConfigFormEdgioApplicationsConfig from "./DeployNodeConfigFormEdgioApplicationsConfig"; +import DeployNodeConfigFormGcoreCDNConfig from "./DeployNodeConfigFormGcoreCDNConfig"; import DeployNodeConfigFormHuaweiCloudCDNConfig from "./DeployNodeConfigFormHuaweiCloudCDNConfig"; import DeployNodeConfigFormHuaweiCloudELBConfig from "./DeployNodeConfigFormHuaweiCloudELBConfig"; +import DeployNodeConfigFormHuaweiCloudWAFConfig from "./DeployNodeConfigFormHuaweiCloudWAFConfig"; +import DeployNodeConfigFormJDCloudALBConfig from "./DeployNodeConfigFormJDCloudALBConfig"; +import DeployNodeConfigFormJDCloudCDNConfig from "./DeployNodeConfigFormJDCloudCDNConfig"; +import DeployNodeConfigFormJDCloudLiveConfig from "./DeployNodeConfigFormJDCloudLiveConfig"; +import DeployNodeConfigFormJDCloudVODConfig from "./DeployNodeConfigFormJDCloudVODConfig"; import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig"; import DeployNodeConfigFormLocalConfig from "./DeployNodeConfigFormLocalConfig"; import DeployNodeConfigFormQiniuCDNConfig from "./DeployNodeConfigFormQiniuCDNConfig"; import DeployNodeConfigFormQiniuPiliConfig from "./DeployNodeConfigFormQiniuPiliConfig"; +import DeployNodeConfigFormSafeLineConfig from "./DeployNodeConfigFormSafeLineConfig"; import DeployNodeConfigFormSSHConfig from "./DeployNodeConfigFormSSHConfig.tsx"; import DeployNodeConfigFormTencentCloudCDNConfig from "./DeployNodeConfigFormTencentCloudCDNConfig.tsx"; import DeployNodeConfigFormTencentCloudCLBConfig from "./DeployNodeConfigFormTencentCloudCLBConfig.tsx"; @@ -45,11 +56,14 @@ import DeployNodeConfigFormTencentCloudCSSConfig from "./DeployNodeConfigFormTen import DeployNodeConfigFormTencentCloudECDNConfig from "./DeployNodeConfigFormTencentCloudECDNConfig.tsx"; import DeployNodeConfigFormTencentCloudEOConfig from "./DeployNodeConfigFormTencentCloudEOConfig.tsx"; import DeployNodeConfigFormTencentCloudSSLDeployConfig from "./DeployNodeConfigFormTencentCloudSSLDeployConfig"; +import DeployNodeConfigFormTencentCloudVODConfig from "./DeployNodeConfigFormTencentCloudVODConfig"; +import DeployNodeConfigFormTencentCloudWAFConfig from "./DeployNodeConfigFormTencentCloudWAFConfig"; import DeployNodeConfigFormUCloudUCDNConfig from "./DeployNodeConfigFormUCloudUCDNConfig.tsx"; import DeployNodeConfigFormUCloudUS3Config from "./DeployNodeConfigFormUCloudUS3Config.tsx"; import DeployNodeConfigFormVolcEngineCDNConfig from "./DeployNodeConfigFormVolcEngineCDNConfig.tsx"; import DeployNodeConfigFormVolcEngineCLBConfig from "./DeployNodeConfigFormVolcEngineCLBConfig.tsx"; import DeployNodeConfigFormVolcEngineDCDNConfig from "./DeployNodeConfigFormVolcEngineDCDNConfig.tsx"; +import DeployNodeConfigFormVolcEngineImageXConfig from "./DeployNodeConfigFormVolcEngineImageXConfig.tsx"; import DeployNodeConfigFormVolcEngineLiveConfig from "./DeployNodeConfigFormVolcEngineLiveConfig.tsx"; import DeployNodeConfigFormVolcEngineTOSConfig from "./DeployNodeConfigFormVolcEngineTOSConfig.tsx"; import DeployNodeConfigFormWebhookConfig from "./DeployNodeConfigFormWebhookConfig.tsx"; @@ -97,10 +111,9 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode provider: z.string({ message: t("workflow_node.deploy.form.provider.placeholder") }).nonempty(t("workflow_node.deploy.form.provider.placeholder")), providerAccessId: z .string({ message: t("workflow_node.deploy.form.provider_access.placeholder") }) - .nonempty(t("workflow_node.deploy.form.provider_access.placeholder")) - .refine(() => !!formInst.getFieldValue("provider"), t("workflow_node.deploy.form.provider.placeholder")), + .nonempty(t("workflow_node.deploy.form.provider_access.placeholder")), providerConfig: z.any(), - skipOnLastSucceeded: z.boolean(), + skipOnLastSucceeded: z.boolean().nullish(), }); const formRule = createSchemaFieldRule(formSchema); const { form: formInst, formProps } = useAntdForm({ @@ -143,24 +156,44 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode return <DeployNodeConfigFormAliyunNLBConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.ALIYUN_OSS: return <DeployNodeConfigFormAliyunOSSConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.ALIYUN_VOD: + return <DeployNodeConfigFormAliyunVODConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.ALIYUN_WAF: return <DeployNodeConfigFormAliyunWAFConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.AWS_CLOUDFRONT: return <DeployNodeConfigFormAWSCloudFrontConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.BAIDUCLOUD_CDN: return <DeployNodeConfigFormBaiduCloudCDNConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.BAISHAN_CDN: + return <DeployNodeConfigFormBaishanCDNConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.BAOTAPANEL_CONSOLE: + return <DeployNodeConfigFormBaotaPanelConsoleConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.BAOTAPANEL_SITE: return <DeployNodeConfigFormBaotaPanelSiteConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.BYTEPLUS_CDN: return <DeployNodeConfigFormBytePlusCDNConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.CDNFLY: + return <DeployNodeConfigFormCdnflyConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.DOGECLOUD_CDN: return <DeployNodeConfigFormDogeCloudCDNConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.EDGIO_APPLICATIONS: return <DeployNodeConfigFormEdgioApplicationsConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.GCORE_CDN: + return <DeployNodeConfigFormGcoreCDNConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.HUAWEICLOUD_CDN: return <DeployNodeConfigFormHuaweiCloudCDNConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.HUAWEICLOUD_ELB: return <DeployNodeConfigFormHuaweiCloudELBConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.HUAWEICLOUD_WAF: + return <DeployNodeConfigFormHuaweiCloudWAFConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.JDCLOUD_ALB: + return <DeployNodeConfigFormJDCloudALBConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.JDCLOUD_CDN: + return <DeployNodeConfigFormJDCloudCDNConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.JDCLOUD_LIVE: + return <DeployNodeConfigFormJDCloudLiveConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.JDCLOUD_VOD: + return <DeployNodeConfigFormJDCloudVODConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.KUBERNETES_SECRET: return <DeployNodeConfigFormKubernetesSecretConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.LOCAL: @@ -169,6 +202,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode return <DeployNodeConfigFormQiniuCDNConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.QINIU_PILI: return <DeployNodeConfigFormQiniuPiliConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.SAFELINE: + return <DeployNodeConfigFormSafeLineConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.SSH: return <DeployNodeConfigFormSSHConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.TENCENTCLOUD_CDN: @@ -185,6 +220,10 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode return <DeployNodeConfigFormTencentCloudEOConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.TENCENTCLOUD_SSL_DEPLOY: return <DeployNodeConfigFormTencentCloudSSLDeployConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.TENCENTCLOUD_VOD: + return <DeployNodeConfigFormTencentCloudVODConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.TENCENTCLOUD_WAF: + return <DeployNodeConfigFormTencentCloudWAFConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.UCLOUD_UCDN: return <DeployNodeConfigFormUCloudUCDNConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.UCLOUD_US3: @@ -195,6 +234,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode return <DeployNodeConfigFormVolcEngineCLBConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.VOLCENGINE_DCDN: return <DeployNodeConfigFormVolcEngineDCDNConfig {...nestedFormProps} />; + case DEPLOY_PROVIDERS.VOLCENGINE_IMAGEX: + return <DeployNodeConfigFormVolcEngineImageXConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.VOLCENGINE_LIVE: return <DeployNodeConfigFormVolcEngineLiveConfig {...nestedFormProps} />; case DEPLOY_PROVIDERS.VOLCENGINE_TOS: @@ -219,13 +260,13 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode const oldValues = formInst.getFieldsValue(); const newValues: Record<string, unknown> = {}; for (const key in oldValues) { - if (key === "provider" || key === "providerAccessId" || key === "certificate") { + if (key === "provider" || key === "providerAccessId" || key === "certificate" || key === "skipOnLastSucceeded") { newValues[key] = oldValues[key]; } else { newValues[key] = undefined; } } - (formInst as FormInstance).setFieldsValue(newValues); + formInst.setFieldsValue(newValues); if (deployProvidersMap.get(fieldProvider)?.provider !== deployProvidersMap.get(value)?.provider) { formInst.setFieldValue("providerAccessId", undefined); @@ -275,7 +316,13 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode fallback={<DeployProviderPicker autoFocus placeholder={t("workflow_node.deploy.search.provider.placeholder")} onSelect={handleProviderPick} />} > <Form.Item name="provider" label={t("workflow_node.deploy.form.provider.label")} rules={[formRule]}> - <DeployProviderSelect allowClear placeholder={t("workflow_node.deploy.form.provider.placeholder")} showSearch onSelect={handleProviderSelect} /> + <DeployProviderSelect + allowClear + disabled={!!initialValues?.provider} + placeholder={t("workflow_node.deploy.form.provider.placeholder")} + showSearch + onSelect={handleProviderSelect} + /> </Form.Item> <Form.Item className="mb-0"> @@ -357,7 +404,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode </Show> </Form> - <Show when={!!fieldProvider}> + <Show when={!!nestedFormEl}> <Divider className="my-1"> <Typography.Text className="text-xs font-normal" type="secondary"> {t("workflow_node.deploy.form.params_config.label")} @@ -365,7 +412,9 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode </Divider> {nestedFormEl} + </Show> + <Show when={!!fieldProvider}> <Divider className="my-1"> <Typography.Text className="text-xs font-normal" type="secondary"> {t("workflow_node.deploy.form.strategy_config.label")} diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx index 05bc632c..065f752c 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx @@ -44,6 +44,7 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({ .nonempty(t("workflow_node.deploy.form.aliyun_cas_deploy_region.placeholder")) .trim(), resourceIds: z.string({ message: t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.placeholder") }).refine((v) => { + if (!v) return false; return String(v) .split(MULTIPLE_INPUT_DELIMITER) .every((e) => /^[1-9]\d*$/.test(e)); diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunESAConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunESAConfig.tsx index 338c0f97..f61485e2 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunESAConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunESAConfig.tsx @@ -5,7 +5,7 @@ import { z } from "zod"; type DeployNodeConfigFormAliyunESAConfigFieldValues = Nullish<{ region: string; - siteId: string; + siteId: string | number; }>; export type DeployNodeConfigFormAliyunESAConfigProps = { @@ -34,10 +34,9 @@ const DeployNodeConfigFormAliyunESAConfig = ({ .string({ message: t("workflow_node.deploy.form.aliyun_esa_region.placeholder") }) .nonempty(t("workflow_node.deploy.form.aliyun_esa_region.placeholder")) .trim(), - siteId: z - .string({ message: t("workflow_node.deploy.form.aliyun_esa_site_id.placeholder") }) - .regex(/^[1-9]\d*$/, t("workflow_node.deploy.form.aliyun_esa_site_id.placeholder")) - .trim(), + siteId: z.union([z.string(), z.number()]).refine((v) => { + return /^\d+$/.test(v + "") && +v > 0; + }, t("workflow_node.deploy.form.aliyun_esa_site_id.placeholder")), }); const formRule = createSchemaFieldRule(formSchema); @@ -69,7 +68,7 @@ const DeployNodeConfigFormAliyunESAConfig = ({ rules={[formRule]} tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_esa_site_id.tooltip") }}></span>} > - <Input placeholder={t("workflow_node.deploy.form.aliyun_esa_site_id.placeholder")} /> + <Input type="number" placeholder={t("workflow_node.deploy.form.aliyun_esa_site_id.placeholder")} /> </Form.Item> </Form> ); diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunVODConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunVODConfig.tsx new file mode 100644 index 00000000..d76e1193 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunVODConfig.tsx @@ -0,0 +1,79 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormAliyunVODConfigFieldValues = Nullish<{ + region: string; + domain: string; +}>; + +export type DeployNodeConfigFormAliyunVODConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormAliyunVODConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormAliyunVODConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormAliyunVODConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormAliyunVODConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormAliyunVODConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + region: z + .string({ message: t("workflow_node.deploy.form.aliyun_vod_region.placeholder") }) + .nonempty(t("workflow_node.deploy.form.aliyun_vod_region.placeholder")) + .trim(), + domain: z + .string({ message: t("workflow_node.deploy.form.aliyun_vod_domain.placeholder") }) + .refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="region" + label={t("workflow_node.deploy.form.aliyun_vod_region.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_vod_region.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.aliyun_vod_region.placeholder")} /> + </Form.Item> + + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.aliyun_vod_domain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_vod_domain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.aliyun_vod_domain.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormAliyunVODConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx new file mode 100644 index 00000000..828d5845 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx @@ -0,0 +1,65 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormBaishanCDNConfigFieldValues = Nullish<{ + domain: string; +}>; + +export type DeployNodeConfigFormBaishanCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormBaishanCDNConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormBaishanCDNConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormBaishanCDNConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormBaishanCDNConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormBaishanCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + domain: z + .string({ message: t("workflow_node.deploy.form.baishan_cdn_domain.placeholder") }) + .refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.baishan_cdn_domain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baishan_cdn_domain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.baishan_cdn_domain.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormBaishanCDNConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelConsoleConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelConsoleConfig.tsx new file mode 100644 index 00000000..40d93b7f --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelConsoleConfig.tsx @@ -0,0 +1,56 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type DeployNodeConfigFormBaotaPanelConsoleConfigFieldValues = Nullish<{ + autoRestart?: boolean; +}>; + +export type DeployNodeConfigFormBaotaPanelConsoleConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormBaotaPanelConsoleConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormBaotaPanelConsoleConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormBaotaPanelConsoleConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormBaotaPanelConsoleConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormBaotaPanelConsoleConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + autoRestart: z.boolean().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item name="autoRestart" label={t("workflow_node.deploy.form.baotapanel_console_auto_restart.label")} rules={[formRule]}> + <Switch /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormBaotaPanelConsoleConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx index aa891245..f62379bb 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx @@ -1,10 +1,19 @@ +import { memo } from "react"; import { useTranslation } from "react-i18next"; -import { Form, type FormInstance, Input } from "antd"; +import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; +import { Button, Form, type FormInstance, Input, Select, Space } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; +import ModalForm from "@/components/ModalForm"; +import MultipleInput from "@/components/MultipleInput"; +import Show from "@/components/Show"; +import { useAntdForm } from "@/hooks"; + type DeployNodeConfigFormBaotaPanelSiteConfigFieldValues = Nullish<{ - siteName: string; + siteType: string; + siteName?: string; + siteNames?: string; }>; export type DeployNodeConfigFormBaotaPanelSiteConfigProps = { @@ -15,8 +24,17 @@ export type DeployNodeConfigFormBaotaPanelSiteConfigProps = { onValuesChange?: (values: DeployNodeConfigFormBaotaPanelSiteConfigFieldValues) => void; }; +const SITE_TYPE_PHP = "php"; +const SITE_TYPE_OTHER = "other"; + +const MULTIPLE_INPUT_DELIMITER = ";"; + const initFormModel = (): DeployNodeConfigFormBaotaPanelSiteConfigFieldValues => { - return {}; + return { + siteType: SITE_TYPE_OTHER, + siteName: "", + siteNames: "", + }; }; const DeployNodeConfigFormBaotaPanelSiteConfig = ({ @@ -29,13 +47,32 @@ const DeployNodeConfigFormBaotaPanelSiteConfig = ({ const { t } = useTranslation(); const formSchema = z.object({ + siteType: z.union([z.literal(SITE_TYPE_PHP), z.literal(SITE_TYPE_OTHER)], { + message: t("workflow_node.deploy.form.baotapanel_site_type.placeholder"), + }), siteName: z - .string({ message: t("workflow_node.deploy.form.baotapanel_site_name.placeholder") }) - .nonempty(t("workflow_node.deploy.form.baotapanel_site_name.placeholder")) - .trim(), + .string() + .nullish() + .refine((v) => { + if (fieldSiteType !== SITE_TYPE_PHP) return true; + return !!v?.trim(); + }, t("workflow_node.deploy.form.baotapanel_site_name.placeholder")), + siteNames: z + .string() + .nullish() + .refine((v) => { + if (fieldSiteType !== SITE_TYPE_OTHER) return true; + if (!v) return false; + return String(v) + .split(MULTIPLE_INPUT_DELIMITER) + .every((e) => !!e.trim()); + }, t("workflow_node.deploy.form.baotapanel_site_names.placeholder")), }); const formRule = createSchemaFieldRule(formSchema); + const fieldSiteType = Form.useWatch<string>("siteType", formInst); + const fieldSiteNames = Form.useWatch<string>("siteNames", formInst); + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { onValuesChange?.(values); }; @@ -49,16 +86,101 @@ const DeployNodeConfigFormBaotaPanelSiteConfig = ({ name={formName} onValuesChange={handleFormChange} > - <Form.Item - name="siteName" - label={t("workflow_node.deploy.form.baotapanel_site_name.label")} - rules={[formRule]} - tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baotapanel_site_name.tooltip") }}></span>} - > - <Input placeholder={t("workflow_node.deploy.form.baotapanel_site_name.placeholder")} /> + <Form.Item name="siteType" label={t("workflow_node.deploy.form.baotapanel_site_type.label")} rules={[formRule]}> + <Select placeholder={t("workflow_node.deploy.form.baotapanel_site_type.placeholder")}> + <Select.Option key={SITE_TYPE_PHP} value={SITE_TYPE_PHP}> + {t("workflow_node.deploy.form.baotapanel_site_type.option.php.label")} + </Select.Option> + <Select.Option key={SITE_TYPE_OTHER} value={SITE_TYPE_OTHER}> + {t("workflow_node.deploy.form.baotapanel_site_type.option.other.label")} + </Select.Option> + </Select> </Form.Item> + + <Show when={fieldSiteType === SITE_TYPE_PHP}> + <Form.Item + name="siteName" + label={t("workflow_node.deploy.form.baotapanel_site_name.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baotapanel_site_name.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.baotapanel_site_name.placeholder")} /> + </Form.Item> + </Show> + + <Show when={fieldSiteType === SITE_TYPE_OTHER}> + <Form.Item + label={t("workflow_node.deploy.form.baotapanel_site_names.label")} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baotapanel_site_names.tooltip") }}></span>} + > + <Space.Compact style={{ width: "100%" }}> + <Form.Item name="siteNames" noStyle rules={[formRule]}> + <Input + allowClear + disabled={disabled} + value={fieldSiteNames} + placeholder={t("workflow_node.deploy.form.baotapanel_site_names.placeholder")} + onChange={(e) => { + formInst.setFieldValue("siteNames", e.target.value); + }} + /> + </Form.Item> + <SiteNamesModalInput + value={fieldSiteNames} + trigger={ + <Button disabled={disabled}> + <FormOutlinedIcon /> + </Button> + } + onChange={(value) => { + formInst.setFieldValue("siteNames", value); + }} + /> + </Space.Compact> + </Form.Item> + </Show> </Form> ); }; +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 ( + <ModalForm + {...formProps} + layout="vertical" + form={formInst} + modalProps={{ destroyOnClose: true }} + title={t("workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title")} + trigger={trigger} + validateTrigger="onSubmit" + width={480} + > + <Form.Item name="siteNames" rules={[formRule]}> + <MultipleInput placeholder={t("workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder")} /> + </Form.Item> + </ModalForm> + ); +}); + export default DeployNodeConfigFormBaotaPanelSiteConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx new file mode 100644 index 00000000..c9f61e02 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx @@ -0,0 +1,89 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import Show from "@/components/Show"; + +type DeployNodeConfigFormCdnflyConfigFieldValues = Nullish<{ + resourceType: string; + certificateId?: string | number; +}>; + +export type DeployNodeConfigFormCdnflyConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormCdnflyConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormCdnflyConfigFieldValues) => void; +}; + +const RESOURCE_TYPE_SITE = "site" as const; +const RESOURCE_TYPE_CERTIFICATE = "certificate" as const; + +const initFormModel = (): DeployNodeConfigFormCdnflyConfigFieldValues => { + return { + resourceType: RESOURCE_TYPE_SITE, + }; +}; + +const DeployNodeConfigFormCdnflyConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormCdnflyConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + resourceType: z.union([z.literal(RESOURCE_TYPE_SITE), z.literal(RESOURCE_TYPE_CERTIFICATE)], { + message: t("workflow_node.deploy.form.cdnfly_resource_type.placeholder"), + }), + siteId: z.union([z.string(), z.number().int()]).refine((v) => { + if (fieldResourceType !== RESOURCE_TYPE_SITE) return true; + return /^\d+$/.test(v + "") && +v > 0; + }, t("workflow_node.deploy.form.cdnfly_site_id.placeholder")), + certificateId: z.union([z.string(), z.number().int()]).refine((v) => { + if (fieldResourceType !== RESOURCE_TYPE_CERTIFICATE) return true; + return /^\d+$/.test(v + "") && +v > 0; + }, t("workflow_node.deploy.form.cdnfly_certificate_id.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldResourceType = Form.useWatch("resourceType", formInst); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item name="resourceType" label={t("workflow_node.deploy.form.cdnfly_resource_type.label")} rules={[formRule]}> + <Select placeholder={t("workflow_node.deploy.form.cdnfly_resource_type.placeholder")}> + <Select.Option key={RESOURCE_TYPE_SITE} value={RESOURCE_TYPE_SITE}> + {t("workflow_node.deploy.form.cdnfly_resource_type.option.site.label")} + </Select.Option> + <Select.Option key={RESOURCE_TYPE_CERTIFICATE} value={RESOURCE_TYPE_CERTIFICATE}> + {t("workflow_node.deploy.form.cdnfly_resource_type.option.certificate.label")} + </Select.Option> + </Select> + </Form.Item> + + <Show when={fieldResourceType === RESOURCE_TYPE_SITE}> + <Form.Item name="siteId" label={t("workflow_node.deploy.form.cdnfly_site_id.label")} rules={[formRule]}> + <Input type="number" placeholder={t("workflow_node.deploy.form.cdnfly_site_id.placeholder")} /> + </Form.Item> + </Show> + + <Show when={fieldResourceType === RESOURCE_TYPE_CERTIFICATE}> + <Form.Item name="certificateId" label={t("workflow_node.deploy.form.cdnfly_certificate_id.label")} rules={[formRule]}> + <Input type="number" placeholder={t("workflow_node.deploy.form.cdnfly_certificate_id.placeholder")} /> + </Form.Item> + </Show> + </Form> + ); +}; + +export default DeployNodeConfigFormCdnflyConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx new file mode 100644 index 00000000..00dc48dd --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx @@ -0,0 +1,59 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type DeployNodeConfigFormGcoreCDNConfigFieldValues = Nullish<{ + resourceId?: string | number; +}>; + +export type DeployNodeConfigFormGcoreCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormGcoreCDNConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormGcoreCDNConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormGcoreCDNConfigFieldValues => { + return { + resourceId: "", + }; +}; + +const DeployNodeConfigFormGcoreCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormGcoreCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + resourceId: z.union([z.string(), z.number()]).refine((v) => { + return /^\d+$/.test(v + "") && +v > 0; + }, t("workflow_node.deploy.form.gcore_cdn_certificate_id.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="resourceId" + label={t("workflow_node.deploy.form.gcore_cdn_resource_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.gcore_cdn_resource_id.tooltip") }}></span>} + > + <Input type="number" placeholder={t("workflow_node.deploy.form.gcore_cdn_resource_id.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormGcoreCDNConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormHuaweiCloudWAFConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormHuaweiCloudWAFConfig.tsx new file mode 100644 index 00000000..cdcabef5 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormHuaweiCloudWAFConfig.tsx @@ -0,0 +1,132 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import Show from "@/components/Show"; +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormHuaweiCloudWAFConfigFieldValues = Nullish<{ + resourceType: string; + region: string; + certificateId?: string; + domain?: string; + listenerId?: string; +}>; + +export type DeployNodeConfigFormHuaweiCloudWAFConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormHuaweiCloudWAFConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormHuaweiCloudWAFConfigFieldValues) => void; +}; + +const RESOURCE_TYPE_CERTIFICATE = "certificate" as const; +const RESOURCE_TYPE_CLOUDSERVER = "cloudserver" as const; +const RESOURCE_TYPE_PREMIUMHOST = "premiumhost" as const; + +const initFormModel = (): DeployNodeConfigFormHuaweiCloudWAFConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormHuaweiCloudWAFConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormHuaweiCloudWAFConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + resourceType: z.union([z.literal(RESOURCE_TYPE_CERTIFICATE), z.literal(RESOURCE_TYPE_CLOUDSERVER), z.literal(RESOURCE_TYPE_PREMIUMHOST)], { + message: t("workflow_node.deploy.form.huaweicloud_waf_resource_type.placeholder"), + }), + region: z + .string({ message: t("workflow_node.deploy.form.huaweicloud_waf_region.placeholder") }) + .nonempty(t("workflow_node.deploy.form.huaweicloud_waf_region.placeholder")) + .trim(), + certificateId: z + .string() + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim() + .nullish() + .refine((v) => { + if (fieldResourceType !== RESOURCE_TYPE_CERTIFICATE) return true; + return !!v?.trim(); + }, t("workflow_node.deploy.form.huaweicloud_waf_certificate_id.placeholder")), + domain: z + .string() + .nullish() + .refine((v) => { + if (fieldResourceType !== RESOURCE_TYPE_CLOUDSERVER && fieldResourceType !== RESOURCE_TYPE_PREMIUMHOST) return true; + return validDomainName(v!, { allowWildcard: true }); + }, t("workflow_node.deploy.form.huaweicloud_waf_domain.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldResourceType = Form.useWatch("resourceType", formInst); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item name="resourceType" label={t("workflow_node.deploy.form.huaweicloud_waf_resource_type.label")} rules={[formRule]}> + <Select placeholder={t("workflow_node.deploy.form.huaweicloud_waf_resource_type.placeholder")}> + <Select.Option key={RESOURCE_TYPE_CERTIFICATE} value={RESOURCE_TYPE_CERTIFICATE}> + {t("workflow_node.deploy.form.huaweicloud_waf_resource_type.option.certificate.label")} + </Select.Option> + <Select.Option key={RESOURCE_TYPE_CLOUDSERVER} value={RESOURCE_TYPE_CLOUDSERVER}> + {t("workflow_node.deploy.form.huaweicloud_waf_resource_type.option.cloudserver.label")} + </Select.Option> + <Select.Option key={RESOURCE_TYPE_PREMIUMHOST} value={RESOURCE_TYPE_PREMIUMHOST}> + {t("workflow_node.deploy.form.huaweicloud_waf_resource_type.option.premiumhost.label")} + </Select.Option> + </Select> + </Form.Item> + + <Form.Item + name="region" + label={t("workflow_node.deploy.form.huaweicloud_waf_region.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.huaweicloud_waf_region.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.huaweicloud_waf_region.placeholder")} /> + </Form.Item> + + <Show when={fieldResourceType === RESOURCE_TYPE_CERTIFICATE}> + <Form.Item + name="certificateId" + label={t("workflow_node.deploy.form.huaweicloud_waf_certificate_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.huaweicloud_waf_certificate_id.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.huaweicloud_waf_certificate_id.placeholder")} /> + </Form.Item> + </Show> + + <Show when={fieldResourceType === RESOURCE_TYPE_CLOUDSERVER || fieldResourceType === RESOURCE_TYPE_PREMIUMHOST}> + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.huaweicloud_waf_domain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.huaweicloud_waf_domain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.huaweicloud_waf_domain.placeholder")} /> + </Form.Item> + </Show> + </Form> + ); +}; + +export default DeployNodeConfigFormHuaweiCloudWAFConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx new file mode 100644 index 00000000..f54477ce --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx @@ -0,0 +1,142 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import Show from "@/components/Show"; +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormJDCloudALBConfigFieldValues = Nullish<{ + resourceType: string; + regionId: string; + loadbalancerId?: string; + listenerId?: string; + domain?: string; +}>; + +export type DeployNodeConfigFormJDCloudALBConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormJDCloudALBConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormJDCloudALBConfigFieldValues) => void; +}; + +const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const; +const RESOURCE_TYPE_LISTENER = "listener" as const; + +const initFormModel = (): DeployNodeConfigFormJDCloudALBConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormJDCloudALBConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormJDCloudALBConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + resourceType: z.union([z.literal(RESOURCE_TYPE_LOADBALANCER), z.literal(RESOURCE_TYPE_LISTENER)], { + message: t("workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder"), + }), + regionId: z + .string({ message: t("workflow_node.deploy.form.jdcloud_alb_region_id.placeholder") }) + .nonempty(t("workflow_node.deploy.form.jdcloud_alb_region_id.placeholder")) + .trim(), + loadbalancerId: z + .string() + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim() + .nullish() + .refine((v) => fieldResourceType !== RESOURCE_TYPE_LOADBALANCER || !!v?.trim(), t("workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder")), + listenerId: z + .string() + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim() + .nullish() + .refine((v) => fieldResourceType !== RESOURCE_TYPE_LISTENER || !!v?.trim(), t("workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder")), + domain: z + .string() + .nullish() + .refine((v) => { + if (![RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER].includes(fieldResourceType)) return true; + return !v || validDomainName(v!, { allowWildcard: true }); + }, t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldResourceType = Form.useWatch("resourceType", formInst); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item name="resourceType" label={t("workflow_node.deploy.form.jdcloud_alb_resource_type.label")} rules={[formRule]}> + <Select placeholder={t("workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder")}> + <Select.Option key={RESOURCE_TYPE_LOADBALANCER} value={RESOURCE_TYPE_LOADBALANCER}> + {t("workflow_node.deploy.form.jdcloud_alb_resource_type.option.loadbalancer.label")} + </Select.Option> + <Select.Option key={RESOURCE_TYPE_LISTENER} value={RESOURCE_TYPE_LISTENER}> + {t("workflow_node.deploy.form.jdcloud_alb_resource_type.option.listener.label")} + </Select.Option> + </Select> + </Form.Item> + + <Form.Item + name="regionId" + label={t("workflow_node.deploy.form.jdcloud_alb_region_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.jdcloud_alb_region_id.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.jdcloud_alb_region_id.placeholder")} /> + </Form.Item> + + <Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER}> + <Form.Item + name="loadbalancerId" + label={t("workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder")} /> + </Form.Item> + </Show> + + <Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}> + <Form.Item + name="listenerId" + label={t("workflow_node.deploy.form.jdcloud_alb_listener_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.jdcloud_alb_listener_id.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder")} /> + </Form.Item> + </Show> + + <Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER || fieldResourceType === RESOURCE_TYPE_LISTENER}> + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.jdcloud_alb_snidomain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.jdcloud_alb_snidomain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.jdcloud_alb_snidomain.placeholder")} /> + </Form.Item> + </Show> + </Form> + ); +}; + +export default DeployNodeConfigFormJDCloudALBConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudCDNConfig.tsx new file mode 100644 index 00000000..9d8af17e --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudCDNConfig.tsx @@ -0,0 +1,65 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormJDCloudCDNConfigFieldValues = Nullish<{ + domain: string; +}>; + +export type DeployNodeConfigFormJDCloudCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormJDCloudCDNConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormJDCloudCDNConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormJDCloudCDNConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormJDCloudCDNConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormJDCloudCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + domain: z + .string({ message: t("workflow_node.deploy.form.jdcloud_cdn_domain.placeholder") }) + .refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.jdcloud_cdn_domain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.jdcloud_cdn_domain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.jdcloud_cdn_domain.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormJDCloudCDNConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudLiveConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudLiveConfig.tsx new file mode 100644 index 00000000..5e13f5eb --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudLiveConfig.tsx @@ -0,0 +1,65 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormJDCloudLiveConfigFieldValues = Nullish<{ + domain: string; +}>; + +export type DeployNodeConfigFormJDCloudLiveConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormJDCloudLiveConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormJDCloudLiveConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormJDCloudLiveConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormJDCloudLiveConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormJDCloudLiveConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + domain: z + .string({ message: t("workflow_node.deploy.form.jdcloud_live_domain.placeholder") }) + .refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.jdcloud_live_domain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.jdcloud_live_domain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.jdcloud_live_domain.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormJDCloudLiveConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudVODConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudVODConfig.tsx new file mode 100644 index 00000000..ad64a735 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudVODConfig.tsx @@ -0,0 +1,65 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormJDCloudVODConfigFieldValues = Nullish<{ + domain: string; +}>; + +export type DeployNodeConfigFormJDCloudVODConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormJDCloudVODConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormJDCloudVODConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormJDCloudVODConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormJDCloudVODConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormJDCloudVODConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + domain: z + .string({ message: t("workflow_node.deploy.form.jdcloud_vod_domain.placeholder") }) + .refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.jdcloud_vod_domain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.jdcloud_vod_domain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.jdcloud_vod_domain.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormJDCloudVODConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx new file mode 100644 index 00000000..239a6c92 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx @@ -0,0 +1,76 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import Show from "@/components/Show"; + +type DeployNodeConfigFormSafeLineConfigFieldValues = Nullish<{ + resourceType: string; + certificateId?: string | number; +}>; + +export type DeployNodeConfigFormSafeLineConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormSafeLineConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormSafeLineConfigFieldValues) => void; +}; + +const RESOURCE_TYPE_CERTIFICATE = "certificate" as const; + +const initFormModel = (): DeployNodeConfigFormSafeLineConfigFieldValues => { + return { + resourceType: RESOURCE_TYPE_CERTIFICATE, + certificateId: "", + }; +}; + +const DeployNodeConfigFormSafeLineConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormSafeLineConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + resourceType: z.literal(RESOURCE_TYPE_CERTIFICATE, { + message: t("workflow_node.deploy.form.safeline_resource_type.placeholder"), + }), + certificateId: z.union([z.string(), z.number().int()]).refine((v) => { + if (fieldResourceType !== RESOURCE_TYPE_CERTIFICATE) return true; + return /^\d+$/.test(v + "") && +v > 0; + }, t("workflow_node.deploy.form.safeline_certificate_id.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldResourceType = Form.useWatch("resourceType", formInst); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item name="resourceType" label={t("workflow_node.deploy.form.safeline_resource_type.label")} rules={[formRule]}> + <Select placeholder={t("workflow_node.deploy.form.safeline_resource_type.placeholder")}> + <Select.Option key={RESOURCE_TYPE_CERTIFICATE} value={RESOURCE_TYPE_CERTIFICATE}> + {t("workflow_node.deploy.form.safeline_resource_type.option.certificate.label")} + </Select.Option> + </Select> + </Form.Item> + + <Show when={fieldResourceType === RESOURCE_TYPE_CERTIFICATE}> + <Form.Item name="certificateId" label={t("workflow_node.deploy.form.safeline_certificate_id.label")} rules={[formRule]}> + <Input type="number" placeholder={t("workflow_node.deploy.form.safeline_certificate_id.placeholder")} /> + </Form.Item> + </Show> + </Form> + ); +}; + +export default DeployNodeConfigFormSafeLineConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx index 14266151..67cdbf11 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx @@ -48,6 +48,7 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({ .nonempty(t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_type.placeholder")) .trim(), resourceIds: z.string({ message: t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.placeholder") }).refine((v) => { + if (!v) return false; return String(v) .split(MULTIPLE_INPUT_DELIMITER) .every((e) => /^[A-Za-z0-9._-]+$/.test(e)); diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudVODConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudVODConfig.tsx new file mode 100644 index 00000000..2c5742eb --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudVODConfig.tsx @@ -0,0 +1,82 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormTencentCloudVODConfigFieldValues = Nullish<{ + subAppId?: string | number; + domain: string; +}>; + +export type DeployNodeConfigFormTencentCloudVODConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormTencentCloudVODConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormTencentCloudVODConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormTencentCloudVODConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormTencentCloudVODConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormTencentCloudVODConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + subAppId: z + .union([z.string(), z.number()]) + .nullish() + .refine((v) => { + if (v == null) return true; + return /^\d+$/.test(v + "") && +v > 0; + }, t("workflow_node.deploy.form.tencentcloud_vod_sub_app_id.placeholder")), + domain: z + .string({ message: t("workflow_node.deploy.form.tencentcloud_vod_domain.placeholder") }) + .refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="subAppId" + label={t("workflow_node.deploy.form.tencentcloud_vod_sub_app_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_vod_sub_app_id.tooltip") }}></span>} + > + <Input type="number" placeholder={t("workflow_node.deploy.form.tencentcloud_vod_sub_app_id.placeholder")} /> + </Form.Item> + + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.tencentcloud_vod_domain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_vod_domain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.tencentcloud_vod_domain.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormTencentCloudVODConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudWAFConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudWAFConfig.tsx new file mode 100644 index 00000000..347ebaa2 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudWAFConfig.tsx @@ -0,0 +1,107 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormTencentCloudWAFConfigFieldValues = Nullish<{ + region: string; + domain: string; + domainId: string; + instanceId: string; +}>; + +export type DeployNodeConfigFormTencentCloudWAFConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormTencentCloudWAFConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormTencentCloudWAFConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormTencentCloudWAFConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormTencentCloudWAFConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormTencentCloudWAFConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + region: z + .string({ message: t("workflow_node.deploy.form.tencentcloud_waf_region.placeholder") }) + .nonempty(t("workflow_node.deploy.form.tencentcloud_waf_region.placeholder")) + .trim(), + domain: z + .string({ message: t("workflow_node.deploy.form.tencentcloud_waf_domain.placeholder") }) + .refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")), + domainId: z + .string({ message: t("workflow_node.deploy.form.tencentcloud_waf_domain_id.placeholder") }) + .nonempty(t("workflow_node.deploy.form.tencentcloud_waf_domain_id.placeholder")) + .trim(), + instanceId: z + .string({ message: t("workflow_node.deploy.form.tencentcloud_waf_instance_id.placeholder") }) + .nonempty(t("workflow_node.deploy.form.tencentcloud_waf_instance_id.placeholder")) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="region" + label={t("workflow_node.deploy.form.tencentcloud_waf_region.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_waf_region.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.tencentcloud_waf_region.placeholder")} /> + </Form.Item> + + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.tencentcloud_waf_domain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_waf_domain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.tencentcloud_waf_domain.placeholder")} /> + </Form.Item> + + <Form.Item + name="domainId" + label={t("workflow_node.deploy.form.tencentcloud_waf_domain_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_waf_domain_id.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.tencentcloud_waf_domain_id.placeholder")} /> + </Form.Item> + + <Form.Item + name="instanceId" + label={t("workflow_node.deploy.form.tencentcloud_waf_instance_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_waf_instance_id.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.tencentcloud_waf_instance_id.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormTencentCloudWAFConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineImageXConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineImageXConfig.tsx new file mode 100644 index 00000000..61f59e23 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineImageXConfig.tsx @@ -0,0 +1,91 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormVolcEngineImageXConfigFieldValues = Nullish<{ + domain: string; +}>; + +export type DeployNodeConfigFormVolcEngineImageXConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormVolcEngineImageXConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormVolcEngineImageXConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormVolcEngineImageXConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormVolcEngineImageXConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormVolcEngineImageXConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + region: z + .string({ message: t("workflow_node.deploy.form.volcengine_imagex_region.placeholder") }) + .nonempty(t("workflow_node.deploy.form.volcengine_imagex_region.placeholder")) + .trim(), + serviceId: z + .string({ message: t("workflow_node.deploy.form.volcengine_imagex_service_id.placeholder") }) + .nonempty(t("workflow_node.deploy.form.volcengine_imagex_service_id.placeholder")) + .trim(), + domain: z + .string({ message: t("workflow_node.deploy.form.volcengine_imagex_domain.placeholder") }) + .refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => { + onValuesChange?.(values); + }; + + return ( + <Form + form={formInst} + disabled={disabled} + initialValues={initialValues ?? initFormModel()} + layout="vertical" + name={formName} + onValuesChange={handleFormChange} + > + <Form.Item + name="region" + label={t("workflow_node.deploy.form.volcengine_imagex_region.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.volcengine_imagex_region.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.volcengine_imagex_region.placeholder")} /> + </Form.Item> + + <Form.Item + name="serviceId" + label={t("workflow_node.deploy.form.volcengine_imagex_service_id.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.volcengine_imagex_service_id.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.volcengine_imagex_service_id.placeholder")} /> + </Form.Item> + + <Form.Item + name="domain" + label={t("workflow_node.deploy.form.volcengine_imagex_domain.label")} + rules={[formRule]} + tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.volcengine_imagex_domain.tooltip") }}></span>} + > + <Input placeholder={t("workflow_node.deploy.form.volcengine_imagex_domain.placeholder")} /> + </Form.Item> + </Form> + ); +}; + +export default DeployNodeConfigFormVolcEngineImageXConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index cf6b1055..d32960ca 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -11,22 +11,31 @@ export interface AccessModel extends BaseModel { | AccessConfigForAWS | AccessConfigForAzure | AccessConfigForBaiduCloud + | AccessConfigForBaishan | AccessConfigForBaotaPanel | AccessConfigForBytePlus + | AccessConfigForCacheFly + | AccessConfigForCdnfly | AccessConfigForCloudflare | AccessConfigForClouDNS + | AccessConfigForCMCCCloud + | AccessConfigForDNSLA | AccessConfigForDogeCloud | AccessConfigForEdgio + | AccessConfigForGcore | AccessConfigForGname | AccessConfigForGoDaddy | AccessConfigForHuaweiCloud + | AccessConfigForJDCloud | AccessConfigForKubernetes | AccessConfigForLocal + | AccessConfigForNamecheap | AccessConfigForNameDotCom | AccessConfigForNameSilo | AccessConfigForPowerDNS | AccessConfigForQiniu | AccessConfigForRainYun + | AccessConfigForSafeLine | AccessConfigForSSH | AccessConfigForTencentCloud | AccessConfigForUCloud @@ -66,6 +75,10 @@ export type AccessConfigForBaiduCloud = { secretAccessKey: string; }; +export type AccessConfigForBaishan = { + apiToken: string; +}; + export type AccessConfigForBaotaPanel = { apiUrl: string; apiKey: string; @@ -76,6 +89,16 @@ export type AccessConfigForBytePlus = { secretKey: string; }; +export type AccessConfigForCacheFly = { + apiToken: string; +}; + +export type AccessConfigForCdnfly = { + apiUrl: string; + apiKey: string; + apiSecret: string; +}; + export type AccessConfigForCloudflare = { dnsApiToken: string; }; @@ -85,6 +108,16 @@ export type AccessConfigForClouDNS = { authPassword: string; }; +export type AccessConfigForCMCCCloud = { + accessKeyId: string; + accessKeySecret: string; +}; + +export type AccessConfigForDNSLA = { + apiId: string; + apiSecret: string; +}; + export type AccessConfigForDogeCloud = { accessKey: string; secretKey: string; @@ -95,6 +128,10 @@ export type AccessConfigForEdgio = { clientSecret: string; }; +export type AccessConfigForGcore = { + apiToken: string; +}; + export type AccessConfigForGname = { appId: string; appKey: string; @@ -110,12 +147,22 @@ export type AccessConfigForHuaweiCloud = { secretAccessKey: string; }; +export type AccessConfigForJDCloud = { + accessKeyId: string; + accessKeySecret: string; +}; + export type AccessConfigForKubernetes = { kubeConfig?: string; }; export type AccessConfigForLocal = NonNullable<unknown>; +export type AccessConfigForNamecheap = { + username: string; + apiKey: string; +}; + export type AccessConfigForNameDotCom = { username: string; apiToken: string; @@ -143,6 +190,11 @@ export type AccessConfigForRainYun = { apiKey: string; }; +export type AccessConfigForSafeLine = { + apiUrl: string; + apiToken: string; +}; + export type AccessConfigForSSH = { host: string; port: number; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 1a3272cd..0841f39d 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -9,23 +9,32 @@ export const ACCESS_PROVIDERS = Object.freeze({ AWS: "aws", AZURE: "azure", BAIDUCLOUD: "baiducloud", + BAISHAN: "baishan", BAOTAPANEL: "baotapanel", BYTEPLUS: "byteplus", + CACHEFLY: "cachefly", + CDNFLY: "cdnfly", CLOUDFLARE: "cloudflare", CLOUDNS: "cloudns", + CMCCCLOUD: "cmcccloud", + DNSLA: "dnsla", DOGECLOUD: "dogecloud", + GCORE: "gcore", GNAME: "gname", GODADDY: "godaddy", EDGIO: "edgio", HUAWEICLOUD: "huaweicloud", + JDCLOUD: "jdcloud", KUBERNETES: "k8s", LOCAL: "local", + NAMECHEAP: "namecheap", NAMEDOTCOM: "namedotcom", NAMESILO: "namesilo", NS1: "ns1", POWERDNS: "powerdns", QINIU: "qiniu", RAINYUN: "rainyun", + SAFELINE: "safeline", SSH: "ssh", TENCENTCLOUD: "tencentcloud", UCLOUD: "ucloud", @@ -62,24 +71,35 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv [ACCESS_PROVIDERS.KUBERNETES, "provider.kubernetes", "/imgs/providers/kubernetes.svg", [ACCESS_USAGES.DEPLOY]], [ACCESS_PROVIDERS.ALIYUN, "provider.aliyun", "/imgs/providers/aliyun.svg", [ACCESS_USAGES.APPLY, ACCESS_USAGES.DEPLOY]], [ACCESS_PROVIDERS.TENCENTCLOUD, "provider.tencentcloud", "/imgs/providers/tencentcloud.svg", [ACCESS_USAGES.APPLY, ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.BAIDUCLOUD, "provider.baiducloud", "/imgs/providers/baiducloud.svg", [ACCESS_USAGES.APPLY, ACCESS_USAGES.DEPLOY]], [ACCESS_PROVIDERS.HUAWEICLOUD, "provider.huaweicloud", "/imgs/providers/huaweicloud.svg", [ACCESS_USAGES.APPLY, ACCESS_USAGES.DEPLOY]], [ACCESS_PROVIDERS.VOLCENGINE, "provider.volcengine", "/imgs/providers/volcengine.svg", [ACCESS_USAGES.APPLY, ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.JDCLOUD, "provider.jdcloud", "/imgs/providers/jdcloud.svg", [ACCESS_USAGES.APPLY, ACCESS_USAGES.DEPLOY]], [ACCESS_PROVIDERS.AWS, "provider.aws", "/imgs/providers/aws.svg", [ACCESS_USAGES.APPLY, ACCESS_USAGES.DEPLOY]], - [ACCESS_PROVIDERS.BAIDUCLOUD, "provider.baiducloud", "/imgs/providers/baiducloud.svg", [ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.GCORE, "provider.gcore", "/imgs/providers/gcore.png", [ACCESS_USAGES.APPLY, ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.QINIU, "provider.qiniu", "/imgs/providers/qiniu.svg", [ACCESS_USAGES.DEPLOY]], - [ACCESS_PROVIDERS.DOGECLOUD, "provider.dogecloud", "/imgs/providers/dogecloud.svg", [ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.BAISHAN, "provider.baishan", "/imgs/providers/baishan.png", [ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.DOGECLOUD, "provider.dogecloud", "/imgs/providers/dogecloud.png", [ACCESS_USAGES.DEPLOY]], [ACCESS_PROVIDERS.BYTEPLUS, "provider.byteplus", "/imgs/providers/byteplus.svg", [ACCESS_USAGES.DEPLOY]], [ACCESS_PROVIDERS.UCLOUD, "provider.ucloud", "/imgs/providers/ucloud.svg", [ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.SAFELINE, "provider.safeline", "/imgs/providers/safeline.svg", [ACCESS_USAGES.DEPLOY]], [ACCESS_PROVIDERS.BAOTAPANEL, "provider.baotapanel", "/imgs/providers/baotapanel.svg", [ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.CACHEFLY, "provider.cachefly", "/imgs/providers/cachefly.png", [ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.CDNFLY, "provider.cdnfly", "/imgs/providers/cdnfly.png", [ACCESS_USAGES.DEPLOY]], [ACCESS_PROVIDERS.EDGIO, "provider.edgio", "/imgs/providers/edgio.svg", [ACCESS_USAGES.DEPLOY]], + [ACCESS_PROVIDERS.AZURE, "provider.azure", "/imgs/providers/azure.svg", [ACCESS_USAGES.APPLY]], [ACCESS_PROVIDERS.CLOUDFLARE, "provider.cloudflare", "/imgs/providers/cloudflare.svg", [ACCESS_USAGES.APPLY]], - [ACCESS_PROVIDERS.CLOUDNS, "provider.cloudns", "/imgs/providers/cloudns.svg", [ACCESS_USAGES.APPLY]], - [ACCESS_PROVIDERS.GNAME, "provider.gname", "/imgs/providers/gname.svg", [ACCESS_USAGES.APPLY]], + [ACCESS_PROVIDERS.CLOUDNS, "provider.cloudns", "/imgs/providers/cloudns.png", [ACCESS_USAGES.APPLY]], + [ACCESS_PROVIDERS.DNSLA, "provider.dnsla", "/imgs/providers/dnsla.svg", [ACCESS_USAGES.APPLY]], + [ACCESS_PROVIDERS.GNAME, "provider.gname", "/imgs/providers/gname.png", [ACCESS_USAGES.APPLY]], [ACCESS_PROVIDERS.GODADDY, "provider.godaddy", "/imgs/providers/godaddy.svg", [ACCESS_USAGES.APPLY]], + [ACCESS_PROVIDERS.NAMECHEAP, "provider.namecheap", "/imgs/providers/namecheap.svg", [ACCESS_USAGES.APPLY]], [ACCESS_PROVIDERS.NAMEDOTCOM, "provider.namedotcom", "/imgs/providers/namedotcom.svg", [ACCESS_USAGES.APPLY]], [ACCESS_PROVIDERS.NAMESILO, "provider.namesilo", "/imgs/providers/namesilo.svg", [ACCESS_USAGES.APPLY]], [ACCESS_PROVIDERS.NS1, "provider.ns1", "/imgs/providers/ns1.svg", [ACCESS_USAGES.APPLY]], + [ACCESS_PROVIDERS.CMCCCLOUD, "provider.cmcccloud", "/imgs/providers/cmcccloud.svg", [ACCESS_USAGES.APPLY]], [ACCESS_PROVIDERS.RAINYUN, "provider.rainyun", "/imgs/providers/rainyun.svg", [ACCESS_USAGES.APPLY]], [ACCESS_PROVIDERS.WESTCN, "provider.westcn", "/imgs/providers/westcn.svg", [ACCESS_USAGES.APPLY]], [ACCESS_PROVIDERS.POWERDNS, "provider.powerdns", "/imgs/providers/powerdns.svg", [ACCESS_USAGES.APPLY]], @@ -96,7 +116,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv ); // #endregion -// #region DNSProvider +// #region ApplyProvider /* 注意:如果追加新的常量值,请保持以 ASCII 排序。 NOTICE: If you add new constant, please keep ASCII order. @@ -107,13 +127,22 @@ export const APPLY_DNS_PROVIDERS = Object.freeze({ ALIYUN_DNS: `${ACCESS_PROVIDERS.ALIYUN}-dns`, AWS: `${ACCESS_PROVIDERS.AWS}`, // 兼容旧值,等同于 `AWS_ROUTE53` AWS_ROUTE53: `${ACCESS_PROVIDERS.AWS}-route53`, + AZURE: `${ACCESS_PROVIDERS.AZURE}`, // 兼容旧值,等同于 `AZURE_DNS` AZURE_DNS: `${ACCESS_PROVIDERS.AZURE}-dns`, + BAIDUCLOUD: `${ACCESS_PROVIDERS.BAIDUCLOUD}`, // 兼容旧值,等同于 `BAIDUCLOUD_DNS` + BAIDUCLOUD_DNS: `${ACCESS_PROVIDERS.BAIDUCLOUD}-dns`, CLOUDFLARE: `${ACCESS_PROVIDERS.CLOUDFLARE}`, CLOUDNS: `${ACCESS_PROVIDERS.CLOUDNS}`, + CMCCCLOUD: `${ACCESS_PROVIDERS.CMCCCLOUD}`, + DNSLA: `${ACCESS_PROVIDERS.DNSLA}`, + GCORE: `${ACCESS_PROVIDERS.GCORE}`, GNAME: `${ACCESS_PROVIDERS.GNAME}`, GODADDY: `${ACCESS_PROVIDERS.GODADDY}`, HUAWEICLOUD: `${ACCESS_PROVIDERS.HUAWEICLOUD}`, // 兼容旧值,等同于 `HUAWEICLOUD_DNS` HUAWEICLOUD_DNS: `${ACCESS_PROVIDERS.HUAWEICLOUD}-dns`, + JDCLOUD: `${ACCESS_PROVIDERS.JDCLOUD}`, // 兼容旧值,等同于 `JDCLOUD_DNS` + JDCLOUD_DNS: `${ACCESS_PROVIDERS.JDCLOUD}-dns`, + NAMECHEAP: `${ACCESS_PROVIDERS.NAMECHEAP}`, NAMEDOTCOM: `${ACCESS_PROVIDERS.NAMEDOTCOM}`, NAMESILO: `${ACCESS_PROVIDERS.NAMESILO}`, NS1: `${ACCESS_PROVIDERS.NS1}`, @@ -143,17 +172,23 @@ export const applyDNSProvidersMap: Map<ApplyDNSProvider["type"] | string, ApplyD [ [APPLY_DNS_PROVIDERS.ALIYUN_DNS, "provider.aliyun.dns"], [APPLY_DNS_PROVIDERS.TENCENTCLOUD_DNS, "provider.tencentcloud.dns"], + [APPLY_DNS_PROVIDERS.BAIDUCLOUD_DNS, "provider.baiducloud.dns"], [APPLY_DNS_PROVIDERS.HUAWEICLOUD_DNS, "provider.huaweicloud.dns"], [APPLY_DNS_PROVIDERS.VOLCENGINE_DNS, "provider.volcengine.dns"], + [APPLY_DNS_PROVIDERS.JDCLOUD_DNS, "provider.jdcloud.dns"], [APPLY_DNS_PROVIDERS.AWS_ROUTE53, "provider.aws.route53"], [APPLY_DNS_PROVIDERS.AZURE_DNS, "provider.azure.dns"], [APPLY_DNS_PROVIDERS.CLOUDFLARE, "provider.cloudflare"], [APPLY_DNS_PROVIDERS.CLOUDNS, "provider.cloudns"], + [APPLY_DNS_PROVIDERS.DNSLA, "provider.dnsla"], + [APPLY_DNS_PROVIDERS.GCORE, "provider.gcore"], [APPLY_DNS_PROVIDERS.GNAME, "provider.gname"], [APPLY_DNS_PROVIDERS.GODADDY, "provider.godaddy"], + [APPLY_DNS_PROVIDERS.NAMECHEAP, "provider.namecheap"], [APPLY_DNS_PROVIDERS.NAMEDOTCOM, "provider.namedotcom"], [APPLY_DNS_PROVIDERS.NAMESILO, "provider.namesilo"], [APPLY_DNS_PROVIDERS.NS1, "provider.ns1"], + [APPLY_DNS_PROVIDERS.CMCCCLOUD, "provider.cmcc"], [APPLY_DNS_PROVIDERS.RAINYUN, "provider.rainyun"], [APPLY_DNS_PROVIDERS.WESTCN, "provider.westcn"], [APPLY_DNS_PROVIDERS.POWERDNS, "provider.powerdns"], @@ -185,19 +220,31 @@ export const DEPLOY_PROVIDERS = Object.freeze({ ALIYUN_LIVE: `${ACCESS_PROVIDERS.ALIYUN}-live`, ALIYUN_NLB: `${ACCESS_PROVIDERS.ALIYUN}-nlb`, ALIYUN_OSS: `${ACCESS_PROVIDERS.ALIYUN}-oss`, + ALIYUN_VOD: `${ACCESS_PROVIDERS.ALIYUN}-vod`, ALIYUN_WAF: `${ACCESS_PROVIDERS.ALIYUN}-waf`, AWS_CLOUDFRONT: `${ACCESS_PROVIDERS.AWS}-cloudfront`, BAIDUCLOUD_CDN: `${ACCESS_PROVIDERS.BAIDUCLOUD}-cdn`, + BAISHAN_CDN: `${ACCESS_PROVIDERS.BAISHAN}-cdn`, + BAOTAPANEL_CONSOLE: `${ACCESS_PROVIDERS.BAOTAPANEL}-console`, BAOTAPANEL_SITE: `${ACCESS_PROVIDERS.BAOTAPANEL}-site`, BYTEPLUS_CDN: `${ACCESS_PROVIDERS.BYTEPLUS}-cdn`, + CACHEFLY: `${ACCESS_PROVIDERS.CACHEFLY}`, + CDNFLY: `${ACCESS_PROVIDERS.CDNFLY}`, DOGECLOUD_CDN: `${ACCESS_PROVIDERS.DOGECLOUD}-cdn`, EDGIO_APPLICATIONS: `${ACCESS_PROVIDERS.EDGIO}-applications`, + GCORE_CDN: `${ACCESS_PROVIDERS.GCORE}-cdn`, HUAWEICLOUD_CDN: `${ACCESS_PROVIDERS.HUAWEICLOUD}-cdn`, HUAWEICLOUD_ELB: `${ACCESS_PROVIDERS.HUAWEICLOUD}-elb`, + HUAWEICLOUD_WAF: `${ACCESS_PROVIDERS.HUAWEICLOUD}-waf`, + JDCLOUD_ALB: `${ACCESS_PROVIDERS.JDCLOUD}-alb`, + JDCLOUD_CDN: `${ACCESS_PROVIDERS.JDCLOUD}-cdn`, + JDCLOUD_LIVE: `${ACCESS_PROVIDERS.JDCLOUD}-live`, + JDCLOUD_VOD: `${ACCESS_PROVIDERS.JDCLOUD}-vod`, KUBERNETES_SECRET: `${ACCESS_PROVIDERS.KUBERNETES}-secret`, LOCAL: `${ACCESS_PROVIDERS.LOCAL}`, QINIU_CDN: `${ACCESS_PROVIDERS.QINIU}-cdn`, QINIU_PILI: `${ACCESS_PROVIDERS.QINIU}-pili`, + SAFELINE: `${ACCESS_PROVIDERS.SAFELINE}`, SSH: `${ACCESS_PROVIDERS.SSH}`, TENCENTCLOUD_CDN: `${ACCESS_PROVIDERS.TENCENTCLOUD}-cdn`, TENCENTCLOUD_CLB: `${ACCESS_PROVIDERS.TENCENTCLOUD}-clb`, @@ -206,11 +253,14 @@ export const DEPLOY_PROVIDERS = Object.freeze({ TENCENTCLOUD_ECDN: `${ACCESS_PROVIDERS.TENCENTCLOUD}-ecdn`, TENCENTCLOUD_EO: `${ACCESS_PROVIDERS.TENCENTCLOUD}-eo`, TENCENTCLOUD_SSL_DEPLOY: `${ACCESS_PROVIDERS.TENCENTCLOUD}-ssldeploy`, + TENCENTCLOUD_VOD: `${ACCESS_PROVIDERS.TENCENTCLOUD}-vod`, + TENCENTCLOUD_WAF: `${ACCESS_PROVIDERS.TENCENTCLOUD}-waf`, UCLOUD_UCDN: `${ACCESS_PROVIDERS.UCLOUD}-ucdn`, UCLOUD_US3: `${ACCESS_PROVIDERS.UCLOUD}-us3`, VOLCENGINE_CDN: `${ACCESS_PROVIDERS.VOLCENGINE}-cdn`, VOLCENGINE_CLB: `${ACCESS_PROVIDERS.VOLCENGINE}-clb`, VOLCENGINE_DCDN: `${ACCESS_PROVIDERS.VOLCENGINE}-dcdn`, + VOLCENGINE_IMAGEX: `${ACCESS_PROVIDERS.VOLCENGINE}-imagex`, VOLCENGINE_LIVE: `${ACCESS_PROVIDERS.VOLCENGINE}-live`, VOLCENGINE_TOS: `${ACCESS_PROVIDERS.VOLCENGINE}-tos`, WEBHOOK: `${ACCESS_PROVIDERS.WEBHOOK}`, @@ -224,7 +274,7 @@ export const DEPLOY_CATEGORIES = Object.freeze({ STORAGE: "storage", LOADBALANCE: "loadbalance", FIREWALL: "firewall", - LIVE: "live", + AV: "av", WEBSITE: "website", OTHER: "other", } as const); @@ -257,32 +307,47 @@ export const deployProvidersMap: Map<DeployProvider["type"] | string, DeployProv [DEPLOY_PROVIDERS.ALIYUN_ALB, "provider.aliyun.alb", DEPLOY_CATEGORIES.LOADBALANCE], [DEPLOY_PROVIDERS.ALIYUN_NLB, "provider.aliyun.nlb", DEPLOY_CATEGORIES.LOADBALANCE], [DEPLOY_PROVIDERS.ALIYUN_WAF, "provider.aliyun.waf", DEPLOY_CATEGORIES.FIREWALL], - [DEPLOY_PROVIDERS.ALIYUN_LIVE, "provider.aliyun.live", DEPLOY_CATEGORIES.LIVE], + [DEPLOY_PROVIDERS.ALIYUN_LIVE, "provider.aliyun.live", DEPLOY_CATEGORIES.AV], + [DEPLOY_PROVIDERS.ALIYUN_VOD, "provider.aliyun.vod", DEPLOY_CATEGORIES.AV], [DEPLOY_PROVIDERS.ALIYUN_CAS_DEPLOY, "provider.aliyun.cas_deploy", DEPLOY_CATEGORIES.OTHER], [DEPLOY_PROVIDERS.TENCENTCLOUD_COS, "provider.tencentcloud.cos", DEPLOY_CATEGORIES.STORAGE], [DEPLOY_PROVIDERS.TENCENTCLOUD_CDN, "provider.tencentcloud.cdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.TENCENTCLOUD_ECDN, "provider.tencentcloud.ecdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.TENCENTCLOUD_EO, "provider.tencentcloud.eo", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.TENCENTCLOUD_CLB, "provider.tencentcloud.clb", DEPLOY_CATEGORIES.LOADBALANCE], - [DEPLOY_PROVIDERS.TENCENTCLOUD_CSS, "provider.tencentcloud.css", DEPLOY_CATEGORIES.LIVE], + [DEPLOY_PROVIDERS.TENCENTCLOUD_WAF, "provider.tencentcloud.waf", DEPLOY_CATEGORIES.FIREWALL], + [DEPLOY_PROVIDERS.TENCENTCLOUD_CSS, "provider.tencentcloud.css", DEPLOY_CATEGORIES.AV], + [DEPLOY_PROVIDERS.TENCENTCLOUD_VOD, "provider.tencentcloud.vod", DEPLOY_CATEGORIES.AV], [DEPLOY_PROVIDERS.TENCENTCLOUD_SSL_DEPLOY, "provider.tencentcloud.ssl_deploy", DEPLOY_CATEGORIES.OTHER], [DEPLOY_PROVIDERS.HUAWEICLOUD_CDN, "provider.huaweicloud.cdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.HUAWEICLOUD_ELB, "provider.huaweicloud.elb", DEPLOY_CATEGORIES.LOADBALANCE], + [DEPLOY_PROVIDERS.HUAWEICLOUD_WAF, "provider.huaweicloud.waf", DEPLOY_CATEGORIES.FIREWALL], [DEPLOY_PROVIDERS.BAIDUCLOUD_CDN, "provider.baiducloud.cdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.VOLCENGINE_TOS, "provider.volcengine.tos", DEPLOY_CATEGORIES.STORAGE], [DEPLOY_PROVIDERS.VOLCENGINE_CDN, "provider.volcengine.cdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.VOLCENGINE_DCDN, "provider.volcengine.dcdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.VOLCENGINE_CLB, "provider.volcengine.clb", DEPLOY_CATEGORIES.LOADBALANCE], - [DEPLOY_PROVIDERS.VOLCENGINE_LIVE, "provider.volcengine.live", DEPLOY_CATEGORIES.LIVE], + [DEPLOY_PROVIDERS.VOLCENGINE_IMAGEX, "provider.volcengine.imagex", DEPLOY_CATEGORIES.STORAGE], + [DEPLOY_PROVIDERS.VOLCENGINE_LIVE, "provider.volcengine.live", DEPLOY_CATEGORIES.AV], + [DEPLOY_PROVIDERS.JDCLOUD_ALB, "provider.jdcloud.alb", DEPLOY_CATEGORIES.LOADBALANCE], + [DEPLOY_PROVIDERS.JDCLOUD_CDN, "provider.jdcloud.cdn", DEPLOY_CATEGORIES.CDN], + [DEPLOY_PROVIDERS.JDCLOUD_LIVE, "provider.jdcloud.live", DEPLOY_CATEGORIES.AV], + [DEPLOY_PROVIDERS.JDCLOUD_VOD, "provider.jdcloud.vod", DEPLOY_CATEGORIES.AV], [DEPLOY_PROVIDERS.QINIU_CDN, "provider.qiniu.cdn", DEPLOY_CATEGORIES.CDN], - [DEPLOY_PROVIDERS.QINIU_PILI, "provider.qiniu.pili", DEPLOY_CATEGORIES.LIVE], + [DEPLOY_PROVIDERS.QINIU_PILI, "provider.qiniu.pili", DEPLOY_CATEGORIES.AV], + [DEPLOY_PROVIDERS.BAISHAN_CDN, "provider.baishan.cdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.DOGECLOUD_CDN, "provider.dogecloud.cdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.BYTEPLUS_CDN, "provider.byteplus.cdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.UCLOUD_US3, "provider.ucloud.us3", DEPLOY_CATEGORIES.STORAGE], [DEPLOY_PROVIDERS.UCLOUD_UCDN, "provider.ucloud.ucdn", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.AWS_CLOUDFRONT, "provider.aws.cloudfront", DEPLOY_CATEGORIES.CDN], - [DEPLOY_PROVIDERS.BAOTAPANEL_SITE, "provider.baotapanel.site", DEPLOY_CATEGORIES.WEBSITE], + [DEPLOY_PROVIDERS.CACHEFLY, "provider.cachefly", DEPLOY_CATEGORIES.CDN], + [DEPLOY_PROVIDERS.CDNFLY, "provider.cdnfly", DEPLOY_CATEGORIES.CDN], [DEPLOY_PROVIDERS.EDGIO_APPLICATIONS, "provider.edgio.applications", DEPLOY_CATEGORIES.WEBSITE], + [DEPLOY_PROVIDERS.GCORE_CDN, "provider.gcore.cdn", DEPLOY_CATEGORIES.CDN], + [DEPLOY_PROVIDERS.BAOTAPANEL_SITE, "provider.baotapanel.site", DEPLOY_CATEGORIES.WEBSITE], + [DEPLOY_PROVIDERS.BAOTAPANEL_CONSOLE, "provider.baotapanel.console", DEPLOY_CATEGORIES.OTHER], + [DEPLOY_PROVIDERS.SAFELINE, "provider.safeline", DEPLOY_CATEGORIES.FIREWALL], ].map(([type, name, category]) => [ type, { diff --git a/ui/src/hooks/index.ts b/ui/src/hooks/index.ts index 045c0aad..6f154701 100644 --- a/ui/src/hooks/index.ts +++ b/ui/src/hooks/index.ts @@ -2,6 +2,7 @@ import useAntdFormName from "./useAntdFormName"; import useBrowserTheme from "./useBrowserTheme"; import useTriggerElement from "./useTriggerElement"; +import useVersionChecker from "./useVersionChecker"; import useZustandShallowSelector from "./useZustandShallowSelector"; -export { useAntdForm, useAntdFormName, useBrowserTheme, useTriggerElement, useZustandShallowSelector }; +export { useAntdForm, useAntdFormName, useBrowserTheme, useTriggerElement, useVersionChecker, useZustandShallowSelector }; diff --git a/ui/src/hooks/useVersionChecker.ts b/ui/src/hooks/useVersionChecker.ts new file mode 100644 index 00000000..5dcc96f8 --- /dev/null +++ b/ui/src/hooks/useVersionChecker.ts @@ -0,0 +1,70 @@ +import { useRequest } from "ahooks"; + +import { version } from "@/domain/version"; + +export type UseVersionCheckerReturns = { + hasNewVersion: boolean; + check: () => void; +}; + +const extractSemver = (vers: string) => { + let semver = String(vers ?? ""); + semver = semver.replace(/^v/i, ""); + semver = semver.split("-")[0]; + return semver; +}; + +const compareVersions = (a: string, b: string) => { + const aSemver = extractSemver(a); + const bSemver = extractSemver(b); + const aSemverParts = aSemver.split("."); + const bSemverParts = bSemver.split("."); + + const len = Math.max(aSemverParts.length, bSemverParts.length); + for (let i = 0; i < len; i++) { + const aPart = parseInt(aSemverParts[i] ?? "0"); + const bPart = parseInt(bSemverParts[i] ?? "0"); + if (aPart > bPart) return 1; + if (bPart > aPart) return -1; + } + + return 0; +}; + +/** + * 获取版本检查器。 + * @returns {UseVersionCheckerReturns} + */ +const useVersionChecker = () => { + const { data, refresh } = useRequest( + async () => { + const releases = await fetch("https://api.github.com/repos/usual2970/certimate/releases") + .then((res) => res.json()) + .then((res) => Array.from(res)); + + const cIdx = releases.findIndex((e: any) => e.name === version); + if (cIdx === 0) { + return false; + } + + const nIdx = releases.findIndex((e: any) => compareVersions(e.name, version) !== -1); + if (cIdx >= nIdx) { + return false; + } + + return !!releases[nIdx]; + }, + { + pollingInterval: 6 * 60 * 60 * 1000, + refreshOnWindowFocus: true, + focusTimespan: 15 * 60 * 1000, + } + ); + + return { + hasNewVersion: !!data, + check: refresh, + }; +}; + +export default useVersionChecker; diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index af3676b3..dc468b92 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -38,8 +38,8 @@ "access.form.aliyun_access_key_id.label": "Aliyun AccessKeyId", "access.form.aliyun_access_key_id.placeholder": "Please enter Aliyun AccessKeyId", "access.form.aliyun_access_key_id.tooltip": "For more information, see <a href=\"https://www.alibabacloud.com/help/en/acr/create-and-obtain-an-accesskey-pair\" target=\"_blank\">https://www.alibabacloud.com/help/en/acr/create-and-obtain-an-accesskey-pair</a>", - "access.form.aliyun_access_key_secret.label": "Aliyun AccessKey Secret", - "access.form.aliyun_access_key_secret.placeholder": "Please enter Aliyun AccessKey Secret", + "access.form.aliyun_access_key_secret.label": "Aliyun AccessKeySecret", + "access.form.aliyun_access_key_secret.placeholder": "Please enter Aliyun AccessKeySecret", "access.form.aliyun_access_key_secret.tooltip": "For more information, see <a href=\"https://www.alibabacloud.com/help/en/acr/create-and-obtain-an-accesskey-pair\" target=\"_blank\">https://www.alibabacloud.com/help/en/acr/create-and-obtain-an-accesskey-pair</a>", "access.form.aws_access_key_id.label": "AWS AccessKeyId", "access.form.aws_access_key_id.placeholder": "Please enter AWS AccessKeyId", @@ -65,6 +65,8 @@ "access.form.baiducloud_secret_access_key.label": "Baidu Cloud SecretAccessKey", "access.form.baiducloud_secret_access_key.placeholder": "Please enter Baidu Cloud SecretAccessKey", "access.form.baiducloud_secret_access_key.tooltip": "For more information, see <a href=\"https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en\" target=\"_blank\">https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en</a>", + "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": "BaoTa Panel URL", "access.form.baotapanel_api_url.placeholder": "Please enter BaoTa Panel URL", "access.form.baotapanel_api_url.tooltip": "For more information, see <a href=\"https://www.bt.cn/bbs/thread-20376-1-1.html\" target=\"_blank\">https://www.bt.cn/bbs/thread-20376-1-1.html</a>", @@ -77,6 +79,18 @@ "access.form.byteplus_secret_key.label": "BytePlus SecretKey", "access.form.byteplus_secret_key.placeholder": "Please enter BytePlus SecretKey", "access.form.byteplus_secret_key.tooltip": "For more information, see <a href=\"https://docs.byteplus.com/en/docs/byteplus-platform/docs-managing-keys\" target=\"_blank\">https://docs.byteplus.com/en/docs/byteplus-platform/docs-managing-keys</a>", + "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 <a href=\"https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228\" target=\"_blank\">https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228</a>", + "access.form.cdnfly_api_url.label": "Cdnfly API URL", + "access.form.cdnfly_api_url.placeholder": "Please enter Cdnfly API URL", + "access.form.cdnfly_api_url.tooltip": "For more information, see <a href=\"https://doc.cdnfly.cn/anzhuangshuoming.html\" target=\"_blank\">https://doc.cdnfly.cn/anzhuangshuoming.html</a>", + "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 <a href=\"https://doc.cdnfly.cn/shiyongjieshao.html\" target=\"_blank\">https://doc.cdnfly.cn/shiyongjieshao.html</a>", + "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 <a href=\"https://doc.cdnfly.cn/shiyongjieshao.html\" target=\"_blank\">https://doc.cdnfly.cn/shiyongjieshao.html</a>", "access.form.cloudflare_dns_api_token.label": "Cloudflare API token", "access.form.cloudflare_dns_api_token.placeholder": "Please enter Cloudflare API token", "access.form.cloudflare_dns_api_token.tooltip": "For more information, see <a href=\"https://developers.cloudflare.com/fundamentals/api/get-started/create-token/\" target=\"_blank\">https://developers.cloudflare.com/fundamentals/api/get-started/create-token/</a>", @@ -86,6 +100,18 @@ "access.form.cloudns_auth_password.label": "ClouDNS API user password", "access.form.cloudns_auth_password.placeholder": "Please enter ClouDNS API user password", "access.form.cloudns_auth_password.tooltip": "For more information, see <a href=\"https://www.cloudns.net/wiki/article/42/\" target=\"_blank\">https://www.cloudns.net/wiki/article/42/</a>", + "access.form.cmcccloud_access_key_id.label": "CMCC ECloud AccessKeyId", + "access.form.cmcccloud_access_key_id.placeholder": "Please enter CMCC ECloud AccessKeyId", + "access.form.cmcccloud_access_key_id.tooltip": "For more information, see <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</a>", + "access.form.cmcccloud_access_key_secret.label": "CMCC ECloud AccessKeySecret", + "access.form.cmcccloud_access_key_secret.placeholder": "Please enter CMCC ECloud AccessKeySecret", + "access.form.cmcccloud_access_key_secret.tooltip": "For more information, see <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</a>", + "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 <a href=\"https://www.dns.la/docs/ApiDoc\" target=\"_blank\">https://www.dns.la/docs/ApiDoc</a>", + "access.form.dnsla_api_secret.label": "DNS.LA API secret", + "access.form.dnsla_api_secret.placeholder": "Please enter DNS.LA API secret", + "access.form.dnsla_api_secret.tooltip": "For more information, see <a href=\"https://www.dns.la/docs/ApiDoc\" target=\"_blank\">https://www.dns.la/docs/ApiDoc</a>", "access.form.dogecloud_access_key.label": "Doge Cloud AccessKey", "access.form.dogecloud_access_key.placeholder": "Please enter Doge Cloud AccessKey", "access.form.dogecloud_access_key.tooltip": "For more information, see <a href=\"https://console.dogecloud.com/\" target=\"_blank\">https://console.dogecloud.com/</a>", @@ -98,6 +124,9 @@ "access.form.edgio_client_secret.label": "Edgio ClientSecret", "access.form.edgio_client_secret.placeholder": "Please enter Edgio ClientSecret", "access.form.edgio_client_secret.tooltip": "For more information, see <a href=\"https://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients\" target=\"_blank\">https://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients</a>", + "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 <a href=\"https://api.gcore.com/docs/iam#section/Authentication\" target=\"_blank\">https://api.gcore.com/docs/iam#section/Authentication</a>", "access.form.gname_app_id.label": "GNAME AppId", "access.form.gname_app_id.placeholder": "Please enter GNAME AppId", "access.form.gname_app_id.tooltip": "For more information, see <a href=\"https://www.gname.com/user#/dealer_api\" target=\"_blank\">https://www.gname.com/user#/dealer_api</a>", @@ -116,10 +145,22 @@ "access.form.huaweicloud_secret_access_key.label": "Huawei Cloud SecretAccessKey", "access.form.huaweicloud_secret_access_key.placeholder": "Please enter Huawei Cloud SecretAccessKey", "access.form.huaweicloud_secret_access_key.tooltip": "For more information, see <a href=\"https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html\" target=\"_blank\">https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html</a>", + "access.form.jdcloud_access_key_id.label": "JD Cloud AccessKeyId", + "access.form.jdcloud_access_key_id.placeholder": "Please enter JD Cloud AccessKeyId", + "access.form.jdcloud_access_key_id.tooltip": "For more information, see <a href=\"https://docs.jdcloud.com/en/account-management/accesskey-management\" target=\"_blank\">https://docs.jdcloud.com/en/account-management/accesskey-management</a>", + "access.form.jdcloud_access_key_secret.label": "JD Cloud AccessKeySecret", + "access.form.jdcloud_access_key_secret.placeholder": "Please enter JD Cloud AccessKeySecret", + "access.form.jdcloud_access_key_secret.tooltip": "For more information, see <a href=\"https://docs.jdcloud.com/en/account-management/accesskey-management\" target=\"_blank\">https://docs.jdcloud.com/en/account-management/accesskey-management</a>", "access.form.k8s_kubeconfig.label": "KubeConfig", "access.form.k8s_kubeconfig.placeholder": "Please enter KubeConfig file", "access.form.k8s_kubeconfig.upload": "Choose File ...", "access.form.k8s_kubeconfig.tooltip": "For more information, see <a href=\"https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/\" target=\"_blank\">https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/</a><br><br>Leave it blank to use the Pod's ServiceAccount.", + "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 <a href=\"https://www.namecheap.com/support/api/intro/\" target=\"_blank\">https://www.namecheap.com/support/api/intro/</a>", + "access.form.namecheap_api_key.label": "Namecheap API key", + "access.form.namecheap_api_key.placeholder": "Please enter Namecheap API key", + "access.form.namecheap_api_key.tooltip": "For more information, see <a href=\"https://www.namecheap.com/support/api/intro/\" target=\"_blank\">https://www.namecheap.com/support/api/intro/</a>", "access.form.namedotcom_username.label": "Name.com username", "access.form.namedotcom_username.placeholder": "Please enter Name.com username", "access.form.namedotcom_username.tooltip": "For more information, see <a href=\"https://www.name.com/account/settings/api\" target=\"_blank\">https://www.name.com/account/settings/api</a>", @@ -147,6 +188,12 @@ "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 <a href=\"https://www.rainyun.com/docs/account/racc/setting#api%E5%AF%86%E9%92%A5\" target=\"_blank\">https://www.rainyun.com/docs/account/racc/setting</a>", + "access.form.safeline_api_url.label": "SafeLine URL", + "access.form.safeline_api_url.placeholder": "Please enter SafeLine URL", + "access.form.safeline_api_url.tooltip": "For more information, see <a href=\"https://docs.waf.chaitin.com/en/tutorials/install#use-web-ui\" target=\"_blank\">https://docs.waf.chaitin.com/en/tutorials/install</a>", + "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 <a href=\"https://docs.waf.chaitin.com/en/reference/articles/openapi\" target=\"_blank\">https://docs.waf.chaitin.com/en/reference/articles/openapi</a>", "access.form.ssh_host.label": "Server host", "access.form.ssh_host.placeholder": "Please enter server host", "access.form.ssh_port.label": "Server port", diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index d5e5508b..c9e671ce 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -35,76 +35,6 @@ "common.errmsg.ip_invalid": "Please enter a valid IP address", "common.errmsg.url_invalid": "Please enter a valid URL", - "provider.acmehttpreq": "Http Request (ACME Proxy)", - "provider.aliyun": "Alibaba Cloud", - "provider.aliyun.alb": "Alibaba Cloud - ALB (Application Load Balancer)", - "provider.aliyun.cas_deploy": "Alibaba Cloud - via CAS (Certificate Management Service) Deployment Job", - "provider.aliyun.cdn": "Alibaba Cloud - CDN (Content Delivery Network)", - "provider.aliyun.clb": "Alibaba Cloud - CLB (Classic Load Balancer)", - "provider.aliyun.dcdn": "Alibaba Cloud - DCDN (Dynamic Route for Content Delivery Network)", - "provider.aliyun.dns": "Alibaba Cloud - DNS (Domain Name Service)", - "provider.aliyun.esa": "Alibaba Cloud - ESA (Edge Security Acceleration)", - "provider.aliyun.live": "Alibaba Cloud - ApsaraVideo Live", - "provider.aliyun.nlb": "Alibaba Cloud - NLB (Network Load Balancer)", - "provider.aliyun.oss": "Alibaba Cloud - OSS (Object Storage Service)", - "provider.aliyun.waf": "Alibaba Cloud - WAF (Web Application Firewall)", - "provider.aws": "AWS", - "provider.aws.cloudfront": "AWS - CloudFront", - "provider.aws.route53": "AWS - Route53", - "provider.azure": "Azure", - "provider.azure.dns": "Azure - DNS", - "provider.baiducloud": "Baidu Cloud", - "provider.baiducloud.cdn": "Baidu Cloud - CDN (Content Delivery Network)", - "provider.baotapanel": "BaoTa Panel", - "provider.baotapanel.site": "BaoTa Panel - Site", - "provider.byteplus": "BytePlus", - "provider.byteplus.cdn": "BytePlus - CDN (Content Delivery Network)", - "provider.cloudflare": "Cloudflare", - "provider.cloudns": "ClouDNS", - "provider.dogecloud": "Doge Cloud", - "provider.dogecloud.cdn": "Doge Cloud - CDN (Content Delivery Network)", - "provider.edgio": "Edgio", - "provider.edgio.applications": "Edgio - Applications", - "provider.gname": "GNAME", - "provider.godaddy": "GoDaddy", - "provider.huaweicloud": "Huawei Cloud", - "provider.huaweicloud.cdn": "Huawei Cloud - CDN (Content Delivery Network)", - "provider.huaweicloud.dns": "Huawei Cloud - DNS (Domain Name Service)", - "provider.huaweicloud.elb": "Huawei Cloud - ELB (Elastic Load Balance)", - "provider.kubernetes": "Kubernetes", - "provider.kubernetes.secret": "Kubernetes - Secret", - "provider.local": "Local deployment", - "provider.namedotcom": "Name.com", - "provider.namesilo": "NameSilo", - "provider.ns1": "NS1 (IBM NS1 Connect)", - "provider.powerdns": "PowerDNS", - "provider.qiniu": "Qiniu", - "provider.qiniu.cdn": "Qiniu - CDN (Content Delivery Network)", - "provider.qiniu.pili": "Qiniu - Pili", - "provider.rainyun": "Rain Yun", - "provider.ssh": "SSH deployment", - "provider.tencentcloud": "Tencent Cloud", - "provider.tencentcloud.cdn": "Tencent Cloud - CDN (Content Delivery Network)", - "provider.tencentcloud.clb": "Tencent Cloud - CLB (Cloud Load Balancer)", - "provider.tencentcloud.cos": "Tencent Cloud - COS (Cloud Object Storage)", - "provider.tencentcloud.css": "Tencent Cloud - CSS (Cloud Streaming Service)", - "provider.tencentcloud.dns": "Tencent Cloud - DNS (Domain Name Service)", - "provider.tencentcloud.ecdn": "Tencent Cloud - ECDN (Enterprise Content Delivery Network)", - "provider.tencentcloud.eo": "Tencent Cloud - EdgeOne", - "provider.tencentcloud.ssl_deploy": "Tencent Cloud - via SSL Certificate Service Deployment Job", - "provider.ucloud": "UCloud", - "provider.ucloud.ucdn": "UCloud - UCDN (UCloud Content Delivery Network)", - "provider.ucloud.us3": "UCloud - US3 (UCloud Object-based Storage)", - "provider.volcengine": "Volcengine", - "provider.volcengine.cdn": "Volcengine - CDN (Content Delivery Network)", - "provider.volcengine.clb": "Volcengine - CLB (Cloud Load Balancer)", - "provider.volcengine.dcdn": "Volcengine - DCDN (Dynamic Content Delivery Network)", - "provider.volcengine.dns": "Volcengine - DNS (Domain Name Service)", - "provider.volcengine.live": "Volcengine - Live", - "provider.volcengine.tos": "Volcengine - TOS (Tinder Object Storage)", - "provider.webhook": "Webhook", - "provider.westcn": "West.cn", - "common.notifier.bark": "Bark", "common.notifier.dingtalk": "DingTalk", "common.notifier.email": "Email", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index 5f9a97e1..e924b43a 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -12,6 +12,7 @@ "provider.aliyun.live": "Alibaba Cloud - ApsaraVideo Live", "provider.aliyun.nlb": "Alibaba Cloud - NLB (Network Load Balancer)", "provider.aliyun.oss": "Alibaba Cloud - OSS (Object Storage Service)", + "provider.aliyun.vod": "Alibaba Cloud - ApsaraVideo VOD (Video on Demand)", "provider.aliyun.waf": "Alibaba Cloud - WAF (Web Application Firewall)", "provider.akamai": "Akamai", "provider.akamai.cdn": "Akamai - CDN (Content Delivery Network)", @@ -22,25 +23,29 @@ "provider.azure.dns": "Azure - DNS", "provider.baiducloud": "Baidu Cloud", "provider.baiducloud.cdn": "Baidu Cloud - CDN (Content Delivery Network)", + "provider.baiducloud.dns": "Baidu Cloud - DNS (Domain Name Service)", "provider.baishan": "Baishan", "provider.baishan.cdn": "Baishan - CDN (Content Delivery Network)", "provider.baotapanel": "BaoTa Panel", + "provider.baotapanel.console": "BaoTa Panel - Console", "provider.baotapanel.site": "BaoTa Panel - Site", "provider.byteplus": "BytePlus", "provider.byteplus.cdn": "BytePlus - CDN (Content Delivery Network)", - "provider.cachefly": "Cachefly", + "provider.cachefly": "CacheFly", "provider.cdnfly": "Cdnfly", "provider.cloudflare": "Cloudflare", "provider.cloudns": "ClouDNS", "provider.cmcccloud": "China Mobile Cloud (ECloud)", "provider.ctcccloud": "China Telecom Cloud (State Cloud)", "provider.cucccloud": "China Unicom Cloud", + "provider.dnsla": "DNS.LA", "provider.dogecloud": "Doge Cloud", "provider.dogecloud.cdn": "Doge Cloud - CDN (Content Delivery Network)", "provider.edgio": "Edgio", "provider.edgio.applications": "Edgio - Applications", "provider.fastly": "Fastly", "provider.gcore": "Gcore", + "provider.gcore.cdn": "Gcore - CDN (Content Delivery Network)", "provider.gname": "GNAME", "provider.godaddy": "GoDaddy", "provider.goedge": "GoEdge", @@ -49,9 +54,17 @@ "provider.huaweicloud.cdn": "Huawei Cloud - CDN (Content Delivery Network)", "provider.huaweicloud.dns": "Huawei Cloud - DNS (Domain Name Service)", "provider.huaweicloud.elb": "Huawei Cloud - ELB (Elastic Load Balance)", + "provider.huaweicloud.waf": "Huawei Cloud - WAF (Web Application Firewall)", + "provider.jdcloud": "JD Cloud", + "provider.jdcloud.alb": "JD Cloud - ALB (Application Load Balancer)", + "provider.jdcloud.cdn": "JD Cloud - CDN (Content Delivery Network)", + "provider.jdcloud.dns": "JD Cloud - DNS", + "provider.jdcloud.live": "JD Cloud - Live Video", + "provider.jdcloud.vod": "JD Cloud - VOD (Video on Demand)", "provider.kubernetes": "Kubernetes", "provider.kubernetes.secret": "Kubernetes - Secret", "provider.local": "Local deployment", + "provider.namecheap": "Namecheap", "provider.namedotcom": "Name.com", "provider.namesilo": "NameSilo", "provider.ns1": "NS1 (IBM NS1 Connect)", @@ -71,6 +84,8 @@ "provider.tencentcloud.ecdn": "Tencent Cloud - ECDN (Enterprise Content Delivery Network)", "provider.tencentcloud.eo": "Tencent Cloud - EdgeOne", "provider.tencentcloud.ssl_deploy": "Tencent Cloud - via SSL Certificate Service Deployment Job", + "provider.tencentcloud.vod": "Tencent Cloud - VOD (Video on Demand)", + "provider.tencentcloud.waf": "Tencent Cloud - WAF (Web Application Firewall)", "provider.ucloud": "UCloud", "provider.ucloud.ucdn": "UCloud - UCDN (UCloud Content Delivery Network)", "provider.ucloud.us3": "UCloud - US3 (UCloud Object-based Storage)", @@ -79,6 +94,7 @@ "provider.volcengine.clb": "Volcengine - CLB (Cloud Load Balancer)", "provider.volcengine.dcdn": "Volcengine - DCDN (Dynamic Content Delivery Network)", "provider.volcengine.dns": "Volcengine - DNS (Domain Name Service)", + "provider.volcengine.imagex": "Volcengine - ImageX", "provider.volcengine.live": "Volcengine - Live", "provider.volcengine.tos": "Volcengine - TOS (Tinder Object Storage)", "provider.webhook": "Webhook", @@ -87,9 +103,9 @@ "provider.category.all": "All", "provider.category.cdn": "CDN", "provider.category.storage": "Storage", - "provider.category.loadbalance": "Load Balance", + "provider.category.loadbalance": "Loadbalance", "provider.category.firewall": "Firewall", - "provider.category.live": "Live", + "provider.category.av": "Audio/Video", "provider.category.website": "Website", "provider.category.other": "Other" } diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index a4b3a15e..ebf7cf3c 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -46,6 +46,9 @@ "workflow_node.apply.form.huaweicloud_dns_region.label": "Huawei Cloud DNS region", "workflow_node.apply.form.huaweicloud_dns_region.placeholder": "Please enter Huawei Cloud DNS region (e.g. cn-north-1)", "workflow_node.apply.form.huaweicloud_dns_region.tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/apiexplorer/#/endpoint?locale=en-us\" target=\"_blank\">https://console-intl.huaweicloud.com/apiexplorer/#/endpoint</a>", + "workflow_node.apply.form.jdcloud_dns_region_id.label": "JD Cloud DNS region ID", + "workflow_node.apply.form.jdcloud_dns_region_id.placeholder": "Please enter JD Cloud DNS region ID (e.g. cn-north-1)", + "workflow_node.apply.form.jdcloud_dns_region_id.tooltip": "For more information, see <a href=\"https://docs.jdcloud.com/en/common-declaration/api/introduction\" target=\"_blank\">https://docs.jdcloud.com/en/common-declaration/api/introduction</a>", "workflow_node.apply.form.advanced_config.label": "Advanced settings", "workflow_node.apply.form.key_algorithm.label": "Certificate key algorithm", "workflow_node.apply.form.key_algorithm.placeholder": "Please select certificate key algorithm", @@ -175,6 +178,12 @@ "workflow_node.deploy.form.aliyun_oss_domain.label": "Alibaba Cloud OSS domain", "workflow_node.deploy.form.aliyun_oss_domain.placeholder": "Please enter Alibaba Cloud OSS domain name", "workflow_node.deploy.form.aliyun_oss_domain.tooltip": "For more information, see <a href=\"https://oss.console.aliyun.com\" target=\"_blank\">https://oss.console.aliyun.com</a>", + "workflow_node.deploy.form.aliyun_vod_region.label": "Alibaba Cloud VOD region", + "workflow_node.deploy.form.aliyun_vod_region.placeholder": "Please enter Alibaba Cloud VOD region (e.g. cn-hangzhou)", + "workflow_node.deploy.form.aliyun_vod_region.tooltip": "For more information, see <a href=\"https://www.alibabacloud.com/help/en/vod/product-overview/regions\" target=\"_blank\">https://www.alibabacloud.com/help/en/vod/product-overview/regions</a>", + "workflow_node.deploy.form.aliyun_vod_domain.label": "Alibaba Cloud VOD domain", + "workflow_node.deploy.form.aliyun_vod_domain.placeholder": "Please enter Alibaba Cloud VOD domain name", + "workflow_node.deploy.form.aliyun_vod_domain.tooltip": "For more information, see <a href=\"https://vod.console.aliyun.com\" target=\"_blank\">https://vod.console.aliyun.com</a>", "workflow_node.deploy.form.aliyun_waf_region.label": "Alibaba Cloud WAF region", "workflow_node.deploy.form.aliyun_waf_region.placeholder": "Please enter Alibaba Cloud WAF region (e.g. cn-hangzhou)", "workflow_node.deploy.form.aliyun_waf_region.tooltip": "For more information, see <a href=\"https://www.alibabacloud.com/help/en/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-endpoint\" target=\"_blank\">https://www.alibabacloud.com/help/en/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-endpoint</a>", @@ -190,18 +199,43 @@ "workflow_node.deploy.form.baiducloud_cdn_domain.label": "Baidu Cloud CDN domain", "workflow_node.deploy.form.baiducloud_cdn_domain.placeholder": "Please enter Baidu Cloud CDN domain name", "workflow_node.deploy.form.baiducloud_cdn_domain.tooltip": "For more information, see <a href=\"https://console.bce.baidu.com/cdn\" target=\"_blank\">https://console.bce.baidu.com/cdn</a>", + "workflow_node.deploy.form.baishan_cdn_domain.label": "Baishan CDN domain", + "workflow_node.deploy.form.baishan_cdn_domain.placeholder": "Please enter Baishan CDN domain name", + "workflow_node.deploy.form.baishan_cdn_domain.tooltip": "For more information, see <a href=\"https://cdnx.console.baishan.com\" target=\"_blank\">https://cdnx.console.baishan.com</a>", + "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "Auto restart after deployment", + "workflow_node.deploy.form.baotapanel_site_type.label": "BaoTa Panel site type", + "workflow_node.deploy.form.baotapanel_site_type.placeholder": "Please select BaoTa Panel site type", + "workflow_node.deploy.form.baotapanel_site_type.option.php.label": "PHP sites", + "workflow_node.deploy.form.baotapanel_site_type.option.other.label": "Other sites", "workflow_node.deploy.form.baotapanel_site_name.label": "BaoTa Panel site name", "workflow_node.deploy.form.baotapanel_site_name.placeholder": "Please enter BaoTa Panel site name", "workflow_node.deploy.form.baotapanel_site_name.tooltip": "Usually equal to the website domain name.", + "workflow_node.deploy.form.baotapanel_site_names.label": "BaoTa Panel site names", + "workflow_node.deploy.form.baotapanel_site_names.placeholder": "Please enter BaoTa Panel site names (separated by semicolons)", + "workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid": "Please enter a valid BaoTa Panel site name", + "workflow_node.deploy.form.baotapanel_site_names.tooltip": "Usually equal to the websites domain name.", + "workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title": "Change BaoTa Panel site names", + "workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder": "Please enter BaoTa Panel site name", "workflow_node.deploy.form.byteplus_cdn_domain.label": "BytePlus CDN domain", "workflow_node.deploy.form.byteplus_cdn_domain.placeholder": "Please enter BytePlus CDN domain name", "workflow_node.deploy.form.byteplus_cdn_domain.tooltip": "For more information, see <a href=\"https://console.byteplus.com/cdn\" target=\"_blank\">https://console.byteplus.com/cdn</a>", + "workflow_node.deploy.form.cdnfly_resource_type.label": "Resource type", + "workflow_node.deploy.form.cdnfly_resource_type.placeholder": "Please select resource type", + "workflow_node.deploy.form.cdnfly_resource_type.option.site.label": "Site", + "workflow_node.deploy.form.cdnfly_resource_type.option.certificate.label": "Certificate", + "workflow_node.deploy.form.cdnfly_site_id.label": "Cdnfly site ID", + "workflow_node.deploy.form.cdnfly_site_id.placeholder": "Please enter Cdnfly site ID", + "workflow_node.deploy.form.cdnfly_certificate_id.label": "Cdnfly certificate ID", + "workflow_node.deploy.form.cdnfly_certificate_id.placeholder": "Please enter Cdnfly certificate ID", "workflow_node.deploy.form.dogecloud_cdn_domain.label": "Doge Cloud CDN domain", "workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "Please enter Doge Cloud CDN domain name", "workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "For more information, see <a href=\"https://console.dogecloud.com/\" target=\"_blank\">https://console.dogecloud.com/</a>", "workflow_node.deploy.form.edgio_applications_environment_id.label": "Edgio Applications environment ID", "workflow_node.deploy.form.edgio_applications_environment_id.placeholder": "Please enter Edgio Applications environment ID", "workflow_node.deploy.form.edgio_applications_environment_id.tooltip": "For more information, see <a href=\"https://edgio.app/\" target=\"_blank\">https://edgio.app/</a>", + "workflow_node.deploy.form.gcore_cdn_resource_id.label": "Gcore CDN resource ID", + "workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "Please enter Gcore CDN resource ID", + "workflow_node.deploy.form.gcore_cdn_resource_id.tooltip": "For more information, see <a href=\"https://cdn.gcore.com/resources/list\" target=\"_blank\">https://cdn.gcore.com/resources/list</a>", "workflow_node.deploy.form.huaweicloud_cdn_region.label": "Huawei Cloud CDN region", "workflow_node.deploy.form.huaweicloud_cdn_region.placeholder": "Please enter Huawei Cloud CDN region (e.g. cn-north-1)", "workflow_node.deploy.form.huaweicloud_cdn_region.tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/apiexplorer/#/endpoint?locale=en-us\" target=\"_blank\">https://console-intl.huaweicloud.com/apiexplorer/#/endpoint</a>", @@ -225,6 +259,45 @@ "workflow_node.deploy.form.huaweicloud_elb_listener_id.label": "Huawei Cloud ELB listener ID", "workflow_node.deploy.form.huaweicloud_elb_listener_id.placeholder": "Please enter Huawei Cloud ELB listener ID", "workflow_node.deploy.form.huaweicloud_elb_listener_id.tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/vpc/#/elb/list/grid\" target=\"_blank\">https://console-intl.huaweicloud.com/vpc/#/elb/list/grid</a>", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.label": "Resource type", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.placeholder": "Please select resource type", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.option.certificate.label": "WAF certificate", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.option.cloudserver.label": "WAF cloud server", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.option.premiumhost.label": "WAF premium host", + "workflow_node.deploy.form.huaweicloud_waf_region.label": "Huawei Cloud WAF region", + "workflow_node.deploy.form.huaweicloud_waf_region.placeholder": "Please enter Huawei Cloud WAF region (e.g. cn-north-1)", + "workflow_node.deploy.form.huaweicloud_waf_region.tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/apiexplorer/#/endpoint?locale=en-us\" target=\"_blank\">https://console-intl.huaweicloud.com/apiexplorer/#/endpoint</a>", + "workflow_node.deploy.form.huaweicloud_waf_certificate_id.label": "Huawei Cloud WAF certificate ID", + "workflow_node.deploy.form.huaweicloud_waf_certificate_id.placeholder": "Please enter Huawei Cloud WAF certificate ID", + "workflow_node.deploy.form.huaweicloud_waf_certificate_id.tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/console/#/waf/certificateManagement\" target=\"_blank\">https://console-intl.huaweicloud.com/console/#/waf/certificateManagement</a>", + "workflow_node.deploy.form.huaweicloud_waf_domain.label": "Huawei Cloud WAF domain", + "workflow_node.deploy.form.huaweicloud_waf_domain.placeholder": "Please enter Huawei Cloud WAF domain name", + "workflow_node.deploy.form.huaweicloud_waf_domain.tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/console/#/waf/domain/list\" target=\"_blank\">https://console-intl.huaweicloud.com/console/#/waf/domain/list</a>", + "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "Resource type", + "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "Please select resource type", + "workflow_node.deploy.form.jdcloud_alb_resource_type.option.loadbalancer.label": "ALB load balancer", + "workflow_node.deploy.form.jdcloud_alb_resource_type.option.listener.label": "ALB listener", + "workflow_node.deploy.form.jdcloud_alb_region_id.label": "JD Cloud ALB region ID", + "workflow_node.deploy.form.jdcloud_alb_region_id.placeholder": "Please enter JD Cloud ALB region ID (e.g. cn-north-1)", + "workflow_node.deploy.form.jdcloud_alb_region_id.tooltip": "For more information, see <a href=\"https://docs.jdcloud.com/en/common-declaration/api/introduction\" target=\"_blank\">https://docs.jdcloud.com/en/common-declaration/api/introduction</a>", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.label": "JD Cloud ALB load balancer ID", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder": "Please enter JD Cloud ALB load balancer ID", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.tooltip": "For more information, see <a href=\"https://cns-console.jdcloud.com/host/loadBalance/list\" target=\"_blank\">https://cns-console.jdcloud.com/host/loadBalance/list</a>", + "workflow_node.deploy.form.jdcloud_alb_listener_id.label": "JD Cloud ALB listener ID", + "workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder": "Please enter JD Cloud ALB listener ID", + "workflow_node.deploy.form.jdcloud_alb_listener_id.tooltip": "For more information, see <a href=\"https://cns-console.jdcloud.com/host/loadBalance/list\" target=\"_blank\">https://cns-console.jdcloud.com/host/loadBalance/list</a>", + "workflow_node.deploy.form.jdcloud_alb_snidomain.label": "JD Cloud ALB SNI domain (Optional)", + "workflow_node.deploy.form.jdcloud_alb_snidomain.placeholder": "Please enter JD Cloud ALB SNI domain name", + "workflow_node.deploy.form.jdcloud_alb_snidomain.tooltip": "For more information, see <a href=\"https://cns-console.jdcloud.com/host/loadBalance/list\" target=\"_blank\">https://cns-console.jdcloud.com/host/loadBalance/list</a>", + "workflow_node.deploy.form.jdcloud_cdn_domain.label": "JD Cloud CDN domain", + "workflow_node.deploy.form.jdcloud_cdn_domain.placeholder": "Please enter JD Cloud CDN domain name", + "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "For more information, see <a href=\"https://cdn-console.jdcloud.com/\" target=\"_blank\">https://cdn-console.jdcloud.com/</a>", + "workflow_node.deploy.form.jdcloud_live_domain.label": "JD Cloud Live Video play domain", + "workflow_node.deploy.form.jdcloud_live_domain.placeholder": "Please enter JD Cloud Live Video play domain name", + "workflow_node.deploy.form.jdcloud_live_domain.tooltip": "For more information, see <a href=\"https://live-console.jdcloud.com/\" target=\"_blank\">https://live-console.jdcloud.com/</a>", + "workflow_node.deploy.form.jdcloud_vod_domain.label": "JD Cloud VOD domain", + "workflow_node.deploy.form.jdcloud_vod_domain.placeholder": "Please enter JD Cloud VOD domain name", + "workflow_node.deploy.form.jdcloud_vod_domain.tooltip": "For more information, see <a href=\"https://vod-console.jdcloud.com/\" target=\"_blank\">https://vod-console.jdcloud.com/</a>", "workflow_node.deploy.form.k8s_namespace.label": "Kubernetes Namespace", "workflow_node.deploy.form.k8s_namespace.placeholder": "Please enter Kubernetes Namespace", "workflow_node.deploy.form.k8s_namespace.tooltip": "For more information, see <a href=\"https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/\" target=\"_blank\">https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/</a>", @@ -285,6 +358,11 @@ "workflow_node.deploy.form.qiniu_pili_domain.label": "Qiniu Pili streaming domain", "workflow_node.deploy.form.qiniu_pili_domain.placeholder": "Please enter Qiniu Pili streaming domain name", "workflow_node.deploy.form.qiniu_pili_domain.tooltip": "For more information, see <a href=\"hhttps://portal.qiniu.com/hub\" target=\"_blank\">https://portal.qiniu.com/hub</a>", + "workflow_node.deploy.form.safeline_resource_type.label": "Resource type", + "workflow_node.deploy.form.safeline_resource_type.placeholder": "Please select resource type", + "workflow_node.deploy.form.safeline_resource_type.option.certificate.label": "Certificate", + "workflow_node.deploy.form.safeline_certificate_id.label": "SafeLine certificate ID", + "workflow_node.deploy.form.safeline_certificate_id.placeholder": "Please enter SafeLine certificate ID", "workflow_node.deploy.form.ssh_format.label": "File format", "workflow_node.deploy.form.ssh_format.placeholder": "Please select file format", "workflow_node.deploy.form.ssh_format.option.pem.label": "PEM (*.pem, *.crt, *.key)", @@ -351,9 +429,9 @@ "workflow_node.deploy.form.tencentcloud_cos_domain.label": "Tencent Cloud COS domain", "workflow_node.deploy.form.tencentcloud_cos_domain.placeholder": "Please enter Tencent Cloud COS domain name", "workflow_node.deploy.form.tencentcloud_cos_domain.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/cos\" target=\"_blank\">https://console.tencentcloud.com/cos</a>", - "workflow_node.deploy.form.tencentcloud_css_domain.label": "Tencent Cloud CSS playing domain", - "workflow_node.deploy.form.tencentcloud_css_domain.placeholder": "Please enter Tencent Cloud CSS playing domain name", - "workflow_node.deploy.form.tencentcloud_css_domain.tooltip": "For more information, see <a href=\"https://console.cloud.tencent.com/live/livestat\" target=\"_blank\">https://console.cloud.tencent.com/live/livestat</a>", + "workflow_node.deploy.form.tencentcloud_css_domain.label": "Tencent Cloud CSS play domain", + "workflow_node.deploy.form.tencentcloud_css_domain.placeholder": "Please enter Tencent Cloud CSS play domain name", + "workflow_node.deploy.form.tencentcloud_css_domain.tooltip": "For more information, see <a href=\"https://console.cloud.tencent.com/live\" target=\"_blank\">https://console.cloud.tencent.com/live</a>", "workflow_node.deploy.form.tencentcloud_ecdn_domain.label": "Tencent Cloud ECDN domain", "workflow_node.deploy.form.tencentcloud_ecdn_domain.placeholder": "Please enter Tencent Cloud ECDN domain name", "workflow_node.deploy.form.tencentcloud_ecdn_domain.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/cdn\" target=\"_blank\">https://console.tencentcloud.com/cdn</a>", @@ -373,6 +451,24 @@ "workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.tooltip": "For more information, see <a href=\"https://cloud.tencent.com.cn/document/product/400/91667\" target=\"_blank\">https://cloud.tencent.com.cn/document/product/400/91667</a>", "workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.multiple_input_modal.title": "Change Tencent Cloud resource IDs", "workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.multiple_input_modal.placeholder": "Please enter Tencent Cloud resouce ID", + "workflow_node.deploy.form.tencentcloud_vod_sub_app_id.label": "Tencent Cloud VOD App ID", + "workflow_node.deploy.form.tencentcloud_vod_sub_app_id.placeholder": "Please enter Tencent Cloud VOD App ID", + "workflow_node.deploy.form.tencentcloud_vod_sub_app_id.tooltip": "For more information, see <a href=\"https://console.cloud.tencent.com/vod\" target=\"_blank\">https://console.cloud.tencent.com/vod</a>", + "workflow_node.deploy.form.tencentcloud_vod_domain.label": "Tencent Cloud VOD domain", + "workflow_node.deploy.form.tencentcloud_vod_domain.placeholder": "Please enter Tencent Cloud VOD domain name", + "workflow_node.deploy.form.tencentcloud_vod_domain.tooltip": "For more information, see <a href=\"https://console.cloud.tencent.com/vod\" target=\"_blank\">https://console.cloud.tencent.com/vod</a>", + "workflow_node.deploy.form.tencentcloud_waf_region.label": "Tencent Cloud WAF region", + "workflow_node.deploy.form.tencentcloud_waf_region.placeholder": "Please enter Tencent Cloud WAF region (e.g. ap-guangzhou)", + "workflow_node.deploy.form.tencentcloud_waf_region.tooltip": "For more information, see <a href=\"https://www.tencentcloud.com/document/product/627/38085\" target=\"_blank\">https://www.tencentcloud.com/document/product/627/38085</a>", + "workflow_node.deploy.form.tencentcloud_waf_domain.label": "Tencent Cloud WAF domain", + "workflow_node.deploy.form.tencentcloud_waf_domain.placeholder": "Please enter Tencent Cloud WAF domain name", + "workflow_node.deploy.form.tencentcloud_waf_domain.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/waf\" target=\"_blank\">https://console.tencentcloud.com/waf</a>", + "workflow_node.deploy.form.tencentcloud_waf_domain_id.label": "Tencent Cloud WAF domain ID", + "workflow_node.deploy.form.tencentcloud_waf_domain_id.placeholder": "Please enter Tencent Cloud WAF domain ID", + "workflow_node.deploy.form.tencentcloud_waf_domain_id.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/waf\" target=\"_blank\">https://console.tencentcloud.com/waf</a>", + "workflow_node.deploy.form.tencentcloud_waf_instance_id.label": "Tencent Cloud WAF instance ID", + "workflow_node.deploy.form.tencentcloud_waf_instance_id.placeholder": "Please enter Tencent Cloud WAF instance ID", + "workflow_node.deploy.form.tencentcloud_waf_instance_id.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/waf\" target=\"_blank\">https://console.tencentcloud.com/waf</a>", "workflow_node.deploy.form.ucloud_ucdn_domain_id.label": "UCloud UCDN domain ID", "workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder": "Please enter UCloud UCDN domain ID", "workflow_node.deploy.form.ucloud_ucdn_domain_id.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ucdn\" target=\"_blank\">https://console.ucloud-global.com/ucdn</a>", @@ -400,6 +496,15 @@ "workflow_node.deploy.form.volcengine_dcdn_domain.label": "VolcEngine DCDN domain", "workflow_node.deploy.form.volcengine_dcdn_domain.placeholder": "Please enter VolcEngine DCDN domain name", "workflow_node.deploy.form.volcengine_dcdn_domain.tooltip": "For more information, see <a href=\"https://console.volcengine.com/dcdn/dashboard\" target=\"_blank\">https://console.volcengine.com/dcdn/dashboard</a>", + "workflow_node.deploy.form.volcengine_imagex_region.label": "VolcEngine ImageX region", + "workflow_node.deploy.form.volcengine_imagex_region.placeholder": "Please enter VolcEngine ImageX region (e.g. cn-north-1)", + "workflow_node.deploy.form.volcengine_imagex_region.tooltip": "For more information, see <a href=\"https://www.volcengine.com/docs/508/23757\" target=\"_blank\">https://www.volcengine.com/docs/508/23757</a>", + "workflow_node.deploy.form.volcengine_imagex_service_id.label": "VolcEngine ImageX service ID", + "workflow_node.deploy.form.volcengine_imagex_service_id.placeholder": "Please enter VolcEngine ImageX service ID", + "workflow_node.deploy.form.volcengine_imagex_service_id.tooltip": "For more information, see <a href=\"https://console.volcengine.com/imagex\" target=\"_blank\">https://console.volcengine.com/imagex</a>", + "workflow_node.deploy.form.volcengine_imagex_domain.label": "VolcEngine ImageX domain", + "workflow_node.deploy.form.volcengine_imagex_domain.placeholder": "Please enter VolcEngine ImageX domain name", + "workflow_node.deploy.form.volcengine_imagex_domain.tooltip": "For more information, see <a href=\"https://console.volcengine.com/imagex\" target=\"_blank\">https://console.volcengine.com/imagex</a>", "workflow_node.deploy.form.volcengine_live_domain.label": "VolcEngine Live streaming domain", "workflow_node.deploy.form.volcengine_live_domain.placeholder": "Please enter VolcEngine Live streaming domain name", "workflow_node.deploy.form.volcengine_live_domain.tooltip": "For more information, see <a href=\"https://console.volcengine.com/live\" target=\"_blank\">https://console.volcengine.com/live</a>", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 37ebb112..64b034d5 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -38,8 +38,8 @@ "access.form.aliyun_access_key_id.label": "阿里云 AccessKeyId", "access.form.aliyun_access_key_id.placeholder": "请输入阿里云 AccessKeyId", "access.form.aliyun_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair\" target=\"_blank\">https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair</a>", - "access.form.aliyun_access_key_secret.label": "阿里云 AccessKey Secret", - "access.form.aliyun_access_key_secret.placeholder": "请输入阿里云 AccessKey Secret", + "access.form.aliyun_access_key_secret.label": "阿里云 AccessKeySecret", + "access.form.aliyun_access_key_secret.placeholder": "请输入阿里云 AccessKeySecret", "access.form.aliyun_access_key_secret.tooltip": "这是什么?请参阅 <a href=\"https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair\" target=\"_blank\">https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair</a>", "access.form.aws_access_key_id.label": "AWS AccessKeyId", "access.form.aws_access_key_id.placeholder": "请输入 AWS AccessKeyId", @@ -65,6 +65,8 @@ "access.form.baiducloud_secret_access_key.label": "百度智能云 SecretAccessKey", "access.form.baiducloud_secret_access_key.placeholder": "请输入百度智能云 SecretAccessKey", "access.form.baiducloud_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://cloud.baidu.com/doc/Reference/s/jjwvz2e3p\" target=\"_blank\">https://cloud.baidu.com/doc/Reference/s/jjwvz2e3p</a>", + "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_api_url.tooltip": "这是什么?请参阅 <a href=\"https://www.bt.cn/bbs/thread-20376-1-1.html\" target=\"_blank\">https://www.bt.cn/bbs/thread-20376-1-1.html</a>", @@ -77,6 +79,18 @@ "access.form.byteplus_secret_key.label": "BytePlus SecretKey", "access.form.byteplus_secret_key.placeholder": "请输入 BytePlus SecretKey", "access.form.byteplus_secret_key.tooltip": "这是什么?请参阅 <a href=\"https://docs.byteplus.com/zh-CN/docs/byteplus-platform/docs-managing-keys\" target=\"_blank\">https://docs.byteplus.com/zh-CN/docs/byteplus-platform/docs-managing-keys</a>", + "access.form.cachefly_api_token.label": "CacheFly API Token", + "access.form.cachefly_api_token.placeholder": "请输入 CacheFly API Token", + "access.form.cachefly_api_token.tooltip": "这是什么?请参阅 <a href=\"https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228\" target=\"_blank\">https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228</a>", + "access.form.cdnfly_api_url.label": "Cdnfly API URL", + "access.form.cdnfly_api_url.placeholder": "请输入 Cdnfly API URL", + "access.form.cdnfly_api_url.tooltip": "这是什么?请参阅 <a href=\"https://doc.cdnfly.cn/anzhuangshuoming.html\" target=\"_blank\">https://doc.cdnfly.cn/anzhuangshuoming.html</a>", + "access.form.cdnfly_api_key.label": "Cdnfly 用户端 API Key", + "access.form.cdnfly_api_key.placeholder": "请输入 Cdnfly 用户端 API Key", + "access.form.cdnfly_api_key.tooltip": "这是什么?请参阅 <a href=\"https://doc.cdnfly.cn/shiyongjieshao.html\" target=\"_blank\">https://doc.cdnfly.cn/shiyongjieshao.html</a>", + "access.form.cdnfly_api_secret.label": "Cdnfly 用户端 API Secret", + "access.form.cdnfly_api_secret.placeholder": "请输入 Cdnfly 用户端 API Secret", + "access.form.cdnfly_api_secret.tooltip": "这是什么?请参阅 <a href=\"https://doc.cdnfly.cn/shiyongjieshao.html\" target=\"_blank\">https://doc.cdnfly.cn/shiyongjieshao.html</a>", "access.form.cloudflare_dns_api_token.label": "Cloudflare API Token", "access.form.cloudflare_dns_api_token.placeholder": "请输入 Cloudflare API Token", "access.form.cloudflare_dns_api_token.tooltip": "这是什么?请参阅 <a href=\"https://developers.cloudflare.com/fundamentals/api/get-started/create-token/\" target=\"_blank\">https://developers.cloudflare.com/fundamentals/api/get-started/create-token/</a>", @@ -86,6 +100,18 @@ "access.form.cloudns_auth_password.label": "ClouDNS API 用户密码", "access.form.cloudns_auth_password.placeholder": "请输入 ClouDNS API 用户密码", "access.form.cloudns_auth_password.tooltip": "这是什么?请参阅 <a href=\"https://www.cloudns.net/wiki/article/42/\" target=\"_blank\">https://www.cloudns.net/wiki/article/42/</a>", + "access.form.cmcccloud_access_key_id.label": "移动云 AccessKeyId", + "access.form.cmcccloud_access_key_id.placeholder": "请输入移动云 AccessKeyId", + "access.form.cmcccloud_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</a>", + "access.form.cmcccloud_access_key_secret.label": "移动云 AccessKeySecret", + "access.form.cmcccloud_access_key_secret.placeholder": "请输入移动云 AccessKeySecret", + "access.form.cmcccloud_access_key_secret.tooltip": "这是什么?请参阅 <a href=\"https://ecloud.10086.cn/op-help-center/doc/article/49739\" target=\"_blank\">https://ecloud.10086.cn/op-help-center/doc/article/49739</a>", + "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": "这是什么?请参阅 <a href=\"https://www.dns.la/docs/ApiDoc\" target=\"_blank\">https://www.dns.la/docs/ApiDoc</a>", + "access.form.dnsla_api_secret.label": "DNS.LA API 密钥", + "access.form.dnsla_api_secret.placeholder": "请输入 DNS.LA API 密钥", + "access.form.dnsla_api_secret.tooltip": "这是什么?请参阅 <a href=\"https://www.dns.la/docs/ApiDoc\" target=\"_blank\">https://www.dns.la/docs/ApiDoc</a>", "access.form.dogecloud_access_key.label": "多吉云 AccessKey", "access.form.dogecloud_access_key.placeholder": "请输入多吉云 AccessKey", "access.form.dogecloud_access_key.tooltip": "这是什么?请参阅 <a href=\"https://console.dogecloud.com/\" target=\"_blank\">https://console.dogecloud.com/</a>", @@ -98,6 +124,9 @@ "access.form.edgio_client_secret.label": "Edgio 客户端密码", "access.form.edgio_client_secret.placeholder": "请输入 Edgio 客户端密码", "access.form.edgio_client_secret.tooltip": "这是什么?请参阅 <a href=\"https://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients\" target=\"_blank\">https://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients</a>", + "access.form.gcore_api_token.label": "Gcore API Token", + "access.form.gcore_api_token.placeholder": "请输入 Gcore API Token", + "access.form.gcore_api_token.tooltip": "这是什么?请参阅 <a href=\"https://api.gcore.com/docs/iam#section/Authentication\" target=\"_blank\">https://api.gcore.com/docs/iam#section/Authentication</a>", "access.form.gname_app_id.label": "GNAME AppId", "access.form.gname_app_id.placeholder": "请输入 GNAME AppId", "access.form.gname_app_id.tooltip": "这是什么?请参阅 <a href=\"https://www.gname.com/user#/dealer_api\" target=\"_blank\">https://www.gname.com/user#/dealer_api</a>", @@ -116,10 +145,22 @@ "access.form.huaweicloud_secret_access_key.label": "华为云 SecretAccessKey", "access.form.huaweicloud_secret_access_key.placeholder": "请输入华为云 SecretAccessKey", "access.form.huaweicloud_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html\" target=\"_blank\">https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html</a>", + "access.form.jdcloud_access_key_id.label": "京东云 AccessKeyId", + "access.form.jdcloud_access_key_id.placeholder": "请输入京东云 AccessKeyId", + "access.form.jdcloud_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.jdcloud.com/cn/account-management/accesskey-management\" target=\"_blank\">https://docs.jdcloud.com/cn/account-management/accesskey-management</a>", + "access.form.jdcloud_access_key_secret.label": "京东云 AccessKeySecret", + "access.form.jdcloud_access_key_secret.placeholder": "请输入京东云 AccessKeySecret", + "access.form.jdcloud_access_key_secret.tooltip": "这是什么?请参阅 <a href=\"https://docs.jdcloud.com/cn/account-management/accesskey-management\" target=\"_blank\">https://docs.jdcloud.com/cn/account-management/accesskey-management</a>", "access.form.k8s_kubeconfig.label": "KubeConfig", "access.form.k8s_kubeconfig.placeholder": "请选择 KubeConfig 文件", "access.form.k8s_kubeconfig.upload": "选择文件", "access.form.k8s_kubeconfig.tooltip": "这是什么?请参阅 <a href=\"https://kubernetes.io/zh-cn/docs/concepts/configuration/organize-cluster-access-kubeconfig/\" target=\"_blank\">https://kubernetes.io/zh-cn/docs/concepts/configuration/organize-cluster-access-kubeconfig/</a><br><br>为空时,将使用 Pod 的 ServiceAccount 作为凭证。", + "access.form.namecheap_username.label": "Namecheap 用户名", + "access.form.namecheap_username.placeholder": "请输入 Namecheap 用户名", + "access.form.namecheap_username.tooltip": "这是什么?请参阅 <a href=\"https://www.namecheap.com/support/api/intro/\" target=\"_blank\">https://www.namecheap.com/support/api/intro/</a>", + "access.form.namecheap_api_key.label": "Namecheap API Key", + "access.form.namecheap_api_key.placeholder": "请输入 Namecheap API Key", + "access.form.namecheap_api_key.tooltip": "这是什么?请参阅 <a href=\"https://www.namecheap.com/support/api/intro/\" target=\"_blank\">https://www.namecheap.com/support/api/intro/</a>", "access.form.namedotcom_username.label": "Name.com 用户名", "access.form.namedotcom_username.placeholder": "请输入 Name.com 用户名", "access.form.namedotcom_username.tooltip": "这是什么?请参阅 <a href=\"https://www.name.com/account/settings/api\" target=\"_blank\">https://www.name.com/account/settings/api</a>", @@ -147,6 +188,12 @@ "access.form.rainyun_api_key.label": "雨云 API 密钥", "access.form.rainyun_api_key.placeholder": "请输入雨云 API 密钥", "access.form.rainyun_api_key.tooltip": "这是什么?请参阅 <a href=\"https://www.rainyun.com/docs/account/racc/setting#api%E5%AF%86%E9%92%A5\" target=\"_blank\">https://www.rainyun.com/docs/account/racc/setting</a>", + "access.form.safeline_api_url.label": "雷池 URL", + "access.form.safeline_api_url.placeholder": "请输入雷池 URL", + "access.form.safeline_api_url.tooltip": "这是什么?请参阅 <a href=\"https://docs.waf-ce.chaitin.cn/zh/%E4%B8%8A%E6%89%8B%E6%8C%87%E5%8D%97/%E5%AE%89%E8%A3%85%E9%9B%B7%E6%B1%A0#%E8%AE%BF%E9%97%AE%E9%9B%B7%E6%B1%A0%E6%8E%A7%E5%88%B6%E5%8F%B0\" target=\"_blank\">https://docs.waf-ce.chaitin.cn/zh/上手指南/安装雷池</a>", + "access.form.safeline_api_token.label": "雷池 API Token", + "access.form.safeline_api_token.placeholder": "请输入雷池 API Token", + "access.form.safeline_api_token.tooltip": "这是什么?请参阅 <a href=\"https://docs.waf-ce.chaitin.cn/zh/%E6%9B%B4%E5%A4%9A%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3/OPENAPI\" target=\"_blank\">https://docs.waf-ce.chaitin.cn/zh/更多技术文档/OPENAPI</a>", "access.form.ssh_host.label": "服务器地址", "access.form.ssh_host.placeholder": "请输入服务器地址", "access.form.ssh_port.label": "服务器端口", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index a4146670..f8d36830 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -12,6 +12,7 @@ "provider.aliyun.live": "阿里云 - 视频直播 Live", "provider.aliyun.nlb": "阿里云 - 网络型负载均衡 NLB", "provider.aliyun.oss": "阿里云 - 对象存储 OSS", + "provider.aliyun.vod": "阿里云 - 视频点播 VOD", "provider.aliyun.waf": "阿里云 - Web 应用防火墙 WAF", "provider.akamai": "Akamai", "provider.akamai.cdn": "Akamai - 内容分发网络 CDN", @@ -22,25 +23,29 @@ "provider.azure.dns": "Azure - DNS", "provider.baiducloud": "百度智能云", "provider.baiducloud.cdn": "百度智能云 - 内容分发网络 CDN", + "provider.baiducloud.dns": "百度智能云 - 智能云解析 DNS", "provider.baishan": "白山云", "provider.baishan.cdn": "白山云 - 内容分发网络 CDN", "provider.baotapanel": "宝塔面板", + "provider.baotapanel.console": "宝塔面板 - 面板", "provider.baotapanel.site": "宝塔面板 - 网站", "provider.byteplus": "BytePlus", "provider.byteplus.cdn": "BytePlus - 内容分发网络 CDN", - "provider.cachefly": "Cachefly", + "provider.cachefly": "CacheFly", "provider.cdnfly": "Cdnfly", "provider.cloudflare": "Cloudflare", "provider.cloudns": "ClouDNS", "provider.cmcccloud": "移动云", "provider.ctcccloud": "联通云", "provider.cucccloud": "天翼云", + "provider.dnsla": "DNS.LA", "provider.dogecloud": "多吉云", "provider.dogecloud.cdn": "多吉云 - 内容分发网络 CDN", "provider.edgio": "Edgio", "provider.edgio.applications": "Edgio - Applications", "provider.fastly": "Fastly", "provider.gcore": "Gcore", + "provider.gcore.cdn": "Gcore - 内容分发网络 CDN", "provider.gname": "GNAME", "provider.godaddy": "GoDaddy", "provider.goedge": "GoEdge", @@ -49,9 +54,17 @@ "provider.huaweicloud.cdn": "华为云 - 内容分发网络 CDN", "provider.huaweicloud.dns": "华为云 - 云解析 DNS", "provider.huaweicloud.elb": "华为云 - 弹性负载均衡 ELB", + "provider.huaweicloud.waf": "华为云 - Web 应用防火墙 WAF", + "provider.jdcloud": "京东云", + "provider.jdcloud.alb": "京东云 - 应用负载均衡 ALB", + "provider.jdcloud.cdn": "京东云 - 内容分发网络 CDN", + "provider.jdcloud.dns": "京东云 - 云解析 DNS", + "provider.jdcloud.live": "京东云 - 视频直播", + "provider.jdcloud.vod": "京东云 - 视频点播", "provider.kubernetes": "Kubernetes", "provider.kubernetes.secret": "Kubernetes - Secret", "provider.local": "本地部署", + "provider.namecheap": "Namecheap", "provider.namedotcom": "Name.com", "provider.namesilo": "NameSilo", "provider.ns1": "NS1(IBM NS1 Connect)", @@ -71,6 +84,8 @@ "provider.tencentcloud.ecdn": "腾讯云 - 全站加速网络 ECDN", "provider.tencentcloud.eo": "腾讯云 - 边缘安全加速平台 EdgeOne", "provider.tencentcloud.ssl_deploy": "腾讯云 - 通过 SSL 证书服务创建部署任务", + "provider.tencentcloud.vod": "腾讯云 - 云点播 VOD", + "provider.tencentcloud.waf": "腾讯云 - Web 应用防火墙 WAF", "provider.ucloud": "优刻得", "provider.ucloud.ucdn": "优刻得 - 内容分发 UCDN", "provider.ucloud.us3": "优刻得 - 对象存储 US3", @@ -79,6 +94,7 @@ "provider.volcengine.clb": "火山引擎 - 负载均衡 CLB", "provider.volcengine.dcdn": "火山引擎 - 全站加速 DCDN", "provider.volcengine.dns": "火山引擎 - 云解析 DNS", + "provider.volcengine.imagex": "火山引擎 - 图片服务 ImageX", "provider.volcengine.live": "火山引擎 - 视频直播 Live", "provider.volcengine.tos": "火山引擎 - 对象存储 TOS", "provider.webhook": "Webhook", @@ -86,10 +102,10 @@ "provider.category.all": "全部", "provider.category.cdn": "CDN", - "provider.category.storage": "存储", + "provider.category.storage": "文件存储", "provider.category.loadbalance": "负载均衡", "provider.category.firewall": "防火墙", - "provider.category.live": "直播", + "provider.category.av": "音视频", "provider.category.website": "网站", "provider.category.other": "其他" } diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 705d8056..a4c0998b 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -46,6 +46,9 @@ "workflow_node.apply.form.huaweicloud_dns_region.label": "华为云 DNS 服务区域", "workflow_node.apply.form.huaweicloud_dns_region.placeholder": "请输入华为云 DNS 服务区域(例如:cn-north-1)", "workflow_node.apply.form.huaweicloud_dns_region.tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/apiexplorer/#/endpoint\" target=\"_blank\">https://console.huaweicloud.com/apiexplorer/#/endpoint</a>", + "workflow_node.apply.form.jdcloud_dns_region_id.label": "京东云 DNS 服务地域 ID", + "workflow_node.apply.form.jdcloud_dns_region_id.placeholder": "请输入京东云 DNS 服务地域 ID(例如:cn-north-1)", + "workflow_node.apply.form.jdcloud_dns_region_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.jdcloud.com/cn/common-declaration/api/introduction\" target=\"_blank\">https://docs.jdcloud.com/cn/common-declaration/api/introduction</a>", "workflow_node.apply.form.advanced_config.label": "高级设置", "workflow_node.apply.form.key_algorithm.label": "数字证书算法", "workflow_node.apply.form.key_algorithm.placeholder": "请选择数字证书算法", @@ -101,7 +104,7 @@ "workflow_node.deploy.form.aliyun_alb_listener_id.placeholder": "请输入阿里云 ALB 监听器 ID", "workflow_node.deploy.form.aliyun_alb_listener_id.tooltip": "这是什么?请参阅 <a href=\"https://slb.console.aliyun.com/alb\" target=\"_blank\">https://slb.console.aliyun.com/alb</a>", "workflow_node.deploy.form.aliyun_alb_snidomain.label": "阿里云 ALB 扩展域名(可选)", - "workflow_node.deploy.form.aliyun_alb_snidomain.placeholder": "请输入阿里云 ALB 扩展域名", + "workflow_node.deploy.form.aliyun_alb_snidomain.placeholder": "请输入阿里云 ALB 扩展域名(支持泛域名)", "workflow_node.deploy.form.aliyun_alb_snidomain.tooltip": "这是什么?请参阅 <a href=\"https://slb.console.aliyun.com/alb\" target=\"_blank\">https://slb.console.aliyun.com/alb</a><br><br>不填写时,将替换监听器的默认证书。", "workflow_node.deploy.form.aliyun_cas_deploy.guide": "小贴士:由于阿里云证书部署任务是异步的,此节点若执行成功仅代表已创建部署任务,实际部署结果需要你自行前往阿里云控制台查询。", "workflow_node.deploy.form.aliyun_cas_deploy_region.label": "阿里云 CAS 服务地域", @@ -133,14 +136,14 @@ "workflow_node.deploy.form.aliyun_clb_listener_port.placeholder": "请输入阿里云 CLB 监听端口", "workflow_node.deploy.form.aliyun_clb_listener_port.tooltip": "这是什么?请参阅 <a href=\"https://slb.console.aliyun.com/clb\" target=\"_blank\">https://slb.console.aliyun.com/clb</a>", "workflow_node.deploy.form.aliyun_clb_snidomain.label": "阿里云 CLB 扩展域名(可选)", - "workflow_node.deploy.form.aliyun_clb_snidomain.placeholder": "请输入阿里云 CLB 扩展域名", + "workflow_node.deploy.form.aliyun_clb_snidomain.placeholder": "请输入阿里云 CLB 扩展域名(支持泛域名)", "workflow_node.deploy.form.aliyun_clb_snidomain.tooltip": "这是什么?请参阅 <a href=\"https://slb.console.aliyun.com/clb\" target=\"_blank\">https://slb.console.aliyun.com/clb</a><br><br>不填写时,将替换监听器的默认证书。", - "workflow_node.deploy.form.aliyun_cdn_domain.label": "阿里云 CDN 加速域名(支持泛域名)", - "workflow_node.deploy.form.aliyun_cdn_domain.placeholder": "请输入阿里云 CDN 加速域名", - "workflow_node.deploy.form.aliyun_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://cdn.console.aliyun.com\" target=\"_blank\">https://cdn.console.aliyun.com</a><br><br>泛域名表示形式为:*.example.com", - "workflow_node.deploy.form.aliyun_dcdn_domain.label": "阿里云 DCDN 加速域名(支持泛域名)", - "workflow_node.deploy.form.aliyun_dcdn_domain.placeholder": "请输入阿里云 DCDN 加速域名", - "workflow_node.deploy.form.aliyun_dcdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://dcdn.console.aliyun.com\" target=\"_blank\">https://dcdn.console.aliyun.com</a><br><br>泛域名表示形式为:*.example.com", + "workflow_node.deploy.form.aliyun_cdn_domain.label": "阿里云 CDN 加速域名", + "workflow_node.deploy.form.aliyun_cdn_domain.placeholder": "请输入阿里云 CDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.aliyun_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://cdn.console.aliyun.com\" target=\"_blank\">https://cdn.console.aliyun.com</a>", + "workflow_node.deploy.form.aliyun_dcdn_domain.label": "阿里云 DCDN 加速域名", + "workflow_node.deploy.form.aliyun_dcdn_domain.placeholder": "请输入阿里云 DCDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.aliyun_dcdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://dcdn.console.aliyun.com\" target=\"_blank\">https://dcdn.console.aliyun.com</a>", "workflow_node.deploy.form.aliyun_esa_region.label": "阿里云 ESA 服务地域", "workflow_node.deploy.form.aliyun_esa_region.placeholder": "请输入阿里云 ESA 服务地域(例如:cn-hangzhou)", "workflow_node.deploy.form.aliyun_esa_region.tooltip": "这是什么?请参阅 <a href=\"https://help.aliyun.com/zh/edge-security-acceleration/esa/api-esa-2024-09-10-endpoint\" target=\"_blank\">https://help.aliyun.com/zh/edge-security-acceleration/esa/api-esa-2024-09-10-endpoint</a>", @@ -150,8 +153,8 @@ "workflow_node.deploy.form.aliyun_live_region.label": "阿里云视频直播服务地域", "workflow_node.deploy.form.aliyun_live_region.placeholder": "请输入阿里云视频直播服务地域(例如:cn-hangzhou)", "workflow_node.deploy.form.aliyun_live_region.tooltip": "这是什么?请参阅 <a href=\"https://help.aliyun.com/zh/live/product-overview/supported-regions\" target=\"_blank\">https://help.aliyun.com/zh/live/product-overview/supported-regions</a>", - "workflow_node.deploy.form.aliyun_live_domain.label": "阿里云视频直播流域名(支持泛域名)", - "workflow_node.deploy.form.aliyun_live_domain.placeholder": "请输入阿里云视频直播流域名", + "workflow_node.deploy.form.aliyun_live_domain.label": "阿里云视频直播流域名", + "workflow_node.deploy.form.aliyun_live_domain.placeholder": "请输入阿里云视频直播流域名(支持泛域名)", "workflow_node.deploy.form.aliyun_live_domain.tooltip": "这是什么?请参阅 <a href=\"https://live.console.aliyun.com\" target=\"_blank\">https://live.console.aliyun.com</a>", "workflow_node.deploy.form.aliyun_nlb_resource_type.label": "证书替换方式", "workflow_node.deploy.form.aliyun_nlb_resource_type.placeholder": "请选择证书替换方式", @@ -175,6 +178,12 @@ "workflow_node.deploy.form.aliyun_oss_domain.label": "阿里云 OSS 自定义域名", "workflow_node.deploy.form.aliyun_oss_domain.placeholder": "请输入阿里云 OSS 自定义域名", "workflow_node.deploy.form.aliyun_oss_domain.tooltip": "这是什么?请参阅 see <a href=\"https://oss.console.aliyun.com\" target=\"_blank\">https://oss.console.aliyun.com</a>", + "workflow_node.deploy.form.aliyun_vod_region.label": "阿里云视频点播服务地域", + "workflow_node.deploy.form.aliyun_vod_region.placeholder": "请输入阿里云视频点播服务地域(例如:cn-hangzhou)", + "workflow_node.deploy.form.aliyun_vod_region.tooltip": "这是什么?请参阅 <a href=\"https://help.aliyun.com/zh/vod/product-overview/regions\" target=\"_blank\">https://help.aliyun.com/zh/vod/product-overview/regions</a>", + "workflow_node.deploy.form.aliyun_vod_domain.label": "阿里云视频点播加速域名", + "workflow_node.deploy.form.aliyun_vod_domain.placeholder": "请输入阿里云视频点播加速域名", + "workflow_node.deploy.form.aliyun_vod_domain.tooltip": "这是什么?请参阅 <a href=\"https://vod.console.aliyun.com\" target=\"_blank\">https://vod.console.aliyun.com</a>", "workflow_node.deploy.form.aliyun_waf_region.label": "阿里云 WAF 服务地域", "workflow_node.deploy.form.aliyun_waf_region.placeholder": "请输入阿里云 WAF 服务地域(例如:cn-hangzhou)", "workflow_node.deploy.form.aliyun_waf_region.tooltip": "这是什么?请参阅 <a href=\"https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-endpoint\" target=\"_blank\">https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-endpoint</a>", @@ -187,21 +196,46 @@ "workflow_node.deploy.form.aws_cloudfront_distribution_id.label": "AWS CloudFront 分配 ID", "workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder": "请输入 AWS CloudFront 分配 ID", "workflow_node.deploy.form.aws_cloudfront_distribution_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html</a>", - "workflow_node.deploy.form.baiducloud_cdn_domain.label": "百度智能云 CDN 加速域名(支持泛域名)", - "workflow_node.deploy.form.baiducloud_cdn_domain.placeholder": "请输入百度智能云 CDN 加速域名", - "workflow_node.deploy.form.baiducloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.bce.baidu.com/cdn\" target=\"_blank\">https://console.bce.baidu.com/cdn</a><br><br>泛域名表示形式为:*.example.com", + "workflow_node.deploy.form.baiducloud_cdn_domain.label": "百度智能云 CDN 加速域名", + "workflow_node.deploy.form.baiducloud_cdn_domain.placeholder": "请输入百度智能云 CDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.baiducloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.bce.baidu.com/cdn\" target=\"_blank\">https://console.bce.baidu.com/cdn</a>", + "workflow_node.deploy.form.baishan_cdn_domain.label": "白山云 CDN 加速域名", + "workflow_node.deploy.form.baishan_cdn_domain.placeholder": "请输入白山云 CDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.baishan_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://cdnx.console.baishan.com\" target=\"_blank\">https://cdnx.console.baishan.com</a>", + "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", + "workflow_node.deploy.form.baotapanel_site_type.option.other.label": "其他", "workflow_node.deploy.form.baotapanel_site_name.label": "宝塔面板网站名称", "workflow_node.deploy.form.baotapanel_site_name.placeholder": "请输入宝塔面板网站名称", "workflow_node.deploy.form.baotapanel_site_name.tooltip": "通常为网站域名。", - "workflow_node.deploy.form.byteplus_cdn_domain.label": "BytePlus CDN 域名(支持泛域名)", - "workflow_node.deploy.form.byteplus_cdn_domain.placeholder": "请输入 BytePlus CDN 域名", - "workflow_node.deploy.form.byteplus_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.byteplus.com/cdn\" target=\"_blank\">https://console.byteplus.com/cdn</a><br><br>泛域名表示形式为:*.example.com", + "workflow_node.deploy.form.baotapanel_site_names.label": "宝塔面板网站名称", + "workflow_node.deploy.form.baotapanel_site_names.placeholder": "请输入宝塔面板网站名称(多个值请用半角分号隔开)", + "workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid": "请输入正确的宝塔面板网站名称", + "workflow_node.deploy.form.baotapanel_site_names.tooltip": "通常为网站域名。", + "workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title": "修改宝塔面板网站名称", + "workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder": "请输入宝塔面板网站名称", + "workflow_node.deploy.form.byteplus_cdn_domain.label": "BytePlus CDN 域名", + "workflow_node.deploy.form.byteplus_cdn_domain.placeholder": "请输入 BytePlus CDN 域名(支持泛域名)", + "workflow_node.deploy.form.byteplus_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.byteplus.com/cdn\" target=\"_blank\">https://console.byteplus.com/cdn</a>", + "workflow_node.deploy.form.cdnfly_resource_type.label": "证书替换方式", + "workflow_node.deploy.form.cdnfly_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.cdnfly_resource_type.option.site.label": "替换指定网站的证书", + "workflow_node.deploy.form.cdnfly_resource_type.option.certificate.label": "替换指定证书", + "workflow_node.deploy.form.cdnfly_site_id.label": "Cdnfly 网站 ID", + "workflow_node.deploy.form.cdnfly_site_id.placeholder": "请输入 Cdnfly 网站 ID", + "workflow_node.deploy.form.cdnfly_certificate_id.label": "Cdnfly 证书 ID", + "workflow_node.deploy.form.cdnfly_certificate_id.placeholder": "请输入 Cdnfly 证书 ID", "workflow_node.deploy.form.dogecloud_cdn_domain.label": "多吉云 CDN 加速域名", "workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "请输入多吉云 CDN 加速域名", "workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.dogecloud.com\" target=\"_blank\">https://console.dogecloud.com</a>", "workflow_node.deploy.form.edgio_applications_environment_id.label": "Edgio Applications 环境 ID", "workflow_node.deploy.form.edgio_applications_environment_id.placeholder": "请输入 Edgio Applications 环境 ID", "workflow_node.deploy.form.edgio_applications_environment_id.tooltip": "这是什么?请参阅 <a href=\"https://edgio.app/\" target=\"_blank\">https://edgio.app/</a>", + "workflow_node.deploy.form.gcore_cdn_resource_id.label": "Gcore CDN 资源 ID", + "workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "请输入 Gcore CDN 资源 ID", + "workflow_node.deploy.form.gcore_cdn_resource_id.tooltip": "这是什么?请参阅 <a href=\"https://cdn.gcore.com/resources/list\" target=\"_blank\">https://cdn.gcore.com/resources/list</a>", "workflow_node.deploy.form.huaweicloud_cdn_region.label": "华为云 CDN 服务区域", "workflow_node.deploy.form.huaweicloud_cdn_region.placeholder": "请输入华为云 CDN 服务区域(例如:cn-north-1)", "workflow_node.deploy.form.huaweicloud_cdn_region.tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/apiexplorer/#/endpoint\" target=\"_blank\">https://console.huaweicloud.com/apiexplorer/#/endpoint</a>", @@ -225,6 +259,45 @@ "workflow_node.deploy.form.huaweicloud_elb_listener_id.label": "华为云 ELB 监听器 ID", "workflow_node.deploy.form.huaweicloud_elb_listener_id.placeholder": "请输入华为云 ELB 监听器 ID", "workflow_node.deploy.form.huaweicloud_elb_listener_id.tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/vpc/#/elb/list/grid\" target=\"_blank\">https://console.huaweicloud.com/vpc/#/elb/list/grid</a>", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.label": "证书替换方式", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.option.certificate.label": "替换指定证书", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.option.cloudserver.label": "替换指定云模式防护网站的证书", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.option.premiumhost.label": "替换指定独享模式防护网站的证书", + "workflow_node.deploy.form.huaweicloud_waf_region.label": "华为云 WAF 服务区域", + "workflow_node.deploy.form.huaweicloud_waf_region.placeholder": "请输入华为云 WAF 服务区域(例如:cn-north-1)", + "workflow_node.deploy.form.huaweicloud_waf_region.tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/apiexplorer/#/endpoint\" target=\"_blank\">https://console.huaweicloud.com/apiexplorer/#/endpoint</a>", + "workflow_node.deploy.form.huaweicloud_waf_certificate_id.label": "华为云 WAF 证书 ID", + "workflow_node.deploy.form.huaweicloud_waf_certificate_id.placeholder": "请输入华为云 WAF 证书 ID", + "workflow_node.deploy.form.huaweicloud_waf_certificate_id.tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/console/#/waf/certificateManagement\" target=\"_blank\">https://console.huaweicloud.com/console/#/waf/certificateManagement</a>", + "workflow_node.deploy.form.huaweicloud_waf_domain.label": "华为云 WAF 防护域名", + "workflow_node.deploy.form.huaweicloud_waf_domain.placeholder": "请输入华为云 WAF 防护域名(支持泛域名)", + "workflow_node.deploy.form.huaweicloud_waf_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/console/#/waf/domain/list\" target=\"_blank\">https://console.huaweicloud.com/console/#/waf/domain/list</a>", + "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "证书替换方式", + "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.jdcloud_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/TLS 监听的证书", + "workflow_node.deploy.form.jdcloud_alb_resource_type.option.listener.label": "替换指定负载均衡监听器的证书", + "workflow_node.deploy.form.jdcloud_alb_region_id.label": "京东云 ALB 服务地域 ID", + "workflow_node.deploy.form.jdcloud_alb_region_id.placeholder": "请输入京东云 ALB 服务地域 ID(例如:cn-north-1", + "workflow_node.deploy.form.jdcloud_alb_region_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.jdcloud.com/cn/common-declaration/api/introduction\" target=\"_blank\">https://docs.jdcloud.com/cn/common-declaration/api/introduction</a>", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.label": "京东云 ALB 负载均衡器 ID", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder": "请输入京东云 ALB 负载均衡器 ID", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.tooltip": "这是什么?请参阅 <a href=\"https://cns-console.jdcloud.com/host/loadBalance/list\" target=\"_blank\">https://cns-console.jdcloud.com/host/loadBalance/list</a>", + "workflow_node.deploy.form.jdcloud_alb_listener_id.label": "京东云 ALB 监听器 ID", + "workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder": "请输入京东云 ALB 监听器 ID", + "workflow_node.deploy.form.jdcloud_alb_listener_id.tooltip": "这是什么?请参阅 <a href=\"https://cns-console.jdcloud.com/host/loadBalance/list\" target=\"_blank\">https://cns-console.jdcloud.com/host/loadBalance/list</a>", + "workflow_node.deploy.form.jdcloud_alb_snidomain.label": "京东云 ALB 扩展域名(可选)", + "workflow_node.deploy.form.jdcloud_alb_snidomain.placeholder": "请输入京东云 ALB 扩展域名(支持泛域名)", + "workflow_node.deploy.form.jdcloud_alb_snidomain.tooltip": "这是什么?请参阅 <a href=\"https://cns-console.jdcloud.com/host/loadBalance/list\" target=\"_blank\">https://cns-console.jdcloud.com/host/loadBalance/list</a><br><br>不填写时,将替换监听器的默认证书。", + "workflow_node.deploy.form.jdcloud_cdn_domain.label": "京东云 CDN 加速域名", + "workflow_node.deploy.form.jdcloud_cdn_domain.placeholder": "请输入京东云 CDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://cdn-console.jdcloud.com/\" target=\"_blank\">https://cdn-console.jdcloud.com/</a>", + "workflow_node.deploy.form.jdcloud_live_domain.label": "京东云视频直播播放域名", + "workflow_node.deploy.form.jdcloud_live_domain.placeholder": "请输入京东云视频直播播放域名", + "workflow_node.deploy.form.jdcloud_live_domain.tooltip": "这是什么?请参阅 <a href=\"https://live-console.jdcloud.com\" target=\"_blank\">https://live-console.jdcloud.com</a>", + "workflow_node.deploy.form.jdcloud_vod_domain.label": "京东云视频点播加速域名", + "workflow_node.deploy.form.jdcloud_vod_domain.placeholder": "请输入京东云视频点播加速域名", + "workflow_node.deploy.form.jdcloud_vod_domain.tooltip": "这是什么?请参阅 <a href=\"https://vod-console.jdcloud.com/\" target=\"_blank\">https://vod-console.jdcloud.com/</a>", "workflow_node.deploy.form.k8s_namespace.label": "Kubernetes 命名空间", "workflow_node.deploy.form.k8s_namespace.placeholder": "请输入 Kubernetes 命名空间", "workflow_node.deploy.form.k8s_namespace.tooltip": "这是什么?请参阅 <a href=\"https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/namespaces/\" target=\"_blank\">https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/namespaces/</a>", @@ -276,15 +349,20 @@ "workflow_node.deploy.form.local_preset_scripts.option.reload_nginx.label": "POSIX Bash - 重启 nginx 进程", "workflow_node.deploy.form.local_preset_scripts.option.binding_iis.label": "PowerShell - 导入并绑定到 IIS(需管理员权限)", "workflow_node.deploy.form.local_preset_scripts.option.binding_netsh.label": "PowerShell - 导入并绑定到 netsh(需管理员权限)", - "workflow_node.deploy.form.qiniu_cdn_domain.label": "七牛云 CDN 加速域名(支持泛域名)", - "workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "请输入七牛云 CDN 加速域名", - "workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://portal.qiniu.com/cdn\" target=\"_blank\">https://portal.qiniu.com/cdn</a><br><br>泛域名表示形式为:*.example.com", + "workflow_node.deploy.form.qiniu_cdn_domain.label": "七牛云 CDN 加速域名", + "workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "请输入七牛云 CDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://portal.qiniu.com/cdn\" target=\"_blank\">https://portal.qiniu.com/cdn</a>", "workflow_node.deploy.form.qiniu_pili_hub.label": "七牛云视频直播空间名", "workflow_node.deploy.form.qiniu_pili_hub.placeholder": "请输入七牛云视频直播空间名", "workflow_node.deploy.form.qiniu_pili_hub.tooltip": "这是什么?请参阅 <a href=\"https://portal.qiniu.com/hub\" target=\"_blank\">https://portal.qiniu.com/hub</a>", "workflow_node.deploy.form.qiniu_pili_domain.label": "七牛云视频直播流域名", "workflow_node.deploy.form.qiniu_pili_domain.placeholder": "请输入七牛云视频直播流域名", "workflow_node.deploy.form.qiniu_pili_domain.tooltip": "这是什么?请参阅 <a href=\"hhttps://portal.qiniu.com/hub\" target=\"_blank\">https://portal.qiniu.com/hub</a>", + "workflow_node.deploy.form.safeline_resource_type.label": "证书替换方式", + "workflow_node.deploy.form.safeline_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.safeline_resource_type.option.certificate.label": "替换指定证书", + "workflow_node.deploy.form.safeline_certificate_id.label": "雷池证书 ID", + "workflow_node.deploy.form.safeline_certificate_id.placeholder": "请输入雷池证书 ID", "workflow_node.deploy.form.ssh_format.label": "文件格式", "workflow_node.deploy.form.ssh_format.placeholder": "请选择文件格式", "workflow_node.deploy.form.ssh_format.option.pem.label": "PEM 格式(*.pem, *.crt, *.key)", @@ -318,9 +396,9 @@ "workflow_node.deploy.form.ssh_preset_scripts.option.reload_nginx.label": "POSIX Bash - 重启 nginx 进程", "workflow_node.deploy.form.ssh_use_scp.label": "回退使用 SCP", "workflow_node.deploy.form.ssh_use_scp.tooltip": "如果你的远程服务器不支持 SFTP,请开启此选项回退为 SCP。", - "workflow_node.deploy.form.tencentcloud_cdn_domain.label": "腾讯云 CDN 加速域名(支持泛域名)", - "workflow_node.deploy.form.tencentcloud_cdn_domain.placeholder": "请输入腾讯云 CDN 加速域名", - "workflow_node.deploy.form.tencentcloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/cdn\" target=\"_blank\">https://console.cloud.tencent.com/cdn</a><br><br>泛域名表示形式为:*.example.com", + "workflow_node.deploy.form.tencentcloud_cdn_domain.label": "腾讯云 CDN 加速域名", + "workflow_node.deploy.form.tencentcloud_cdn_domain.placeholder": "请输入腾讯云 CDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.tencentcloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/cdn\" target=\"_blank\">https://console.cloud.tencent.com/cdn</a>", "workflow_node.deploy.form.tencentcloud_clb_resource_type.label": "证书替换方式", "workflow_node.deploy.form.tencentcloud_clb_resource_type.placeholder": "请选择证书替换方式", "workflow_node.deploy.form.tencentcloud_clb_resource_type.option.ssl_deploy.label": "通过 SSL 服务部署到云资源实例", @@ -353,10 +431,10 @@ "workflow_node.deploy.form.tencentcloud_cos_domain.tooltip": "这是什么?请参阅 see <a href=\"https://console.cloud.tencent.com/cos\" target=\"_blank\">https://console.cloud.tencent.com/cos</a>", "workflow_node.deploy.form.tencentcloud_css_domain.label": "腾讯云云直播播放域名", "workflow_node.deploy.form.tencentcloud_css_domain.placeholder": "请输入腾讯云云直播播放域名", - "workflow_node.deploy.form.tencentcloud_css_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/live/livestat\" target=\"_blank\">https://console.cloud.tencent.com/live/livestat</a>", - "workflow_node.deploy.form.tencentcloud_ecdn_domain.label": "腾讯云 ECDN 加速域名(支持泛域名)", - "workflow_node.deploy.form.tencentcloud_ecdn_domain.placeholder": "请输入腾讯云 ECDN 加速域名", - "workflow_node.deploy.form.tencentcloud_ecdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/cdn\" target=\"_blank\">https://console.cloud.tencent.com/cdn</a><br><br>泛域名表示形式为:*.example.com", + "workflow_node.deploy.form.tencentcloud_css_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/live\" target=\"_blank\">https://console.cloud.tencent.com/live</a>", + "workflow_node.deploy.form.tencentcloud_ecdn_domain.label": "腾讯云 ECDN 加速域名", + "workflow_node.deploy.form.tencentcloud_ecdn_domain.placeholder": "请输入腾讯云 ECDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.tencentcloud_ecdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/cdn\" target=\"_blank\">https://console.cloud.tencent.com/cdn</a>", "workflow_node.deploy.form.tencentcloud_eo_zone_id.label": "腾讯云 EdgeOne 站点 ID", "workflow_node.deploy.form.tencentcloud_eo_zone_id.placeholder": "请输入腾讯云 EdgeOne 站点 ID", "workflow_node.deploy.form.tencentcloud_eo_zone_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/edgeone\" target=\"_blank\">https://console.cloud.tencent.com/edgeone</a>", @@ -376,6 +454,24 @@ "workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com.cn/document/product/400/91667\" target=\"_blank\">https://cloud.tencent.com.cn/document/product/400/91667</a><br><br>注意与各产品本身的实例 ID 区分。", "workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.multiple_input_modal.title": "修改腾讯云云产品资源 ID", "workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.multiple_input_modal.placeholder": "请输入腾讯云云产品资源 ID", + "workflow_node.deploy.form.tencentcloud_vod_sub_app_id.label": "腾讯云云点播应用 ID", + "workflow_node.deploy.form.tencentcloud_vod_sub_app_id.placeholder": "请输入腾讯云云点播应用 ID", + "workflow_node.deploy.form.tencentcloud_vod_sub_app_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/vod\" target=\"_blank\">https://console.cloud.tencent.com/vod</a>", + "workflow_node.deploy.form.tencentcloud_vod_domain.label": "腾讯云云点播加速域名", + "workflow_node.deploy.form.tencentcloud_vod_domain.placeholder": "请输入腾讯云云点播加速域名", + "workflow_node.deploy.form.tencentcloud_vod_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/vod\" target=\"_blank\">https://console.cloud.tencent.com/vod</a>", + "workflow_node.deploy.form.tencentcloud_waf_region.label": "腾讯云 WAF 产品地域", + "workflow_node.deploy.form.tencentcloud_waf_region.placeholder": "请输入腾讯云 WAF 产品地域(例如:ap-guangzhou)", + "workflow_node.deploy.form.tencentcloud_waf_region.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/627/47525\" target=\"_blank\">https://cloud.tencent.com/document/product/627/47525</a>", + "workflow_node.deploy.form.tencentcloud_waf_domain.label": "腾讯云 WAF 防护域名", + "workflow_node.deploy.form.tencentcloud_waf_domain.placeholder": "请输入腾讯云 WAF 防护域名", + "workflow_node.deploy.form.tencentcloud_waf_domain.tooltip": "这是什么?请参阅 see <a href=\"https://console.cloud.tencent.com/waf\" target=\"_blank\">https://console.cloud.tencent.com/waf</a>", + "workflow_node.deploy.form.tencentcloud_waf_domain_id.label": "腾讯云 WAF 域名 ID", + "workflow_node.deploy.form.tencentcloud_waf_domain_id.placeholder": "请输入腾讯云 WAF 域名 ID", + "workflow_node.deploy.form.tencentcloud_waf_domain_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/waf\" target=\"_blank\">https://console.cloud.tencent.com/waf</a>", + "workflow_node.deploy.form.tencentcloud_waf_instance_id.label": "腾讯云 WAF 实例 ID", + "workflow_node.deploy.form.tencentcloud_waf_instance_id.placeholder": "请输入腾讯云 WAF 实例 ID", + "workflow_node.deploy.form.tencentcloud_waf_instance_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/waf\" target=\"_blank\">https://console.cloud.tencent.com/waf</a>", "workflow_node.deploy.form.ucloud_ucdn_domain_id.label": "优刻得 UCDN 域名 ID", "workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder": "请输入优刻得 UCDN 域名 ID", "workflow_node.deploy.form.ucloud_ucdn_domain_id.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/ucdn\" target=\"_blank\">https://console.ucloud.cn/ucdn</a>", @@ -388,9 +484,9 @@ "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": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/ufile\" target=\"_blank\">https://console.ucloud.cn/ufile</a>", - "workflow_node.deploy.form.volcengine_cdn_domain.label": "火山引擎 CDN 加速域名(支持泛域名)", - "workflow_node.deploy.form.volcengine_cdn_domain.placeholder": "请输入火山引擎 CDN 加速域名", - "workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/cdn/homepage\" target=\"_blank\">https://console.volcengine.com/cdn/homepage</a><br><br>泛域名表示形式为:*.example.com", + "workflow_node.deploy.form.volcengine_cdn_domain.label": "火山引擎 CDN 加速域名", + "workflow_node.deploy.form.volcengine_cdn_domain.placeholder": "请输入火山引擎 CDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/cdn/homepage\" target=\"_blank\">https://console.volcengine.com/cdn/homepage</a>", "workflow_node.deploy.form.volcengine_clb_resource_type.label": "证书替换方式", "workflow_node.deploy.form.volcengine_clb_resource_type.placeholder": "请选择证书替换方式", "workflow_node.deploy.form.volcengine_clb_resource_type.option.listener.label": "替换指定监听器的证书", @@ -400,14 +496,23 @@ "workflow_node.deploy.form.volcengine_clb_listener_id.label": "火山引擎 CLB 监听器 ID", "workflow_node.deploy.form.volcengine_clb_listener_id.placeholder": "请输入火山引擎 CLB 监听器 ID", "workflow_node.deploy.form.volcengine_clb_listener_id.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/clb/LoadBalancer\" target=\"_blank\">https://console.volcengine.com/clb/LoadBalancer</a>", - "workflow_node.deploy.form.volcengine_dcdn_domain.label": "火山引擎 DCDN 加速域名(支持泛域名)", - "workflow_node.deploy.form.volcengine_dcdn_domain.placeholder": "请输入火山引擎 DCDN 加速域名", - "workflow_node.deploy.form.volcengine_dcdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/dcdn/dashboard\" target=\"_blank\">https://console.volcengine.com/dcdn/dashboard</a><br><br>泛域名表示形式为:*.example.com", - "workflow_node.deploy.form.volcengine_live_domain.label": "火山引擎视频直播流域名(支持泛域名)", - "workflow_node.deploy.form.volcengine_live_domain.placeholder": "请输入火山引擎视频直播流域名", - "workflow_node.deploy.form.volcengine_live_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/live\" target=\"_blank\">https://console.volcengine.com/live</a><br><br>泛域名表示形式为:*.example.com", + "workflow_node.deploy.form.volcengine_dcdn_domain.label": "火山引擎 DCDN 加速域名", + "workflow_node.deploy.form.volcengine_dcdn_domain.placeholder": "请输入火山引擎 DCDN 加速域名(支持泛域名)", + "workflow_node.deploy.form.volcengine_dcdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/dcdn/dashboard\" target=\"_blank\">https://console.volcengine.com/dcdn/dashboard</a>", + "workflow_node.deploy.form.volcengine_imagex_region.label": "火山引擎 ImageX 服务地域", + "workflow_node.deploy.form.volcengine_imagex_region.placeholder": "请输入火山引擎 ImageX 服务地域(例如:cn-north-1)", + "workflow_node.deploy.form.volcengine_imagex_region.tooltip": "这是什么?请参阅 <a href=\"https://www.volcengine.com/docs/508/23757\" target=\"_blank\">https://www.volcengine.com/docs/508/23757</a>", + "workflow_node.deploy.form.volcengine_imagex_service_id.label": "火山引擎 TOS 服务 ID", + "workflow_node.deploy.form.volcengine_imagex_service_id.placeholder": "请输入火山引擎 TOS 服务 ID", + "workflow_node.deploy.form.volcengine_imagex_service_id.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/imagex\" target=\"_blank\">https://console.volcengine.com/imagex</a>", + "workflow_node.deploy.form.volcengine_imagex_domain.label": "火山引擎 ImageX 绑定域名", + "workflow_node.deploy.form.volcengine_imagex_domain.placeholder": "请输入火山引擎 ImageX 绑定域名", + "workflow_node.deploy.form.volcengine_imagex_domain.tooltip": "这是什么?请参阅 see <a href=\"https://console.volcengine.com/imagex\" target=\"_blank\">https://console.volcengine.com/imagex</a>", + "workflow_node.deploy.form.volcengine_live_domain.label": "火山引擎视频直播流域名", + "workflow_node.deploy.form.volcengine_live_domain.placeholder": "请输入火山引擎视频直播流域名(支持泛域名)", + "workflow_node.deploy.form.volcengine_live_domain.tooltip": "这是什么?请参阅 <a href=\"https://console.volcengine.com/live\" target=\"_blank\">https://console.volcengine.com/live</a>", "workflow_node.deploy.form.volcengine_tos_region.label": "火山引擎 TOS 服务地域", - "workflow_node.deploy.form.volcengine_tos_region.placeholder": "请输入火山引擎 TOS 服务地域(例如:cn-beijing", + "workflow_node.deploy.form.volcengine_tos_region.placeholder": "请输入火山引擎 TOS 服务地域(例如:cn-beijing)", "workflow_node.deploy.form.volcengine_tos_region.tooltip": "这是什么?请参阅 <a href=\"https://www.volcengine.com/docs/6349/107356\" target=\"_blank\">https://www.volcengine.com/docs/6349/107356</a>", "workflow_node.deploy.form.volcengine_tos_bucket.label": "火山引擎 TOS 存储桶名", "workflow_node.deploy.form.volcengine_tos_bucket.placeholder": "请输入火山引擎 TOS 存储桶名", diff --git a/ui/src/vite-env.d.ts b/ui/types/vite-env.d.ts similarity index 100% rename from ui/src/vite-env.d.ts rename to ui/types/vite-env.d.ts