From 542262dfefdbdb28aeac082749ef98aae9cac23c Mon Sep 17 00:00:00 2001 From: v-me-50 Date: Thu, 10 Jul 2025 17:32:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=83=A8=E7=BD=B2=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E4=BA=91dcdn=E3=80=81lecdn=20=E6=96=B0=E5=A2=9Edns?= =?UTF-8?q?=E5=8E=82=E5=95=86constellix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/cert/apply/apply.go | 15 +- backend/internal/cert/deploy/aliyun/dcdn.go | 92 ++++++ backend/internal/cert/deploy/deploy.go | 3 + backend/internal/cert/deploy/lecdn/lecdn.go | 310 ++++++++++++++++++++ backend/migrations/init.go | 2 + go.mod | 3 + go.sum | 15 + 7 files changed, 438 insertions(+), 2 deletions(-) create mode 100644 backend/internal/cert/deploy/aliyun/dcdn.go create mode 100644 backend/internal/cert/deploy/lecdn/lecdn.go diff --git a/backend/internal/cert/apply/apply.go b/backend/internal/cert/apply/apply.go index c95300f..86e117f 100644 --- a/backend/internal/cert/apply/apply.go +++ b/backend/internal/cert/apply/apply.go @@ -20,6 +20,7 @@ import ( "github.com/go-acme/lego/v4/providers/dns/bunny" "github.com/go-acme/lego/v4/providers/dns/cloudflare" "github.com/go-acme/lego/v4/providers/dns/cloudns" + "github.com/go-acme/lego/v4/providers/dns/constellix" "github.com/go-acme/lego/v4/providers/dns/gcore" "github.com/go-acme/lego/v4/providers/dns/godaddy" "github.com/go-acme/lego/v4/providers/dns/huaweicloud" @@ -78,8 +79,12 @@ func GetDNSProvider(providerName string, creds map[string]string, httpClient *ht return tencentcloud.NewDNSProviderConfig(config) case "cloudflare": config := cloudflare.NewDefaultConfig() - config.AuthEmail = creds["email"] - config.AuthKey = creds["api_key"] + if creds["email"] == "" { + config.AuthToken = creds["api_key"] + } else { + config.AuthEmail = creds["email"] + config.AuthKey = creds["api_key"] + } config.PropagationTimeout = maxWait return cloudflare.NewDNSProviderConfig(config) case "aliyun": @@ -191,6 +196,12 @@ func GetDNSProvider(providerName string, creds map[string]string, httpClient *ht config.RegionId = "cn-north-1" config.PropagationTimeout = maxWait return jdcloud.NewDNSProviderConfig(config) + case "constellix": + config := constellix.NewDefaultConfig() + config.APIKey = creds["api_key"] + config.SecretKey = creds["secret_key"] + config.PropagationTimeout = maxWait + return constellix.NewDNSProviderConfig(config) default: return nil, fmt.Errorf("不支持的 DNS Provider: %s", providerName) diff --git a/backend/internal/cert/deploy/aliyun/dcdn.go b/backend/internal/cert/deploy/aliyun/dcdn.go new file mode 100644 index 0000000..43bb9db --- /dev/null +++ b/backend/internal/cert/deploy/aliyun/dcdn.go @@ -0,0 +1,92 @@ +package aliyun + +import ( + "ALLinSSL/backend/internal/access" + "encoding/json" + "fmt" + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + dcdn "github.com/alibabacloud-go/dcdn-20180115/v3/client" + util "github.com/alibabacloud-go/tea-utils/v2/service" + "github.com/alibabacloud-go/tea/tea" + "strconv" +) + +func CreateDcdnClient(accessKey, accessSecret string) (*dcdn.Client, error) { + config := &openapi.Config{ + AccessKeyId: tea.String(accessKey), + AccessKeySecret: tea.String(accessSecret), + RegionId: tea.String("cn-hangzhou"), + } + return dcdn.NewClient(config) +} + +func DeployCertToDcdn(client *dcdn.Client, domain, certPEM, privkeyPEM string) error { + request := &dcdn.SetDcdnDomainSSLCertificateRequest{ + DomainName: tea.String(domain), + SSLPri: tea.String(privkeyPEM), + SSLPub: tea.String(certPEM), + SSLProtocol: tea.String("on"), + CertType: tea.String("upload"), + } + + runtime := &util.RuntimeOptions{} + + _, err := client.SetDcdnDomainSSLCertificateWithOptions(request, runtime) + if err != nil { + return err + } + return nil + +} + +func DeployAliyunDcdn(cfg map[string]any) error { + cert, ok := cfg["certificate"].(map[string]any) + if !ok { + return fmt.Errorf("证书不存在") + } + var providerID string + switch v := cfg["provider_id"].(type) { + case float64: + providerID = strconv.Itoa(int(v)) + case string: + providerID = v + default: + return fmt.Errorf("参数错误:provider_id") + } + // + providerData, err := access.GetAccess(providerID) + if err != nil { + return err + } + providerConfigStr, ok := providerData["config"].(string) + if !ok { + return fmt.Errorf("api配置错误") + } + // 解析 JSON 配置 + var providerConfig map[string]string + err = json.Unmarshal([]byte(providerConfigStr), &providerConfig) + if err != nil { + return err + } + client, err := CreateDcdnClient(providerConfig["access_key_id"], providerConfig["access_key_secret"]) + if err != nil { + return fmt.Errorf("创建 DCDN 客户端失败: %w", err) + } + certPEM, ok := cert["cert"].(string) + if !ok { + return fmt.Errorf("证书内容不存在或格式错误") + } + privkeyPEM, ok := cert["key"].(string) + if !ok { + return fmt.Errorf("私钥内容不存在或格式错误") + } + domain, ok := cfg["domain"].(string) + if !ok { + return fmt.Errorf("域名不存在或格式错误") + } + err = DeployCertToDcdn(client, domain, certPEM, privkeyPEM) + if err != nil { + return fmt.Errorf("部署证书到 DCDN 失败: %w", err) + } + return nil +} diff --git a/backend/internal/cert/deploy/deploy.go b/backend/internal/cert/deploy/deploy.go index 15ab426..1348053 100644 --- a/backend/internal/cert/deploy/deploy.go +++ b/backend/internal/cert/deploy/deploy.go @@ -66,6 +66,9 @@ func Deploy(cfg map[string]any, logger *public.Logger) error { case "aliyun-esa": logger.Debug("部署到阿里云ESA...") return aliyun.DeployAliyunESA(cfg) + case "aliyun-dcdn": + logger.Debug("部署到阿里云DCDN...") + return aliyun.DeployAliyunDcdn(cfg) case "safeline-site": logger.Debug("部署雷池WAF网站...") return DeploySafeLineWafSite(cfg, logger) diff --git a/backend/internal/cert/deploy/lecdn/lecdn.go b/backend/internal/cert/deploy/lecdn/lecdn.go new file mode 100644 index 0000000..729159a --- /dev/null +++ b/backend/internal/cert/deploy/lecdn/lecdn.go @@ -0,0 +1,310 @@ +package lecdn + +import ( + "ALLinSSL/backend/internal/access" + "ALLinSSL/backend/public" + "bytes" + "crypto/tls" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" +) + +// LecdnConfig 代表 LeCDN 的配置 +type LecdnConfig struct { + BaseURL string `json:"baseurl"` + Token string `json:"token"` + Username string `json:"username"` + Password string `json:"password"` + IgnoreSSL bool `json:"ignore_ssl"` +} + +// NewLecdnConfig 创建一个新的 LecdnConfig 实例 +func NewLecdnConfig(providerID string) (*LecdnConfig, error) { + providerData, err := access.GetAccess(providerID) + if err != nil { + return nil, err + } + providerConfigStr, ok := providerData["config"].(string) + if !ok { + return nil, fmt.Errorf("api配置错误") + } + // 解析 JSON 配置 + var providerConfig map[string]string + err = json.Unmarshal([]byte(providerConfigStr), &providerConfig) + if err != nil { + return nil, err + } + l := &LecdnConfig{ + BaseURL: providerConfig["url"], + Username: providerConfig["username"], + Password: providerConfig["password"], + IgnoreSSL: providerConfig["ignore_ssl"] == "1", + } + return l, nil +} + +// requestLecdn 发送 HTTP 请求到 LeCDN API +func requestLecdn(url, method, token string, params map[string]any, ignoreSsl bool) (map[string]any, error) { + var res map[string]any + + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl}, + }, + } + + jsonData, _ := json.Marshal(params) + req, err := http.NewRequest(method, url, bytes.NewBuffer(jsonData)) + if err != nil { + return res, err + } + req.Header.Set("Content-Type", "application/json") + if token != "" { + req.Header.Set("Authorization", "Bearer "+token) + req.Header.Set("cookie", "LeCDN-Client="+token) + } + + resp, err := client.Do(req) + if err != nil { + return res, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return res, fmt.Errorf("请求失败,状态码:%d", resp.StatusCode) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + err = json.Unmarshal(body, &res) + if err != nil { + return res, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body)) + } + + return res, nil +} + +// client 登录 LeCDN 并获取 Token +func (l *LecdnConfig) client() error { + url := fmt.Sprintf("%s/prod-api/login", l.BaseURL) + resp, err := requestLecdn(url, "POST", "", map[string]any{ + "username": l.Username, + "password": l.Password, + }, l.IgnoreSSL) + if err != nil { + return fmt.Errorf("登录失败: %v", err) + } + data, ok := resp["data"].(map[string]any) + if !ok { + return fmt.Errorf("登录响应格式错误: %v", resp) + } + token, ok := data["token"].(string) + if !ok { + return fmt.Errorf("登录响应中未找到 token: %v", resp) + } + l.Token = token + return nil +} + +// GetCertList 获取证书列表 +func (l *LecdnConfig) GetCertList() ([]map[string]any, error) { + url := fmt.Sprintf("%s/prod-api/certificate?current_page=1&total=9999&page_size=9999", l.BaseURL) + resp, err := requestLecdn(url, "GET", l.Token, nil, l.IgnoreSSL) + if err != nil { + return nil, fmt.Errorf("获取证书列表失败: %v", err) + } + data, ok := resp["data"].(map[string]any) + if !ok { + return nil, fmt.Errorf("获取证书列表响应格式错误: %v", resp) + } + list, ok := data["items"].([]any) + if !ok { + return nil, fmt.Errorf("获取证书列表响应中未找到 items: %v", resp) + } + if len(list) == 0 { + return nil, fmt.Errorf("未找到任何证书") + } + var certs []map[string]any + for _, item := range list { + cert, ok := item.(map[string]any) + if !ok { + return nil, fmt.Errorf("证书列表项格式错误: %v", item) + } + certs = append(certs, cert) + } + return certs, nil +} + +// UploadCert 上传证书到 LeCDN +func (l *LecdnConfig) UploadCert(cert, key, name string) (int, error) { + url := fmt.Sprintf("%s/prod-api/certificate", l.BaseURL) + keyBase64 := base64.StdEncoding.EncodeToString([]byte(key)) + certBase64 := base64.StdEncoding.EncodeToString([]byte(cert)) + + params := map[string]any{ + "ssl_key": keyBase64, + "ssl_pem": certBase64, + "type": "upload", + "auto_renewal": false, + "name": name, + } + resp, err := requestLecdn(url, "POST", l.Token, params, l.IgnoreSSL) + if err != nil { + return 0, fmt.Errorf("上传证书失败: %v", err) + } + data, ok := resp["data"].(map[string]any) + if !ok { + return 0, fmt.Errorf("上传证书响应格式错误: %v", resp) + } + id, ok := data["id"].(float64) + if !ok { + return 0, fmt.Errorf("上传证书响应中未找到 id: %v", resp) + } + return int(id), nil +} + +// GetDomainIdMapFromSite 获取指定站点的域名列表,并返回域名到 ID 的映射 +func (l *LecdnConfig) GetDomainIdMapFromSite(siteId int) (map[string]int, error) { + url := fmt.Sprintf("%s/prod-api/site/%d/domain_name", l.BaseURL, siteId) + resp, err := requestLecdn(url, "GET", l.Token, nil, l.IgnoreSSL) + if err != nil { + return nil, fmt.Errorf("获取域名列表失败: %v", err) + } + data, ok := resp["data"].([]any) + if !ok { + return nil, fmt.Errorf("获取域名列表响应格式错误: %v", resp) + } + domains := make(map[string]int) + for _, domain := range data { + domainData, ok := domain.(map[string]any) + if !ok { + return nil, fmt.Errorf("域名列表项格式错误: %v", domain) + } + domainName, ok := domainData["domain_name"].(string) + if !ok { + return nil, fmt.Errorf("域名列表项中未找到 domain_name: %v", domainData) + } + id, ok := domainData["id"].(float64) + if !ok { + return nil, fmt.Errorf("域名列表项中未找到 id: %v", domainData) + } + domains[domainName] = int(id) + } + return domains, nil +} + +// DeployCert 部署证书到指定站点的域名 +func (l *LecdnConfig) DeployCert(siteId, domainId, certId int) error { + url := fmt.Sprintf("%s/prod-api/site/%d/domain_name/certificate", l.BaseURL, siteId) + params := map[string]any{"certificate_enable": true, "certificate_id": certId, "domain_name_id": domainId} + _, err := requestLecdn(url, "PUT", l.Token, params, l.IgnoreSSL) + if err != nil { + return fmt.Errorf("部署证书失败: %v", err) + } + return nil +} + +// DeployLeCDN 部署 LeCDN 证书主方法 +func DeployLeCDN(cfg map[string]any) error { + cert, ok := cfg["certificate"].(map[string]any) + if !ok { + return fmt.Errorf("证书不存在") + } + certPem, ok := cert["cert"].(string) + if !ok { + return fmt.Errorf("证书错误:cert") + } + keyPem, ok := cert["key"].(string) + if !ok { + return fmt.Errorf("证书错误:key") + } + certName, err := public.GetSHA256(certPem) + if err != nil { + return fmt.Errorf("获取证书名称失败: %v", err) + } + certName = "ALLinSSL-" + certName + var providerID string + switch v := cfg["provider_id"].(type) { + case float64: + providerID = strconv.Itoa(int(v)) + case string: + providerID = v + default: + return fmt.Errorf("参数错误:provider_id") + } + domainName, ok := cfg["domain_name"].(string) + if !ok || domainName == "" { + return fmt.Errorf("参数错误:domain_name") + } + var siteId int + switch v := cfg["site_id"].(type) { + case float64: + siteId = int(v) + case string: + siteId, err = strconv.Atoi(v) + if err != nil { + return fmt.Errorf("参数错误:site_id") + } + case int: + siteId = v + default: + return fmt.Errorf("参数错误:site_id") + } + + l, err := NewLecdnConfig(providerID) + if err != nil { + return err + } + err = l.client() + if err != nil { + return fmt.Errorf("登录 LeCDN 失败: %v", err) + } + + certId := 0 + // 获取证书列表 + certList, err := l.GetCertList() + if err == nil && len(certList) > 0 { + for _, c := range certList { + name, ok := c["name"].(string) + if !ok { + continue + } + id, ok := c["id"].(float64) + if !ok { + continue + } + if name == certName { + certId = int(id) + } + } + } + if certId == 0 { + // 上传证书 + certId, err = l.UploadCert(certPem, keyPem, certName) + if err != nil { + return fmt.Errorf("上传证书失败: %v", err) + } + } + // 获取域名列表 + domainList, err := l.GetDomainIdMapFromSite(siteId) + if err != nil { + return fmt.Errorf("获取域名列表失败: %v", err) + } + domainId, ok := domainList[domainName] + if !ok { + return fmt.Errorf("未找到域名 %s 的 ID", domainName) + } + // 部署证书到域名 + err = l.DeployCert(siteId, domainId, certId) + if err != nil { + return fmt.Errorf("部署证书到域名失败: %v", err) + } + // 部署完成,返回成功 + return nil +} diff --git a/backend/migrations/init.go b/backend/migrations/init.go index 53d62fa..ecd57ef 100644 --- a/backend/migrations/init.go +++ b/backend/migrations/init.go @@ -182,6 +182,8 @@ func init() { InsertIfNotExists(db, "access_type", map[string]any{"name": "bunny", "type": "dns"}, []string{"name", "type"}, []any{"bunny", "dns"}) InsertIfNotExists(db, "access_type", map[string]any{"name": "namedotcom", "type": "dns"}, []string{"name", "type"}, []any{"namedotcom", "dns"}) InsertIfNotExists(db, "access_type", map[string]any{"name": "namesilo", "type": "dns"}, []string{"name", "type"}, []any{"namesilo", "dns"}) + InsertIfNotExists(db, "access_type", map[string]any{"name": "constellix", "type": "dns"}, []string{"name", "type"}, []any{"constellix", "dns"}) + InsertIfNotExists(db, "access_type", map[string]any{"name": "lecdn", "type": "host"}, []string{"name", "type"}, []any{"lecdn", "host"}) err = sqlite_migrate.EnsureDatabaseWithTables( "data/site_monitor.db", diff --git a/go.mod b/go.mod index 241af38..fb75673 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/alibabacloud-go/cas-20200407/v4 v4.0.0 github.com/alibabacloud-go/cdn-20180510/v6 v6.0.0 github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7 + github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0 github.com/alibabacloud-go/esa-20240910/v2 v2.34.0 github.com/alibabacloud-go/market-20151101/v4 v4.1.0 github.com/alibabacloud-go/openapi-util v0.1.1 @@ -93,6 +94,8 @@ require ( github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/sessions v1.4.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect diff --git a/go.sum b/go.sum index 7f2d6b6..09d66ad 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,8 @@ github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDN github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ= github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo= github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= +github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0 h1:EQmKhYju6y38kJ1ZvZROeJG2Q1Wk6hlc8KQrVhvGyaw= +github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0/go.mod h1:b9qzvr/2V1f0r1Z6xUmkLqEouKcPGy4LCC22yV+6HQo= github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= @@ -123,6 +125,7 @@ github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeG github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.10/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.12/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= @@ -132,11 +135,13 @@ github.com/alibabacloud-go/tea v1.3.9 h1:bjgt1bvdY780vz/17iWNNtbXl4A77HWntWMeaUF github.com/alibabacloud-go/tea v1.3.9/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= github.com/alibabacloud-go/tea-fileform v1.1.1 h1:1YG6erAP3joQ0XdCXYIotuD7zyOM6qCR49xkp5FZDeU= github.com/alibabacloud-go/tea-fileform v1.1.1/go.mod h1:ZeCV91o4ISmxidd686f0ebdS5EDHWU+vW+TkjLhrsFE= +github.com/alibabacloud-go/tea-oss-sdk v1.1.3/go.mod h1:yUnodpR3Bf2rudLE7V/Gft5txjJF30Pk+hH77K/Eab0= github.com/alibabacloud-go/tea-oss-sdk v1.1.5 h1:CFUFcqanvBaoGN/CyTHUZrVNtFZd1WTjem46m0HTTV0= github.com/alibabacloud-go/tea-oss-sdk v1.1.5/go.mod h1:5fhlKMa/kWRJNgPYRt+5qSg3UidRvNbf9Z2bI8Dp5/s= 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-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= github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4= @@ -144,6 +149,7 @@ github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eU github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0= github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= +github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= 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= @@ -270,6 +276,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= @@ -431,15 +439,20 @@ github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOj github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -526,6 +539,8 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=