From e264d7104893235b58b4ded52c9532b7d16f000c Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 15 Jan 2025 14:24:48 +0800 Subject: [PATCH 1/5] refactor: slices utils --- internal/applicant/applicant.go | 5 ++- internal/pkg/utils/slices/slices.go | 69 +++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 internal/pkg/utils/slices/slices.go diff --git a/internal/applicant/applicant.go b/internal/applicant/applicant.go index 51512416..218b42fc 100644 --- a/internal/applicant/applicant.go +++ b/internal/applicant/applicant.go @@ -15,6 +15,7 @@ import ( "github.com/go-acme/lego/v4/lego" "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/pkg/utils/slices" "github.com/usual2970/certimate/internal/repository" ) @@ -61,13 +62,13 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) { } options := &applicantOptions{ - Domains: strings.Split(node.GetConfigString("domains"), ";"), + Domains: slices.Filter(strings.Split(node.GetConfigString("domains"), ";"), func(s string) bool { return s != "" }), ContactEmail: node.GetConfigString("contactEmail"), Provider: domain.ApplyDNSProviderType(node.GetConfigString("provider")), ProviderAccessConfig: accessConfig, ProviderApplyConfig: node.GetConfigMap("providerConfig"), KeyAlgorithm: node.GetConfigString("keyAlgorithm"), - Nameservers: strings.Split(node.GetConfigString("nameservers"), ";"), + Nameservers: slices.Filter(strings.Split(node.GetConfigString("nameservers"), ";"), func(s string) bool { return s != "" }), PropagationTimeout: node.GetConfigInt32("propagationTimeout"), DisableFollowCNAME: node.GetConfigBool("disableFollowCNAME"), } diff --git a/internal/pkg/utils/slices/slices.go b/internal/pkg/utils/slices/slices.go new file mode 100644 index 00000000..e4e4d746 --- /dev/null +++ b/internal/pkg/utils/slices/slices.go @@ -0,0 +1,69 @@ +package slices + +// 创建给定切片一部分的浅拷贝,其包含通过所提供函数实现的测试的所有元素。 +// +// 入参: +// - slice: 原切片。 +// - filter: 为切片中的每个元素执行的函数。它应该返回一个布尔值以指使是否将元素保留在结果切片中。 +// +// 出参: +// - 新切片。 +func Filter[T any](slice []T, filter func(T) bool) []T { + var result []T + for _, item := range slice { + if filter(item) { + result = append(result, item) + } + } + return result +} + +// 创建一个新切片,这个新切片由原切片中的每个元素都调用一次提供的函数后的返回值组成。 +// +// 入参: +// - slice: 原切片。 +// - mapper: 为切片中的每个元素执行的函数。它的返回值作为一个元素被添加为新切片中。 +// +// 出参: +// - 新切片。 +func Map[T1 any, T2 any](slice []T1, mapper func(T1) T2) []T2 { + result := make([]T2, 0, len(slice)) + for _, item := range slice { + result = append(result, mapper(item)) + } + return result +} + +// 测试切片中是否每个元素都通过了由提供的函数实现的测试。 +// +// 入参: +// - slice: 切片。 +// - condition: 为切片中的每个元素执行的函数。它应该返回一个布尔值以指示元素是否通过测试。 +// +// 出参: +// - 测试结果。 +func Every[T any](slice []T, condition func(T) bool) bool { + for _, item := range slice { + if !condition(item) { + return false + } + } + return true +} + +// 测试切片中是否至少有一个元素通过了由提供的函数实现的测试。 +// +// 入参: +// - slice: 切片。 +// - condition: 为切片中的每个元素执行的函数。它应该返回一个布尔值以指示元素是否通过测试。 +// +// 出参: +// - 测试结果。 +func Some[T any](slice []T, condition func(T) bool) bool { + for _, item := range slice { + if condition(item) { + return true + } + } + return false +} From dd236b925df33cfa242cf755bf54c70fa7deff7d Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 15 Jan 2025 14:24:51 +0800 Subject: [PATCH 2/5] feat: add ns1 applicant --- README.md | 29 ++++----- README_EN.md | 29 ++++----- go.mod | 1 + go.sum | 2 + internal/applicant/providers.go | 15 +++++ internal/domain/access.go | 4 ++ internal/domain/provider.go | 2 + .../acme-dns-01/lego-providers/ns1/ns1.go | 33 ++++++++++ ui/public/imgs/providers/ns1.svg | 1 + ui/src/components/access/AccessForm.tsx | 3 + .../components/access/AccessFormNS1Config.tsx | 61 +++++++++++++++++++ ui/src/domain/access.ts | 4 ++ ui/src/domain/provider.ts | 4 ++ ui/src/i18n/locales/en/nls.access.json | 3 + ui/src/i18n/locales/en/nls.common.json | 1 + ui/src/i18n/locales/zh/nls.access.json | 15 +++-- ui/src/i18n/locales/zh/nls.common.json | 1 + 17 files changed, 174 insertions(+), 34 deletions(-) create mode 100644 internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1/ns1.go create mode 100644 ui/public/imgs/providers/ns1.svg create mode 100644 ui/src/components/access/AccessFormNS1Config.tsx diff --git a/README.md b/README.md index 3ab77300..ed79b12f 100644 --- a/README.md +++ b/README.md @@ -86,20 +86,21 @@ make local.run [展开查看] -| 提供商 | 备注 | -| :--------------------------------------------- | :-------------------------------------- | -| [阿里云](https://www.aliyun.com/) | | -| [腾讯云](https://cloud.tencent.com/) | | -| [华为云](https://www.huaweicloud.com/) | | -| [火山引擎](https://www.volcengine.com/) | | -| [AWS Route53](https://aws.amazon.com/route53/) | | -| [Azure](https://azure.microsoft.com/) | | -| [CloudFlare](https://www.cloudflare.com/) | | -| [GoDaddy](https://www.godaddy.com/) | | -| [Name.com](https://www.name.com/) | | -| [NameSilo](https://www.namesilo.com/) | | -| [PowerDNS](https://www.powerdns.com/) | | -| ACME 代理 HTTP 请求 | 可申请允许通过 HTTP 请求修改 DNS 的域名 | +| 提供商 | 备注 | +| :----------------------------------------------------------------- | :-------------------------------------- | +| [阿里云](https://www.aliyun.com/) | | +| [腾讯云](https://cloud.tencent.com/) | | +| [华为云](https://www.huaweicloud.com/) | | +| [火山引擎](https://www.volcengine.com/) | | +| [AWS Route53](https://aws.amazon.com/route53/) | | +| [Azure](https://azure.microsoft.com/) | | +| [CloudFlare](https://www.cloudflare.com/) | | +| [GoDaddy](https://www.godaddy.com/) | | +| [Name.com](https://www.name.com/) | | +| [NameSilo](https://www.namesilo.com/) | | +| [IBM NS1 Connect](https://www.ibm.com/cn-zh/products/ns1-connect/) | | +| [PowerDNS](https://www.powerdns.com/) | | +| ACME 代理 HTTP 请求 | 可申请允许通过 HTTP 请求修改 DNS 的域名 | diff --git a/README_EN.md b/README_EN.md index f0f8338e..9c9aab55 100644 --- a/README_EN.md +++ b/README_EN.md @@ -85,20 +85,21 @@ The following DNS providers are supported: [Fold/Unfold to view ...] -| Provider | Remarks | -| :--------------------------------------------- | :------------------------------------ | -| [Alibaba Cloud](https://www.alibabacloud.com/) | | -| [Tencent Cloud](https://www.tencentcloud.com/) | | -| [Huawei Cloud](https://www.huaweicloud.com/) | | -| [Volcengine](https://www.volcengine.com/) | | -| [AWS Route53](https://aws.amazon.com/route53/) | | -| [Azure DNS](https://azure.microsoft.com/) | | -| [CloudFlare](https://www.cloudflare.com/) | | -| [GoDaddy](https://www.godaddy.com/) | | -| [Name.com](https://www.name.com/) | | -| [NameSilo](https://www.namesilo.com/) | | -| [PowerDNS](https://www.powerdns.com/) | | -| ACME Proxy HTTP Request | Supports managing DNS by HTTP request | +| Provider | Remarks | +| :----------------------------------------------------------- | :------------------------------------ | +| [Alibaba Cloud](https://www.alibabacloud.com/) | | +| [Tencent Cloud](https://www.tencentcloud.com/) | | +| [Huawei Cloud](https://www.huaweicloud.com/) | | +| [Volcengine](https://www.volcengine.com/) | | +| [AWS Route53](https://aws.amazon.com/route53/) | | +| [Azure DNS](https://azure.microsoft.com/) | | +| [CloudFlare](https://www.cloudflare.com/) | | +| [GoDaddy](https://www.godaddy.com/) | | +| [Name.com](https://www.name.com/) | | +| [NameSilo](https://www.namesilo.com/) | | +| [IBM NS1 Connect](https://www.ibm.com/products/ns1-connect/) | | +| [PowerDNS](https://www.powerdns.com/) | | +| ACME Proxy HTTP Request | Supports managing DNS by HTTP request | diff --git a/go.mod b/go.mod index 1ff91571..9d52261f 100644 --- a/go.mod +++ b/go.mod @@ -87,6 +87,7 @@ require ( go.mongodb.org/mongo-driver v1.12.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect diff --git a/go.sum b/go.sum index 8e6555c8..d41faffa 100644 --- a/go.sum +++ b/go.sum @@ -1369,6 +1369,8 @@ gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/ns1/ns1-go.v2 v2.13.0 h1:I5NNqI9Bi1SGK92TVkOvLTwux5LNrix/99H2datVh48= +gopkg.in/ns1/ns1-go.v2 v2.13.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index 795afe08..8e42f4f2 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -15,6 +15,7 @@ import ( 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" 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" @@ -167,6 +168,20 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } + case domain.ApplyDNSProviderTypeNS1: + { + access := domain.AccessConfigForNS1{} + if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to decode provider access config: %w", err) + } + + applicant, err := providerNS1.NewChallengeProvider(&providerNS1.NS1ApplicantConfig{ + ApiKey: access.ApiKey, + PropagationTimeout: options.PropagationTimeout, + }) + return applicant, err + } + case domain.ApplyDNSProviderTypePowerDNS: { access := domain.AccessConfigForPowerDNS{} diff --git a/internal/domain/access.go b/internal/domain/access.go index 1893b4ab..dc118482 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -91,6 +91,10 @@ type AccessConfigForNameSilo struct { ApiKey string `json:"apiKey"` } +type AccessConfigForNS1 struct { + ApiKey string `json:"apiKey"` +} + type AccessConfigForPowerDNS struct { ApiUrl string `json:"apiUrl"` ApiKey string `json:"apiKey"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index b90d9b38..dead9c57 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -23,6 +23,7 @@ const ( AccessProviderTypeLocal = AccessProviderType("local") AccessProviderTypeNameDotCom = AccessProviderType("namedotcom") AccessProviderTypeNameSilo = AccessProviderType("namesilo") + AccessProviderTypeNS1 = AccessProviderType("ns1") AccessProviderTypePowerDNS = AccessProviderType("powerdns") AccessProviderTypeQiniu = AccessProviderType("qiniu") AccessProviderTypeSSH = AccessProviderType("ssh") @@ -54,6 +55,7 @@ const ( ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns") ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom") ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo") + ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1") ApplyDNSProviderTypePowerDNS = ApplyDNSProviderType("powerdns") ApplyDNSProviderTypeTencentCloud = ApplyDNSProviderType("tencentcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeTencentCloudDNS] ApplyDNSProviderTypeTencentCloudDNS = ApplyDNSProviderType("tencentcloud-dns") 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 new file mode 100644 index 00000000..7403ecb8 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1/ns1.go @@ -0,0 +1,33 @@ +package ns1 + +import ( + "errors" + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/ns1" +) + +type NS1ApplicantConfig struct { + ApiKey string `json:"apiKey"` + PropagationTimeout int32 `json:"propagationTimeout,omitempty"` +} + +func NewChallengeProvider(config *NS1ApplicantConfig) (challenge.Provider, error) { + if config == nil { + return nil, errors.New("config is nil") + } + + providerConfig := ns1.NewDefaultConfig() + providerConfig.APIKey = config.ApiKey + if config.PropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second + } + + provider, err := ns1.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/ns1.svg b/ui/public/imgs/providers/ns1.svg new file mode 100644 index 00000000..07811134 --- /dev/null +++ b/ui/public/imgs/providers/ns1.svg @@ -0,0 +1 @@ + diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index b398013a..040e52dd 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -21,6 +21,7 @@ import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; import AccessFormLocalConfig from "./AccessFormLocalConfig"; +import AccessFormNS1Config from "./AccessFormNS1Config"; import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig"; import AccessFormNameSiloConfig from "./AccessFormNameSiloConfig"; import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig"; @@ -111,6 +112,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.NAMESILO: return ; + case ACCESS_PROVIDERS.NS1: + return ; case ACCESS_PROVIDERS.POWERDNS: return ; case ACCESS_PROVIDERS.QINIU: diff --git a/ui/src/components/access/AccessFormNS1Config.tsx b/ui/src/components/access/AccessFormNS1Config.tsx new file mode 100644 index 00000000..5b12feb0 --- /dev/null +++ b/ui/src/components/access/AccessFormNS1Config.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 AccessConfigForNS1 } from "@/domain/access"; + +type AccessFormNS1ConfigFieldValues = Nullish; + +export type AccessFormNS1ConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormNS1ConfigFieldValues; + onValuesChange?: (values: AccessFormNS1ConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormNS1ConfigFieldValues => { + return { + apiKey: "", + }; +}; + +const AccessFormNS1Config = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormNS1ConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiKey: z + .string() + .min(1, t("access.form.ns1_api_key.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormNS1Config; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 01d97d04..ed2bd310 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -102,6 +102,10 @@ export type AccessConfigForNameSilo = { apiKey: string; }; +export type AccessConfigForNS1 = { + apiKey: string; +}; + export type AccessConfigForPowerDNS = { apiUrl: string; apiKey: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 42250e9d..ba1a10f2 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -18,6 +18,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ LOCAL: "local", NAMEDOTCOM: "namedotcom", NAMESILO: "namesilo", + NS1: "ns1", POWERDNS: "powerdns", QINIU: "qiniu", SSH: "ssh", @@ -68,6 +69,7 @@ export const accessProvidersMap: Map [ diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index 66228013..d609d6e7 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -103,6 +103,9 @@ "access.form.namesilo_api_key.label": "NameSilo API key", "access.form.namesilo_api_key.placeholder": "Please enter NameSilo API key", "access.form.namesilo_api_key.tooltip": "For more information, see https://www.namesilo.com/support/v2/articles/account-options/api-manager", + "access.form.ns1_api_key.label": "NS1 API key", + "access.form.ns1_api_key.placeholder": "Please enter NS1 API key", + "access.form.ns1_api_key.tooltip": "For more information, see https://www.ibm.com/docs/en/ns1-connect?topic=introduction-using-api", "access.form.powerdns_api_url.label": "PowerDNS API URL", "access.form.powerdns_api_url.placeholder": "Please enter PowerDNS API URL", "access.form.powerdns_api_url.tooltip": "For more information, see https://doc.powerdns.com/authoritative/http-api/index.html#endpoints-and-objects-in-the-api", diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index dc8ea864..7b0199a9 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -66,6 +66,7 @@ "common.provider.local": "Local deployment", "common.provider.namedotcom": "Name.com", "common.provider.namesilo": "NameSilo", + "common.provider.ns1": "NS1 (IBM NS1 Connect)", "common.provider.powerdns": "PowerDNS", "common.provider.qiniu": "Qiniu", "common.provider.qiniu.cdn": "Qiniu - Content Delivery Network (CDN)", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index e24c125b..b9107581 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -45,14 +45,14 @@ "access.form.aws_secret_access_key.label": "AWS SecretAccessKey", "access.form.aws_secret_access_key.placeholder": "请输入 AWS SecretAccessKey", "access.form.aws_secret_access_key.tooltip": "这是什么?请参阅 https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_credentials_access-keys.html", - "access.form.azure_tenant_id.label": "Azure TenantId", - "access.form.azure_tenant_id.placeholder": "请输入 Azure TenantId", + "access.form.azure_tenant_id.label": "Azure 租户 ID", + "access.form.azure_tenant_id.placeholder": "请输入 Azure 租户 ID", "access.form.azure_tenant_id.tooltip": "这是什么?请参阅 https://learn.microsoft.com/zh-cn/azure/azure-portal/get-subscription-tenant-id", - "access.form.azure_client_id.label": "Azure ClientId", - "access.form.azure_client_id.placeholder": "请输入 Azure ClientId", + "access.form.azure_client_id.label": "Azure 客户端 ID", + "access.form.azure_client_id.placeholder": "请输入 Azure 客户端 ID", "access.form.azure_client_id.tooltip": "这是什么?请参阅 https://learn.microsoft.com/zh-cn/azure/azure-monitor/logs/api/register-app-for-token", - "access.form.azure_client_secret.label": "Azure ClientSecret", - "access.form.azure_client_secret.placeholder": "请输入 Azure ClientSecret", + "access.form.azure_client_secret.label": "Azure 客户端密码", + "access.form.azure_client_secret.placeholder": "请输入 Azure 客户端密码", "access.form.azure_client_secret.tooltip": "这是什么?请参阅 https://learn.microsoft.com/zh-cn/azure/azure-monitor/logs/api/register-app-for-token", "access.form.azure_cloud_name.label": "Azure 主权云环境(可选)", "access.form.azure_cloud_name.placeholder": "请输入 Azure 主权云环境(例如:public)", @@ -103,6 +103,9 @@ "access.form.namesilo_api_key.label": "NameSilo API Key", "access.form.namesilo_api_key.placeholder": "请输入 NameSilo API Key", "access.form.namesilo_api_key.tooltip": "这是什么?请参阅 https://www.namesilo.com/support/v2/articles/account-options/api-manager", + "access.form.ns1_api_key.label": "NS1 API Key", + "access.form.ns1_api_key.placeholder": "请输入 NS1 API Key", + "access.form.ns1_api_key.tooltip": "这是什么?请参阅 https://www.ibm.com/docs/zh/ns1-connect?topic=introduction-using-api", "access.form.powerdns_api_url.label": "PowerDNS API URL", "access.form.powerdns_api_url.placeholder": "请输入 PowerDNS API URL", "access.form.powerdns_api_url.tooltip": "这是什么?请参阅 https://doc.powerdns.com/authoritative/http-api/index.html#endpoints-and-objects-in-the-api", diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index 80d6ccc9..0951152a 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -66,6 +66,7 @@ "common.provider.local": "本地部署", "common.provider.namedotcom": "Name.com", "common.provider.namesilo": "NameSilo", + "common.provider.ns1": "NS1(IBM NS1 Connect)", "common.provider.powerdns": "PowerDNS", "common.provider.qiniu": "七牛云", "common.provider.qiniu.cdn": "七牛云 - 内容分发网络 CDN", From 974c320925a9beda0feb3887b798883af61839ec Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 15 Jan 2025 22:13:28 +0800 Subject: [PATCH 3/5] feat: add edgio applications v7 deployer --- README.md | 1 + README_EN.md | 1 + go.mod | 1 + go.sum | 2 + internal/applicant/applicant.go | 10 +- internal/deployer/providers.go | 16 + internal/domain/access.go | 5 + internal/domain/provider.go | 62 +- .../edgio-applications/edgio_applications.go | 112 ++++ .../edgio_applications_test.go | 75 +++ .../edgio-sdk/applications/v7/README.md | 3 + .../applications/v7/dtos/cdn_configuration.go | 93 +++ .../applications/v7/dtos/environment.go | 29 + .../applications/v7/dtos/property.go | 18 + .../edgio-sdk/applications/v7/dtos/purge.go | 18 + .../applications/v7/dtos/tls_cert.go | 30 + .../edgio-sdk/applications/v7/edgio_client.go | 546 ++++++++++++++++++ .../applications/v7/edgio_client_interface.go | 26 + .../workflow/node-processor/apply_node.go | 4 +- ui/public/imgs/providers/edgio.svg | 1 + ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormEdgioConfig.tsx | 76 +++ .../workflow/node/DeployNodeConfigForm.tsx | 3 + ...yNodeConfigFormEdgioApplicationsConfig.tsx | 65 +++ ui/src/domain/access.ts | 6 + ui/src/domain/provider.ts | 4 + ui/src/i18n/locales/en/nls.access.json | 6 + ui/src/i18n/locales/en/nls.common.json | 2 + .../i18n/locales/en/nls.workflow.nodes.json | 3 + ui/src/i18n/locales/zh/nls.access.json | 6 + ui/src/i18n/locales/zh/nls.common.json | 2 + .../i18n/locales/zh/nls.workflow.nodes.json | 3 + 32 files changed, 1195 insertions(+), 37 deletions(-) create mode 100644 internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go create mode 100644 internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go create mode 100644 internal/pkg/vendors/edgio-sdk/applications/v7/README.md create mode 100644 internal/pkg/vendors/edgio-sdk/applications/v7/dtos/cdn_configuration.go create mode 100644 internal/pkg/vendors/edgio-sdk/applications/v7/dtos/environment.go create mode 100644 internal/pkg/vendors/edgio-sdk/applications/v7/dtos/property.go create mode 100644 internal/pkg/vendors/edgio-sdk/applications/v7/dtos/purge.go create mode 100644 internal/pkg/vendors/edgio-sdk/applications/v7/dtos/tls_cert.go create mode 100644 internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client.go create mode 100644 internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client_interface.go create mode 100644 ui/public/imgs/providers/edgio.svg create mode 100644 ui/src/components/access/AccessFormEdgioConfig.tsx create mode 100644 ui/src/components/workflow/node/DeployNodeConfigFormEdgioApplicationsConfig.tsx diff --git a/README.md b/README.md index ed79b12f..745cec19 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ make local.run | [多吉云](https://www.dogecloud.com/) | 可部署到多吉云 CDN | | [BytePlus](https://www.byteplus.com/) | 可部署到 BytePlus CDN 等服务 | | [优刻得](https://www.ucloud.cn/) | 可部署到优刻得 US3、UCDN 等服务 | +| [Edgio](https://edg.io/) | 可部署到 Edgio Applications 等服务 | diff --git a/README_EN.md b/README_EN.md index 9c9aab55..ece9591c 100644 --- a/README_EN.md +++ b/README_EN.md @@ -126,6 +126,7 @@ The following hosting providers are supported: | [Doge Cloud](https://www.dogecloud.com/) | Supports deployment to Doge Cloud CDN | | [BytePlus](https://www.byteplus.com/) | Supports deployment to BytePlus CDN | | [UCloud](https://www.ucloud-global.com/) | Supports deployment to UCloud US3, UCDN | +| [Edgio](https://edg.io/) | Supports deployment to Edgio Applications | diff --git a/go.mod b/go.mod index 9d52261f..e6ba637a 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/baidubce/bce-sdk-go v0.9.209 github.com/byteplus-sdk/byteplus-sdk-golang v1.0.35 github.com/go-acme/lego/v4 v4.21.0 + github.com/go-resty/resty/v2 v2.16.3 github.com/go-viper/mapstructure/v2 v2.2.1 github.com/gojek/heimdall/v7 v7.0.3 github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.128 diff --git a/go.sum b/go.sum index d41faffa..52df7389 100644 --- a/go.sum +++ b/go.sum @@ -398,6 +398,8 @@ github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= +github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E= +github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= diff --git a/internal/applicant/applicant.go b/internal/applicant/applicant.go index 218b42fc..a89204c6 100644 --- a/internal/applicant/applicant.go +++ b/internal/applicant/applicant.go @@ -20,9 +20,9 @@ import ( ) type ApplyCertResult struct { - Certificate string - PrivateKey string + CertificateChain string IssuerCertificate string + PrivateKey string ACMECertUrl string ACMECertStableUrl string CSR string @@ -150,9 +150,9 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap } return &ApplyCertResult{ - PrivateKey: string(certResource.PrivateKey), - Certificate: string(certResource.Certificate), - IssuerCertificate: string(certResource.IssuerCertificate), + CertificateChain: strings.TrimSpace(string(certResource.Certificate)), + IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)), + PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)), ACMECertUrl: certResource.CertURL, ACMECertStableUrl: certResource.CertStableURL, CSR: string(certResource.CSR), diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index 6cb192f9..6fd3457c 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -15,6 +15,7 @@ import ( providerBaiduCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-cdn" 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" @@ -175,6 +176,21 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, return deployer, logger, err } + case domain.DeployProviderTypeEdgioApplications: + { + access := domain.AccessConfigForEdgio{} + if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil { + return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err) + } + + deployer, err := providerEdgioApplications.NewWithLogger(&providerEdgioApplications.EdgioApplicationsDeployerConfig{ + ClientId: access.ClientId, + ClientSecret: access.ClientSecret, + EnvironmentId: maps.GetValueAsString(options.ProviderDeployConfig, "environmentId"), + }, logger) + return deployer, logger, err + } + case domain.DeployProviderTypeHuaweiCloudCDN, domain.DeployProviderTypeHuaweiCloudELB: { access := domain.AccessConfigForHuaweiCloud{} diff --git a/internal/domain/access.go b/internal/domain/access.go index dc118482..c9b1d2a9 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -66,6 +66,11 @@ type AccessConfigForDogeCloud struct { SecretKey string `json:"secretKey"` } +type AccessConfigForEdgio struct { + ClientId string `json:"clientId"` + ClientSecret string `json:"clientSecret"` +} + type AccessConfigForGoDaddy struct { ApiKey string `json:"apiKey"` ApiSecret string `json:"apiSecret"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index dead9c57..34666531 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -17,6 +17,7 @@ const ( AccessProviderTypeBytePlus = AccessProviderType("byteplus") AccessProviderTypeCloudflare = AccessProviderType("cloudflare") AccessProviderTypeDogeCloud = AccessProviderType("dogecloud") + AccessProviderTypeEdgio = AccessProviderType("edgio") AccessProviderTypeGoDaddy = AccessProviderType("godaddy") AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") AccessProviderTypeKubernetes = AccessProviderType("k8s") @@ -73,34 +74,35 @@ type DeployProviderType string NOTICE: If you add new constant, please keep ASCII order. */ const ( - DeployProviderTypeAliyunALB = DeployProviderType("aliyun-alb") - DeployProviderTypeAliyunCDN = DeployProviderType("aliyun-cdn") - DeployProviderTypeAliyunCLB = DeployProviderType("aliyun-clb") - DeployProviderTypeAliyunDCDN = DeployProviderType("aliyun-dcdn") - DeployProviderTypeAliyunLive = DeployProviderType("aliyun-live") - DeployProviderTypeAliyunNLB = DeployProviderType("aliyun-nlb") - DeployProviderTypeAliyunOSS = DeployProviderType("aliyun-oss") - DeployProviderTypeBaiduCloudCDN = DeployProviderType("baiducloud-cdn") - DeployProviderTypeBytePlusCDN = DeployProviderType("byteplus-cdn") - DeployProviderTypeDogeCloudCDN = DeployProviderType("dogecloud-cdn") - DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn") - DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb") - DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret") - DeployProviderTypeLocal = DeployProviderType("local") - DeployProviderTypeQiniuCDN = DeployProviderType("qiniu-cdn") - DeployProviderTypeSSH = DeployProviderType("ssh") - DeployProviderTypeTencentCloudCDN = DeployProviderType("tencentcloud-cdn") - DeployProviderTypeTencentCloudCLB = DeployProviderType("tencentcloud-clb") - DeployProviderTypeTencentCloudCOS = DeployProviderType("tencentcloud-cos") - DeployProviderTypeTencentCloudCSS = DeployProviderType("tencentcloud-css") - DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn") - DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo") - DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn") - DeployProviderTypeUCloudUS3 = DeployProviderType("ucloud-us3") - DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn") - DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb") - DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn") - DeployProviderTypeVolcEngineLive = DeployProviderType("volcengine-live") - DeployProviderTypeVolcEngineTOS = DeployProviderType("volcengine-tos") - DeployProviderTypeWebhook = DeployProviderType("webhook") + DeployProviderTypeAliyunALB = DeployProviderType("aliyun-alb") + DeployProviderTypeAliyunCDN = DeployProviderType("aliyun-cdn") + DeployProviderTypeAliyunCLB = DeployProviderType("aliyun-clb") + DeployProviderTypeAliyunDCDN = DeployProviderType("aliyun-dcdn") + DeployProviderTypeAliyunLive = DeployProviderType("aliyun-live") + DeployProviderTypeAliyunNLB = DeployProviderType("aliyun-nlb") + DeployProviderTypeAliyunOSS = DeployProviderType("aliyun-oss") + DeployProviderTypeBaiduCloudCDN = DeployProviderType("baiducloud-cdn") + DeployProviderTypeBytePlusCDN = DeployProviderType("byteplus-cdn") + DeployProviderTypeDogeCloudCDN = DeployProviderType("dogecloud-cdn") + DeployProviderTypeEdgioApplications = DeployProviderType("edgio-applications") + DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn") + DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb") + DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret") + DeployProviderTypeLocal = DeployProviderType("local") + DeployProviderTypeQiniuCDN = DeployProviderType("qiniu-cdn") + DeployProviderTypeSSH = DeployProviderType("ssh") + DeployProviderTypeTencentCloudCDN = DeployProviderType("tencentcloud-cdn") + DeployProviderTypeTencentCloudCLB = DeployProviderType("tencentcloud-clb") + DeployProviderTypeTencentCloudCOS = DeployProviderType("tencentcloud-cos") + DeployProviderTypeTencentCloudCSS = DeployProviderType("tencentcloud-css") + DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn") + DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo") + DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn") + DeployProviderTypeUCloudUS3 = DeployProviderType("ucloud-us3") + DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn") + DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb") + DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn") + DeployProviderTypeVolcEngineLive = DeployProviderType("volcengine-live") + DeployProviderTypeVolcEngineTOS = DeployProviderType("volcengine-tos") + DeployProviderTypeWebhook = DeployProviderType("webhook") ) diff --git a/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go new file mode 100644 index 00000000..d4d70e04 --- /dev/null +++ b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications.go @@ -0,0 +1,112 @@ +package edgioapplications + +import ( + "context" + "encoding/pem" + "errors" + + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + edgsdk "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7" + edgsdkDtos "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7/dtos" +) + +type EdgioApplicationsDeployerConfig struct { + // Edgio ClientId。 + ClientId string `json:"clientId"` + // Edgio ClientSecret。 + ClientSecret string `json:"clientSecret"` + // Edgio 环境 ID。 + EnvironmentId string `json:"environmentId"` +} + +type EdgioApplicationsDeployer struct { + config *EdgioApplicationsDeployerConfig + logger logger.Logger + sdkClient *edgsdk.EdgioClient +} + +var _ deployer.Deployer = (*EdgioApplicationsDeployer)(nil) + +func New(config *EdgioApplicationsDeployerConfig) (*EdgioApplicationsDeployer, error) { + return NewWithLogger(config, logger.NewNilLogger()) +} + +func NewWithLogger(config *EdgioApplicationsDeployerConfig, logger logger.Logger) (*EdgioApplicationsDeployer, error) { + if config == nil { + return nil, errors.New("config is nil") + } + + if logger == nil { + return nil, errors.New("logger is nil") + } + + client, err := createSdkClient(config.ClientId, config.ClientSecret) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &EdgioApplicationsDeployer{ + logger: logger, + config: config, + sdkClient: client, + }, nil +} + +func (d *EdgioApplicationsDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 提取 Edgio 所需的服务端证书和中间证书内容 + privateCertPem, intermediateCertPem := extractCertChains(certPem) + + // 上传 TLS 证书 + // REF: https://docs.edg.io/rest_api/#tag/tls-certs/operation/postConfigV01TlsCerts + uploadTlsCertReq := edgsdkDtos.UploadTlsCertRequest{ + EnvironmentID: d.config.EnvironmentId, + PrimaryCert: privateCertPem, + IntermediateCert: intermediateCertPem, + PrivateKey: privkeyPem, + } + uploadTlsCertResp, err := d.sdkClient.UploadTlsCert(uploadTlsCertReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'edgio.UploadTlsCert'") + } + + d.logger.Logt("已上传 TLS 证书", uploadTlsCertResp) + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(clientId, clientSecret string) (*edgsdk.EdgioClient, error) { + client := edgsdk.NewEdgioClient(clientId, clientSecret, "", "") + return client, nil +} + +func extractCertChains(certPem string) (primaryCertPem string, intermediateCertPem string) { + pemBlocks := make([]*pem.Block, 0) + pemData := []byte(certPem) + for { + block, rest := pem.Decode(pemData) + if block == nil { + break + } + + pemBlocks = append(pemBlocks, block) + pemData = rest + } + + primaryCertPem = "" + intermediateCertPem = "" + + if len(pemBlocks) > 0 { + primaryCertPem = string(pem.EncodeToMemory(pemBlocks[0])) + } + + if len(pemBlocks) > 1 { + for i := 1; i < len(pemBlocks); i++ { + intermediateCertPem += string(pem.EncodeToMemory(pemBlocks[i])) + } + } + + return primaryCertPem, intermediateCertPem +} 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 new file mode 100644 index 00000000..04f7a4cb --- /dev/null +++ b/internal/pkg/core/deployer/providers/edgio-applications/edgio_applications_test.go @@ -0,0 +1,75 @@ +package edgioapplications_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/edgio-applications" +) + +var ( + fInputCertPath string + fInputKeyPath string + fClientId string + fClientSecret string + fEnvironmentId string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_EDGIOAPPLICATIONS_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fClientId, argsPrefix+"CLIENTID", "", "") + flag.StringVar(&fClientSecret, argsPrefix+"CLIENTSECRET", "", "") + flag.StringVar(&fEnvironmentId, argsPrefix+"ENVIRONMENTID", "", "") +} + +/* +Shell command to run this test: + + go test -v ./edgio_applications_test.go -args \ + --CERTIMATE_DEPLOYER_EDGIOAPPLICATIONS_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_EDGIOAPPLICATIONS_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_EDGIOAPPLICATIONS_CLIENTID="your-client-id" \ + --CERTIMATE_DEPLOYER_EDGIOAPPLICATIONS_CLIENTSECRET="your-client-secret" \ + --CERTIMATE_DEPLOYER_EDGIOAPPLICATIONS_ENVIRONMENTID="your-enviroment-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("CLIENTID: %v", fClientId), + fmt.Sprintf("CLIENTSECRET: %v", fClientSecret), + fmt.Sprintf("ENVIRONMENTID: %v", fEnvironmentId), + }, "\n")) + + deployer, err := provider.New(&provider.EdgioApplicationsDeployerConfig{ + ClientId: fClientId, + ClientSecret: fClientSecret, + EnvironmentId: fEnvironmentId, + }) + 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/vendors/edgio-sdk/applications/v7/README.md b/internal/pkg/vendors/edgio-sdk/applications/v7/README.md new file mode 100644 index 00000000..fae60236 --- /dev/null +++ b/internal/pkg/vendors/edgio-sdk/applications/v7/README.md @@ -0,0 +1,3 @@ +```shell +git clone https://github.com/Edgio/terraform-provider-edgio.git +``` diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/cdn_configuration.go b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/cdn_configuration.go new file mode 100644 index 00000000..2ae5d434 --- /dev/null +++ b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/cdn_configuration.go @@ -0,0 +1,93 @@ +package dtos + +import "encoding/json" + +type CDNConfiguration struct { + ConfigurationID string `json:"id"` + EnvironmentID string `json:"environment_id"` + Rules json.RawMessage `json:"rules"` + Origins []Origin `json:"origins"` + Hostnames []Hostname `json:"hostnames"` + Experiments *[]string `json:"experiments,omitempty"` + EdgeFunctionsSources *map[string]string `json:"edge_functions_sources,omitempty"` + EdgeFunctionInitScript *string `json:"edge_function_init_script,omitempty"` +} + +type Origin struct { + Name string `json:"name"` + Type *string `json:"type,omitempty"` + Hosts []Host `json:"hosts"` + Balancer *string `json:"balancer,omitempty"` + OverrideHostHeader *string `json:"override_host_header,omitempty"` + Shields *Shields `json:"shields,omitempty"` + PciCertifiedShields *bool `json:"pci_certified_shields,omitempty"` + TLSVerify *TLSVerify `json:"tls_verify,omitempty"` + Retry *Retry `json:"retry,omitempty"` +} + +type Host struct { + Weight *int64 `json:"weight,omitempty"` + DNSMaxTTL *int64 `json:"dns_max_ttl,omitempty"` + DNSPreference *string `json:"dns_preference,omitempty"` + MaxHardPool *int64 `json:"max_hard_pool,omitempty"` + DNSMinTTL *int64 `json:"dns_min_ttl,omitempty"` + Location *[]Location `json:"location,omitempty"` + MaxPool *int64 `json:"max_pool,omitempty"` + Balancer *string `json:"balancer,omitempty"` + Scheme *string `json:"scheme,omitempty"` + OverrideHostHeader *string `json:"override_host_header,omitempty"` + SNIHintAndStrictSanCheck *string `json:"sni_hint_and_strict_san_check,omitempty"` + UseSNI *bool `json:"use_sni,omitempty"` +} + +type Location struct { + Port *int64 `json:"port,omitempty"` + Hostname *string `json:"hostname,omitempty"` +} + +type Shields struct { + Apac *string `json:"apac,omitempty"` + Emea *string `json:"emea,omitempty"` + USWest *string `json:"us_west,omitempty"` + USEast *string `json:"us_east,omitempty"` +} + +type TLSVerify struct { + UseSNI *bool `json:"use_sni,omitempty"` + SNIHintAndStrictSanCheck *string `json:"sni_hint_and_strict_san_check,omitempty"` + AllowSelfSignedCerts *bool `json:"allow_self_signed_certs,omitempty"` + PinnedCerts *[]string `json:"pinned_certs,omitempty"` +} + +type Retry struct { + StatusCodes *[]int64 `json:"status_codes,omitempty"` + IgnoreRetryAfterHeader *bool `json:"ignore_retry_after_header,omitempty"` + AfterSeconds *int64 `json:"after_seconds,omitempty"` + MaxRequests *int64 `json:"max_requests,omitempty"` + MaxWaitSeconds *int64 `json:"max_wait_seconds,omitempty"` +} + +type Hostname struct { + Hostname *string `json:"hostname,omitempty"` + DefaultOriginName *string `json:"default_origin_name,omitempty"` + ReportCode *int64 `json:"report_code,omitempty"` + TLS *TLS `json:"tls,omitempty"` + Directory *string `json:"directory,omitempty"` +} + +type TLS struct { + NPN *bool `json:"npn,omitempty"` + ALPN *bool `json:"alpn,omitempty"` + Protocols *string `json:"protocols,omitempty"` + UseSigAlgs *bool `json:"use_sigalgs,omitempty"` + SNI *bool `json:"sni,omitempty"` + SniStrict *bool `json:"sni_strict,omitempty"` + SniHostMatch *bool `json:"sni_host_match,omitempty"` + ClientRenegotiation *bool `json:"client_renegotiation,omitempty"` + Options *string `json:"options,omitempty"` + CipherList *string `json:"cipher_list,omitempty"` + NamedCurve *string `json:"named_curve,omitempty"` + OCSP *bool `json:"oscp,omitempty"` + PEM *string `json:"pem,omitempty"` + CA *string `json:"ca,omitempty"` +} diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/environment.go b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/environment.go new file mode 100644 index 00000000..74d99dd6 --- /dev/null +++ b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/environment.go @@ -0,0 +1,29 @@ +package dtos + +import ( + "time" +) + +type Environment struct { + Type string `json:"@type"` + IdLink string `json:"@id"` + Id string `json:"id"` + PropertyID string `json:"property_id"` + LegacyAccountNumber string `json:"legacy_account_number"` + Name string `json:"name"` + CanMembersDeploy bool `json:"can_members_deploy"` + OnlyMaintainersCanDeploy bool `json:"only_maintainers_can_deploy"` + HttpRequestLogging bool `json:"http_request_logging"` + DefaultDomainName string `json:"default_domain_name"` + PciCompliance bool `json:"pci_compliance"` + DnsDomainName string `json:"dns_domain_name"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type EnvironmentsResponse struct { + Type string `json:"@type"` + Id string `json:"@id"` + TotalItems int `json:"total_items"` + Items []Environment `json:"items"` +} diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/property.go b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/property.go new file mode 100644 index 00000000..378ab999 --- /dev/null +++ b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/property.go @@ -0,0 +1,18 @@ +package dtos + +import "time" + +type Property struct { + IdLink string `json:"@id"` + Id string `json:"id"` + OrganizationID string `json:"organization_id"` + Slug string `json:"slug"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type Properties struct { + ID string `json:"@id"` + TotalItems int `json:"total_items"` + Items []Property `json:"items"` +} diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/purge.go b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/purge.go new file mode 100644 index 00000000..67ad2612 --- /dev/null +++ b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/purge.go @@ -0,0 +1,18 @@ +package dtos + +import "time" + +type PurgeResponse struct { + ID string `json:"id"` + Status string `json:"status"` + CreatedAt time.Time `json:"created_at"` + CompletedAt time.Time `json:"completed_at"` + ProgressPercentage float32 `json:"progress_percentage"` +} + +type PurgeRequest struct { + EnvironmentID string `json:"environment_id"` + PurgeType string `json:"purge_type"` + Values []string `json:"values"` + Hostname *string `json:"hostname"` +} diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/tls_cert.go b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/tls_cert.go new file mode 100644 index 00000000..08da9b4c --- /dev/null +++ b/internal/pkg/vendors/edgio-sdk/applications/v7/dtos/tls_cert.go @@ -0,0 +1,30 @@ +package dtos + +type TLSCertResponse struct { + ID string `json:"id"` + EnvironmentID string `json:"environment_id"` + PrimaryCert string `json:"primary_cert"` + IntermediateCert string `json:"intermediate_cert"` + Expiration string `json:"expiration"` + Status string `json:"status"` + Generated bool `json:"generated"` + Serial string `json:"serial"` + CommonName string `json:"common_name"` + AlternativeNames []string `json:"alternative_names"` + ActivationError string `json:"activation_error"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +type UploadTlsCertRequest struct { + EnvironmentID string `json:"environment_id"` + PrimaryCert string `json:"primary_cert"` + IntermediateCert string `json:"intermediate_cert"` + PrivateKey string `json:"private_key"` +} + +type TLSCertSResponse struct { + EnvironmentID string `json:"environment_id"` + TotalItems int32 `json:"total_items"` + Certificates []TLSCertResponse `json:"items"` +} diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client.go b/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client.go new file mode 100644 index 00000000..a03436fc --- /dev/null +++ b/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client.go @@ -0,0 +1,546 @@ +package edgio_api + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/go-resty/resty/v2" + + "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7/dtos" +) + +// AccessTokenResponse represents the response from the token endpoint. +type AccessTokenResponse struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` +} + +// TokenCache represents a cached token. The token is stored along +// with its expiry time. Because different endpoints require different +// scopes, we store the token with the scope as the key, so that we +// can fetch the token from the cache based on the scope. +type TokenCache struct { + AccessToken string + Expiry time.Time +} + +type EdgioClient struct { + client *resty.Client + clientID string + clientSecret string + tokenURL string + apiURL string + tokenCache map[string]TokenCache +} + +func NewEdgioClient(clientID, clientSecret, tokenURL, apiURL string) *EdgioClient { + client := resty.New(). + SetTimeout(30 * time.Second). + SetRetryCount(3). + SetRetryWaitTime(5 * time.Second). + SetRetryMaxWaitTime(20 * time.Second) + + if tokenURL == "" { + tokenURL = "https://id.edgio.app/connect/token" + } + + if apiURL == "" { + apiURL = "https://edgioapis.com" + } + + return &EdgioClient{ + client: client, + clientID: clientID, + clientSecret: clientSecret, + tokenURL: tokenURL, + apiURL: apiURL, + tokenCache: make(map[string]TokenCache), + } +} + +func (c *EdgioClient) getToken(scope string) (string, error) { + if cachedToken, exists := c.tokenCache[scope]; exists && time.Now().Before(cachedToken.Expiry) { + return cachedToken.AccessToken, nil + } + + var tokenResp AccessTokenResponse + resp, err := c.client.R(). + SetFormData(map[string]string{ + "client_id": c.clientID, + "client_secret": c.clientSecret, + "grant_type": "client_credentials", + "scope": scope, + }). + SetResult(&tokenResp). + Post(c.tokenURL) + if err != nil { + return "", fmt.Errorf("failed to request token: %w", err) + } + + if resp.IsError() { + return "", fmt.Errorf("unexpected status code for getToken: %d", resp.StatusCode()) + } + + c.tokenCache[scope] = TokenCache{ + AccessToken: tokenResp.AccessToken, + Expiry: time.Now().Add(time.Duration(tokenResp.ExpiresIn) * time.Second), + } + + return tokenResp.AccessToken, nil +} + +func (c *EdgioClient) GetProperty(ctx context.Context, propertyID string) (*dtos.Property, error) { + token, err := c.getToken("app.accounts") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/properties/%s", c.apiURL, propertyID) + + var property dtos.Property + resp, err := c.client.R(). + SetContext(ctx). + SetAuthToken(token). + SetResult(&property). + Get(url) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("unexpected status code for getSpecificProperty: %d, %s", resp.StatusCode(), resp.Request.URL) + } + + return &property, nil +} + +func (c *EdgioClient) GetProperties(page int, pageSize int, organizationID string) (*dtos.Properties, error) { + token, err := c.getToken("app.accounts") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/properties", c.apiURL) + + var propertiesResp dtos.Properties + resp, err := c.client.R(). + SetAuthToken(token). + SetQueryParams(map[string]string{ + "page": fmt.Sprintf("%d", page), + "page_size": fmt.Sprintf("%d", pageSize), + "organization_id": organizationID, + }). + SetResult(&propertiesResp). + Get(url) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("unexpected status code for getProperties: %d, %s", resp.StatusCode(), resp.Body()) + } + + return &propertiesResp, nil +} + +func (c *EdgioClient) CreateProperty(ctx context.Context, organizationID, slug string) (*dtos.Property, error) { + token, err := c.getToken("app.accounts") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/properties", c.apiURL) + + var createdProperty dtos.Property + resp, err := c.client.R(). + SetContext(ctx). + SetAuthToken(token). + SetHeader("Content-Type", "application/json"). + SetBody(map[string]string{ + "organization_id": organizationID, + "slug": slug, + }). + SetResult(&createdProperty). + Post(url) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("unexpected status code for createProperty: %d, response: %s", resp.StatusCode(), resp.String()) + } + + return &createdProperty, nil +} + +func (c *EdgioClient) DeleteProperty(propertyID string) error { + token, err := c.getToken("app.accounts") + if err != nil { + return fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/properties/%s", c.apiURL, propertyID) + + resp, err := c.client.R(). + SetAuthToken(token). + Delete(url) + if err != nil { + return fmt.Errorf("error sending DELETE request: %w", err) + } + + if resp.IsError() { + return fmt.Errorf("error deleting property: status code %d", resp.StatusCode()) + } + + return nil +} + +func (c *EdgioClient) UpdateProperty(ctx context.Context, propertyID string, slug string) (*dtos.Property, error) { + token, err := c.getToken("app.accounts") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/properties/%s", c.apiURL, propertyID) + + requestBody := map[string]interface{}{ + "slug": slug, + } + + var updatedProperty dtos.Property + resp, err := c.client.R(). + SetContext(ctx). + SetAuthToken(token). + SetBody(requestBody). + SetResult(&updatedProperty). + Patch(url) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("unexpected status code for updateProperty: %d", resp.StatusCode()) + } + + return &updatedProperty, nil +} + +func (c *EdgioClient) GetEnvironments(page, pageSize int, propertyID string) (*dtos.EnvironmentsResponse, error) { + token, err := c.getToken("app.accounts") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/environments", c.apiURL) + + resp, err := c.client.R(). + SetAuthToken(token). + SetQueryParams(map[string]string{ + "page": fmt.Sprintf("%d", page), + "page_size": fmt.Sprintf("%d", pageSize), + "property_id": propertyID, + }). + SetResult(&dtos.EnvironmentsResponse{}). + Get(url) + if err != nil { + return nil, err + } + + if resp.IsError() { + return nil, fmt.Errorf("error response: %s", resp.String()) + } + + return resp.Result().(*dtos.EnvironmentsResponse), nil +} + +func (c *EdgioClient) GetEnvironment(environmentID string) (*dtos.Environment, error) { + token, err := c.getToken("app.accounts") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/environments/%s", c.apiURL, environmentID) + + resp, err := c.client.R(). + SetPathParams(map[string]string{ + "environment_id": environmentID, + }). + SetAuthToken(token). + SetResult(&dtos.Environment{}). + Get(url) + if err != nil { + return nil, err + } + + if resp.IsError() { + return nil, fmt.Errorf("error response: %s", resp.String()) + } + + return resp.Result().(*dtos.Environment), nil +} + +func (c *EdgioClient) CreateEnvironment(propertyID, name string, onlyMaintainersCanDeploy, httpRequestLogging bool) (*dtos.Environment, error) { + token, err := c.getToken("app.accounts") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/environments", c.apiURL) + + body := map[string]interface{}{ + "property_id": propertyID, + "name": name, + "only_maintainers_can_deploy": onlyMaintainersCanDeploy, + "http_request_logging": httpRequestLogging, + } + + resp, err := c.client.R(). + SetBody(body). + SetAuthToken(token). + SetResult(&dtos.Environment{}). + Post(url) + if err != nil { + return nil, err + } + + if resp.IsError() { + return nil, fmt.Errorf("error response: %s", resp.String()) + } + + return resp.Result().(*dtos.Environment), nil +} + +func (c *EdgioClient) UpdateEnvironment(environmentID, name string, onlyMaintainersCanDeploy, httpRequestLogging, preserveCache bool) (*dtos.Environment, error) { + token, err := c.getToken("app.accounts") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/environments/%s", c.apiURL, environmentID) + + body := map[string]interface{}{ + "name": name, + // as can_members_deploy is depricated, but update api is not + // we need to use it to map onlyMaintainersCanDeploy + "only_maintainers_can_deploy": onlyMaintainersCanDeploy, + "http_request_logging": httpRequestLogging, + "preserve_cache": preserveCache, + } + + resp, err := c.client.R(). + SetPathParams(map[string]string{ + "environment_id": environmentID, + }). + SetBody(body). + SetAuthToken(token). + SetResult(&dtos.Environment{}). + Patch(url) + if err != nil { + return nil, err + } + + if resp.IsError() { + return nil, fmt.Errorf("error response: %s", resp.String()) + } + + return resp.Result().(*dtos.Environment), nil +} + +func (c *EdgioClient) DeleteEnvironment(environmentID string) error { + token, err := c.getToken("app.accounts") + if err != nil { + return fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/accounts/v0.1/environments/%s", c.apiURL, environmentID) + + resp, err := c.client.R(). + SetPathParams(map[string]string{ + "environment_id": environmentID, + }). + SetAuthToken(token). + SetResult(&dtos.Environment{}). + Delete(url) + if err != nil { + return err + } + + if resp.IsError() { + return fmt.Errorf("error response: %s", resp.String()) + } + + return nil +} + +func (c *EdgioClient) GetTlsCert(tlsCertId string) (*dtos.TLSCertResponse, error) { + token, err := c.getToken("app.config") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/config/v0.1/tls-certs/%s", c.apiURL, tlsCertId) + + var tlsCertResponse dtos.TLSCertResponse + resp, err := c.client.R(). + SetAuthToken(token). + SetResult(&tlsCertResponse). + Get(url) + if err != nil { + return nil, fmt.Errorf("error response: %s", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("error response: %s", resp.String()) + } + + return &tlsCertResponse, nil +} + +func (c *EdgioClient) UploadTlsCert(req dtos.UploadTlsCertRequest) (*dtos.TLSCertResponse, error) { + token, err := c.getToken("app.config") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/config/v0.1/tls-certs", c.apiURL) + response := &dtos.TLSCertResponse{} + + resp, err := c.client.R(). + SetAuthToken(token). + SetHeader("Content-Type", "application/json"). + SetBody(req). + SetResult(response). + Post(url) + if err != nil { + return nil, fmt.Errorf("failed to upload TLS certificate: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("API responded with error: %s", resp.String()) + } + + return response, nil +} + +func (c *EdgioClient) GenerateTlsCert(environmentId string) (*dtos.TLSCertResponse, error) { + token, err := c.getToken("app.config") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/config/v0.1/tls-certs/generate", c.apiURL) + request := map[string]interface{}{ + "environment_id": environmentId, + } + response := &dtos.TLSCertResponse{} + + resp, err := c.client.R(). + SetAuthToken(token). + SetHeader("Content-Type", "application/json"). + SetBody(request). + SetResult(response). + Post(url) + if err != nil { + return nil, fmt.Errorf("failed to upload TLS certificate: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("API responded with error: %s", resp.String()) + } + + return response, nil +} + +func (c *EdgioClient) GetTlsCerts(page int, pageSize int, environmentID string) (*dtos.TLSCertSResponse, error) { + token, err := c.getToken("app.config") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/config/v0.1/tls-certs", c.apiURL) + + var tlsCertsResponse dtos.TLSCertSResponse + resp, err := c.client.R(). + SetAuthToken(token). + SetQueryParams(map[string]string{ + "page": fmt.Sprintf("%d", page), + "page_size": fmt.Sprintf("%d", pageSize), + "environment_id": environmentID, + }). + SetResult(&tlsCertsResponse). + Get(url) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("unexpected status code for getTlsCerts: %d", resp.StatusCode()) + } + + return &tlsCertsResponse, nil +} + +func (c *EdgioClient) UploadCdnConfiguration(config *dtos.CDNConfiguration) (*dtos.CDNConfiguration, error) { + fmt.Println("------------------------------------------------------------------------- uploading") + + token, err := c.getToken("app.config") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("%s/config/v0.1/configs", c.apiURL) + var response dtos.CDNConfiguration + + // Convert config to json + jsonBody, _ := json.MarshalIndent(config, "", " ") + jsonString := string(jsonBody) + fmt.Println("------------------------- config report code: ", config.Hostnames[0].ReportCode == nil) + fmt.Println("------------------------- config report code value: ", config.Hostnames[0].ReportCode) + fmt.Println("----------------------------------- jsonBody: ", jsonString) + + resp, err := c.client.R(). + SetAuthToken(token). + SetHeader("Content-Type", "application/json"). + SetBody(config). + SetResult(&response). + Post(url) + if err != nil { + return nil, fmt.Errorf("failed to upload CDN configuration: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("unexpected status code for uploadCdnConfiguration: %d, %s", resp.StatusCode(), resp.Body()) + } + + return &response, nil +} + +func (c *EdgioClient) GetCDNConfiguration(configID string) (*dtos.CDNConfiguration, error) { + fmt.Println("------------------------------------------------------------------------- reading config") + + token, err := c.getToken("app.config") + if err != nil { + return nil, fmt.Errorf("failed to get token: %w", err) + } + + url := fmt.Sprintf("https://edgioapis.com/config/v0.1/configs/%s", configID) + var response dtos.CDNConfiguration + + resp, err := c.client.R(). + SetAuthToken(token). + SetResult(&response). + Get(url) + if err != nil { + return nil, fmt.Errorf("failed to get CDN configuration: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("unexpected status code for GetCDNConfiguration: %d", resp.StatusCode()) + } + + return &response, nil +} diff --git a/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client_interface.go b/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client_interface.go new file mode 100644 index 00000000..ea5fa958 --- /dev/null +++ b/internal/pkg/vendors/edgio-sdk/applications/v7/edgio_client_interface.go @@ -0,0 +1,26 @@ +package edgio_api + +import ( + "context" + + "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7/dtos" +) + +type EdgioClientInterface interface { + GetProperty(ctx context.Context, propertyID string) (*dtos.Property, error) + GetProperties(page int, pageSize int, organizationID string) (*dtos.Properties, error) + CreateProperty(ctx context.Context, organizationID, slug string) (*dtos.Property, error) + DeleteProperty(propertyID string) error + UpdateProperty(ctx context.Context, propertyID string, slug string) (*dtos.Property, error) + GetEnvironments(page, pageSize int, propertyID string) (*dtos.EnvironmentsResponse, error) + GetEnvironment(environmentID string) (*dtos.Environment, error) + CreateEnvironment(propertyID, name string, onlyMaintainersCanDeploy, httpRequestLogging bool) (*dtos.Environment, error) + UpdateEnvironment(environmentID, name string, onlyMaintainersCanDeploy, httpRequestLogging, preserveCache bool) (*dtos.Environment, error) + DeleteEnvironment(environmentID string) error + GetTlsCert(tlsCertId string) (*dtos.TLSCertResponse, error) + UploadTlsCert(req dtos.UploadTlsCertRequest) (*dtos.TLSCertResponse, error) + GenerateTlsCert(environmentId string) (*dtos.TLSCertResponse, error) + GetTlsCerts(page int, pageSize int, environmentID string) (*dtos.TLSCertSResponse, error) + UploadCdnConfiguration(config *dtos.CDNConfiguration) (*dtos.CDNConfiguration, error) + GetCDNConfiguration(configID string) (*dtos.CDNConfiguration, error) +} diff --git a/internal/workflow/node-processor/apply_node.go b/internal/workflow/node-processor/apply_node.go index d214ca63..7ed5861a 100644 --- a/internal/workflow/node-processor/apply_node.go +++ b/internal/workflow/node-processor/apply_node.go @@ -89,7 +89,7 @@ func (a *applyNode) Run(ctx context.Context) error { Outputs: a.node.Outputs, } - certX509, err := x509.ParseCertificateFromPEM(applyResult.Certificate) + certX509, err := x509.ParseCertificateFromPEM(applyResult.CertificateChain) if err != nil { a.AddOutput(ctx, a.node.Name, "解析证书失败", err.Error()) return err @@ -98,7 +98,7 @@ func (a *applyNode) Run(ctx context.Context) error { certificate := &domain.Certificate{ Source: domain.CertificateSourceTypeWorkflow, SubjectAltNames: strings.Join(certX509.DNSNames, ";"), - Certificate: applyResult.Certificate, + Certificate: applyResult.CertificateChain, PrivateKey: applyResult.PrivateKey, IssuerCertificate: applyResult.IssuerCertificate, ACMECertUrl: applyResult.ACMECertUrl, diff --git a/ui/public/imgs/providers/edgio.svg b/ui/public/imgs/providers/edgio.svg new file mode 100644 index 00000000..72f21b47 --- /dev/null +++ b/ui/public/imgs/providers/edgio.svg @@ -0,0 +1 @@ + diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index 040e52dd..afe49ccd 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -17,6 +17,7 @@ import AccessFormBaiduCloudConfig from "./AccessFormBaiduCloudConfig"; import AccessFormBytePlusConfig from "./AccessFormBytePlusConfig"; import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig"; import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; +import AccessFormEdgioConfig from "./AccessFormEdgioConfig"; import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; @@ -102,6 +103,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.GODADDY: return ; + case ACCESS_PROVIDERS.EDGIO: + return ; case ACCESS_PROVIDERS.HUAWEICLOUD: return ; case ACCESS_PROVIDERS.KUBERNETES: diff --git a/ui/src/components/access/AccessFormEdgioConfig.tsx b/ui/src/components/access/AccessFormEdgioConfig.tsx new file mode 100644 index 00000000..d70ece6e --- /dev/null +++ b/ui/src/components/access/AccessFormEdgioConfig.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 AccessConfigForEdgio } from "@/domain/access"; + +type AccessFormEdgioConfigFieldValues = Nullish; + +export type AccessFormEdgioConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormEdgioConfigFieldValues; + onValuesChange?: (values: AccessFormEdgioConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormEdgioConfigFieldValues => { + return { + clientId: "", + clientSecret: "", + }; +}; + +const AccessFormEdgioConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormEdgioConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + clientId: z + .string() + .min(1, t("access.form.edgio_client_id.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + clientSecret: z + .string() + .min(1, t("access.form.edgio_client_secret.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormEdgioConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index cbeb6c7a..77516024 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -25,6 +25,7 @@ import DeployNodeConfigFormAliyunOSSConfig from "./DeployNodeConfigFormAliyunOSS import DeployNodeConfigFormBaiduCloudCDNConfig from "./DeployNodeConfigFormBaiduCloudCDNConfig"; import DeployNodeConfigFormBytePlusCDNConfig from "./DeployNodeConfigFormBytePlusCDNConfig"; import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig"; +import DeployNodeConfigFormEdgioApplicationsConfig from "./DeployNodeConfigFormEdgioApplicationsConfig"; import DeployNodeConfigFormHuaweiCloudCDNConfig from "./DeployNodeConfigFormHuaweiCloudCDNConfig"; import DeployNodeConfigFormHuaweiCloudELBConfig from "./DeployNodeConfigFormHuaweiCloudELBConfig"; import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig"; @@ -134,6 +135,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOY_PROVIDERS.DOGECLOUD_CDN: return ; + case DEPLOY_PROVIDERS.EDGIO_APPLICATIONS: + return ; case DEPLOY_PROVIDERS.HUAWEICLOUD_CDN: return ; case DEPLOY_PROVIDERS.HUAWEICLOUD_ELB: diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormEdgioApplicationsConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormEdgioApplicationsConfig.tsx new file mode 100644 index 00000000..2a6929d8 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormEdgioApplicationsConfig.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"; + +type DeployNodeConfigFormEdgioApplicationsConfigFieldValues = Nullish<{ + environmentId: string; +}>; + +export type DeployNodeConfigFormEdgioApplicationsConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormEdgioApplicationsConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormEdgioApplicationsConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormEdgioApplicationsConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormEdgioApplicationsConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormEdgioApplicationsConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + environmentId: z + .string({ message: t("workflow_node.deploy.form.edgio_applications_environment_id.placeholder") }) + .min(1, t("workflow_node.deploy.form.edgio_applications_environment_id.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default DeployNodeConfigFormEdgioApplicationsConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index ed2bd310..0c0d6986 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -16,6 +16,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForBytePlus | AccessConfigForCloudflare | AccessConfigForDogeCloud + | AccessConfigForEdgio | AccessConfigForGoDaddy | AccessConfigForHuaweiCloud | AccessConfigForKubernetes @@ -77,6 +78,11 @@ export type AccessConfigForDogeCloud = { secretKey: string; }; +export type AccessConfigForEdgio = { + clientId: string; + clientSecret: string; +}; + export type AccessConfigForGoDaddy = { apiKey: string; apiSecret: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index ba1a10f2..574c022a 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -13,6 +13,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ CLOUDFLARE: "cloudflare", DOGECLOUD: "dogecloud", GODADDY: "godaddy", + EDGIO: "edgio", HUAWEICLOUD: "huaweicloud", KUBERNETES: "k8s", LOCAL: "local", @@ -64,6 +65,7 @@ export const accessProvidersMap: Map [ type, { diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index d609d6e7..47f59120 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -78,6 +78,12 @@ "access.form.dogecloud_secret_key.label": "Doge Cloud SecretKey", "access.form.dogecloud_secret_key.placeholder": "Please enter Doge Cloud SecretKey", "access.form.dogecloud_secret_key.tooltip": "For more information, see https://console.dogecloud.com/", + "access.form.edgio_client_id.label": "Edgio ClientId", + "access.form.edgio_client_id.placeholder": "Please enter Edgio ClientId", + "access.form.edgio_client_id.tooltip": "For more information, see https://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients", + "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 https://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients", "access.form.godaddy_api_key.label": "GoDaddy API key", "access.form.godaddy_api_key.placeholder": "Please enter GoDaddy API key", "access.form.godaddy_api_key.tooltip": "For more information, see https://developer.godaddy.com/", diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index 7b0199a9..53eeb515 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -56,6 +56,8 @@ "common.provider.cloudflare": "Cloudflare", "common.provider.dogecloud": "Doge Cloud", "common.provider.dogecloud.cdn": "Doge Cloud - Content Delivery Network (CDN)", + "common.provider.edgio": "Edgio", + "common.provider.edgio.applications": "Edgio - Applications", "common.provider.godaddy": "GoDaddy", "common.provider.huaweicloud": "Huawei Cloud", "common.provider.huaweicloud.cdn": "Huawei Cloud - Content Delivery Network (CDN)", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 7275d08b..89d8ae71 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -148,6 +148,9 @@ "workflow_node.deploy.form.dogecloud_cdn_domain.label": "Doge Cloud CDN domain", "workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "Please enter Doge Cloud CDN domain name", "workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "For more information, see https://console.dogecloud.com/", + "workflow_node.deploy.form.edgio_applications_environment_id.label": "Edgio Applications environment ID", + "workflow_node.deploy.form.edgio_applications_environment_id.placeholder": "Please enter Edgio Applications environment ID", + "workflow_node.deploy.form.edgio_applications_environment_id.tooltip": "For more information, see https://edgio.app/", "workflow_node.deploy.form.huaweicloud_cdn_region.label": "Huawei Cloud region", "workflow_node.deploy.form.huaweicloud_cdn_region.placeholder": "Please enter Huawei Cloud region (e.g. cn-north-1)", "workflow_node.deploy.form.huaweicloud_cdn_region.tooltip": "For more information, see https://console-intl.huaweicloud.com/apiexplorer/#/endpoint", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index b9107581..dc8d8c93 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -78,6 +78,12 @@ "access.form.dogecloud_secret_key.label": "多吉云 SecretKey", "access.form.dogecloud_secret_key.placeholder": "请输入多吉云 SecretKey", "access.form.dogecloud_secret_key.tooltip": "这是什么?请参阅 https://console.dogecloud.com/", + "access.form.edgio_client_id.label": "Edgio 客户端 ID", + "access.form.edgio_client_id.placeholder": "请输入 Edgio 客户端 ID", + "access.form.edgio_client_id.tooltip": "这是什么?请参阅 https://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients", + "access.form.edgio_client_secret.label": "Edgio 客户端密码", + "access.form.edgio_client_secret.placeholder": "请输入 Edgio 客户端密码", + "access.form.edgio_client_secret.tooltip": "这是什么?请参阅 https://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients", "access.form.godaddy_api_key.label": "GoDaddy API Key", "access.form.godaddy_api_key.placeholder": "请输入 GoDaddy API Key", "access.form.godaddy_api_key.tooltip": "这是什么?请参阅 https://developer.godaddy.com/", diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index 0951152a..87fce337 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -56,6 +56,8 @@ "common.provider.cloudflare": "Cloudflare", "common.provider.dogecloud": "多吉云", "common.provider.dogecloud.cdn": "多吉云 - 内容分发网络 CDN", + "common.provider.edgio": "Edgio", + "common.provider.edgio.applications": "Edgio - Applications", "common.provider.godaddy": "GoDaddy", "common.provider.huaweicloud": "华为云", "common.provider.huaweicloud.cdn": "华为云 - 内容分发网络 CDN", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 00b24d75..b6291e06 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -148,6 +148,9 @@ "workflow_node.deploy.form.dogecloud_cdn_domain.label": "多吉云 CDN 加速域名", "workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "请输入多吉云 CDN 加速域名", "workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.dogecloud.com", + "workflow_node.deploy.form.edgio_applications_environment_id.label": "Edgio Applications 环境 ID", + "workflow_node.deploy.form.edgio_applications_environment_id.placeholder": "请输入 Edgio Applications 环境 ID", + "workflow_node.deploy.form.edgio_applications_environment_id.tooltip": "这是什么?请参阅 https://edgio.app/", "workflow_node.deploy.form.huaweicloud_cdn_region.label": "华为云区域", "workflow_node.deploy.form.huaweicloud_cdn_region.placeholder": "请输入华为云区域(例如:cn-north-1)", "workflow_node.deploy.form.huaweicloud_cdn_region.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/apiexplorer/#/endpoint", From b657405e46325c4f132b550cf9f846896492a83a Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 15 Jan 2025 22:45:02 +0800 Subject: [PATCH 4/5] refactor: clean code --- internal/applicant/acme-user.go | 6 ++--- internal/applicant/applicant.go | 24 +++++++++---------- .../providers/k8s-secret/k8s_secret.go | 4 ++-- .../core/deployer/providers/local/local.go | 16 ++++++------- .../pkg/core/deployer/providers/ssh/ssh.go | 6 ++--- .../deployer/providers/webhook/webhook.go | 4 ++-- .../providers/aliyun-cas/aliyun_cas.go | 8 +++---- .../providers/aliyun-slb/aliyun_slb.go | 4 ++-- .../providers/byteplus-cdn/byteplus_cdn.go | 4 ++-- .../huaweicloud-elb/huaweicloud_elb.go | 8 +++---- .../huaweicloud-scm/huaweicloud_scm.go | 8 +++---- .../providers/qiniu-sslcert/qiniu_sslcert.go | 4 ++-- .../providers/ucloud-ussl/ucloud_ussl.go | 4 ++-- .../volcengine-cdn/volcengine_cdn.go | 4 ++-- .../volcengine-live/volcengine_live.go | 8 +++---- internal/pkg/utils/{x509 => certs}/common.go | 2 +- .../pkg/utils/{x509 => certs}/converter.go | 2 +- internal/pkg/utils/{x509 => certs}/parser.go | 2 +- .../pkg/utils/{x509 => certs}/transformer.go | 2 +- .../pkg/utils/{fs/fs.go => files/files.go} | 10 ++++---- .../workflow/node-processor/apply_node.go | 6 ++--- 21 files changed, 68 insertions(+), 68 deletions(-) rename internal/pkg/utils/{x509 => certs}/common.go (97%) rename internal/pkg/utils/{x509 => certs}/converter.go (97%) rename internal/pkg/utils/{x509 => certs}/parser.go (99%) rename internal/pkg/utils/{x509 => certs}/transformer.go (99%) rename internal/pkg/utils/{fs/fs.go => files/files.go} (79%) diff --git a/internal/applicant/acme-user.go b/internal/applicant/acme-user.go index 11aa1fff..3b74d5ca 100644 --- a/internal/applicant/acme-user.go +++ b/internal/applicant/acme-user.go @@ -11,7 +11,7 @@ import ( "github.com/go-acme/lego/v4/registration" "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" "github.com/usual2970/certimate/internal/repository" ) @@ -38,7 +38,7 @@ func newAcmeUser(ca, email string) (*acmeUser, error) { return nil, err } - keyPEM, err := x509.ConvertECPrivateKeyToPEM(key) + keyPEM, err := certs.ConvertECPrivateKeyToPEM(key) if err != nil { return nil, err } @@ -62,7 +62,7 @@ func (u acmeUser) GetRegistration() *registration.Resource { } func (u *acmeUser) GetPrivateKey() crypto.PrivateKey { - rs, _ := x509.ParseECPrivateKeyFromPEM(u.privkey) + rs, _ := certs.ParseECPrivateKeyFromPEM(u.privkey) return rs } diff --git a/internal/applicant/applicant.go b/internal/applicant/applicant.go index a89204c6..5b77c95f 100644 --- a/internal/applicant/applicant.go +++ b/internal/applicant/applicant.go @@ -20,12 +20,12 @@ import ( ) type ApplyCertResult struct { - CertificateChain string - IssuerCertificate string - PrivateKey string - ACMECertUrl string - ACMECertStableUrl string - CSR string + CertificateFullChain string + IssuerCertificate string + PrivateKey string + ACMECertUrl string + ACMECertStableUrl string + CSR string } type Applicant interface { @@ -150,12 +150,12 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap } return &ApplyCertResult{ - CertificateChain: strings.TrimSpace(string(certResource.Certificate)), - IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)), - PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)), - ACMECertUrl: certResource.CertURL, - ACMECertStableUrl: certResource.CertStableURL, - CSR: string(certResource.CSR), + CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)), + IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)), + PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)), + ACMECertUrl: certResource.CertURL, + ACMECertStableUrl: certResource.CertStableURL, + CSR: string(certResource.CSR), }, nil } 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 9e74e794..60020ec6 100644 --- a/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go +++ b/internal/pkg/core/deployer/providers/k8s-secret/k8s_secret.go @@ -14,7 +14,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" ) type K8sSecretDeployerConfig struct { @@ -75,7 +75,7 @@ func (d *K8sSecretDeployer) Deploy(ctx context.Context, certPem string, privkeyP return nil, errors.New("config `secretDataKeyForKey` is required") } - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } diff --git a/internal/pkg/core/deployer/providers/local/local.go b/internal/pkg/core/deployer/providers/local/local.go index 344936d9..e3c3d84e 100644 --- a/internal/pkg/core/deployer/providers/local/local.go +++ b/internal/pkg/core/deployer/providers/local/local.go @@ -12,8 +12,8 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" - "github.com/usual2970/certimate/internal/pkg/utils/fs" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" + "github.com/usual2970/certimate/internal/pkg/utils/files" ) type LocalDeployerConfig struct { @@ -84,41 +84,41 @@ func (d *LocalDeployer) Deploy(ctx context.Context, certPem string, privkeyPem s // 写入证书和私钥文件 switch d.config.OutputFormat { case OUTPUT_FORMAT_PEM: - if err := fs.WriteFileString(d.config.OutputCertPath, certPem); err != nil { + if err := files.WriteString(d.config.OutputCertPath, certPem); err != nil { return nil, xerrors.Wrap(err, "failed to save certificate file") } d.logger.Logt("certificate file saved") - if err := fs.WriteFileString(d.config.OutputKeyPath, privkeyPem); err != nil { + if err := files.WriteString(d.config.OutputKeyPath, privkeyPem); err != nil { return nil, xerrors.Wrap(err, "failed to save private key file") } d.logger.Logt("private key file saved") case OUTPUT_FORMAT_PFX: - pfxData, err := x509.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword) + pfxData, err := certs.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword) if err != nil { return nil, xerrors.Wrap(err, "failed to transform certificate to PFX") } d.logger.Logt("certificate transformed to PFX") - if err := fs.WriteFile(d.config.OutputCertPath, pfxData); err != nil { + if err := files.Write(d.config.OutputCertPath, pfxData); err != nil { return nil, xerrors.Wrap(err, "failed to save certificate file") } d.logger.Logt("certificate file saved") case OUTPUT_FORMAT_JKS: - jksData, err := x509.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass) + jksData, err := certs.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass) if err != nil { return nil, xerrors.Wrap(err, "failed to transform certificate to JKS") } d.logger.Logt("certificate transformed to JKS") - if err := fs.WriteFile(d.config.OutputCertPath, jksData); err != nil { + if err := files.Write(d.config.OutputCertPath, jksData); err != nil { return nil, xerrors.Wrap(err, "failed to save certificate file") } diff --git a/internal/pkg/core/deployer/providers/ssh/ssh.go b/internal/pkg/core/deployer/providers/ssh/ssh.go index 6d86434c..e09c35b6 100644 --- a/internal/pkg/core/deployer/providers/ssh/ssh.go +++ b/internal/pkg/core/deployer/providers/ssh/ssh.go @@ -14,7 +14,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" ) type SshDeployerConfig struct { @@ -125,7 +125,7 @@ func (d *SshDeployer) Deploy(ctx context.Context, certPem string, privkeyPem str d.logger.Logt("private key file uploaded") case OUTPUT_FORMAT_PFX: - pfxData, err := x509.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword) + pfxData, err := certs.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword) if err != nil { return nil, xerrors.Wrap(err, "failed to transform certificate to PFX") } @@ -139,7 +139,7 @@ func (d *SshDeployer) Deploy(ctx context.Context, certPem string, privkeyPem str d.logger.Logt("certificate file uploaded") case OUTPUT_FORMAT_JKS: - jksData, err := x509.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass) + jksData, err := certs.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass) if err != nil { return nil, xerrors.Wrap(err, "failed to transform certificate to JKS") } diff --git a/internal/pkg/core/deployer/providers/webhook/webhook.go b/internal/pkg/core/deployer/providers/webhook/webhook.go index d81da684..24d60494 100644 --- a/internal/pkg/core/deployer/providers/webhook/webhook.go +++ b/internal/pkg/core/deployer/providers/webhook/webhook.go @@ -14,7 +14,7 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/logger" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" ) type WebhookDeployerConfig struct { @@ -55,7 +55,7 @@ func NewWithLogger(config *WebhookDeployerConfig, logger logger.Logger) (*Webhoo } func (d *WebhookDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, xerrors.Wrap(err, "failed to parse x509") } 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 7649f618..ac71202e 100644 --- a/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go +++ b/internal/pkg/core/uploader/providers/aliyun-cas/aliyun_cas.go @@ -13,7 +13,7 @@ import ( xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" ) type AliyunCASUploaderConfig struct { @@ -54,7 +54,7 @@ func New(config *AliyunCASUploaderConfig) (*AliyunCASUploader, error) { func (u *AliyunCASUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } @@ -90,12 +90,12 @@ func (u *AliyunCASUploader) Upload(ctx context.Context, certPem string, privkeyP if *getUserCertificateDetailResp.Body.Cert == certPem { isSameCert = true } else { - oldCertX509, err := x509.ParseCertificateFromPEM(*getUserCertificateDetailResp.Body.Cert) + oldCertX509, err := certs.ParseCertificateFromPEM(*getUserCertificateDetailResp.Body.Cert) if err != nil { continue } - isSameCert = x509.EqualCertificate(certX509, oldCertX509) + isSameCert = certs.EqualCertificate(certX509, oldCertX509) } // 如果已存在相同证书,直接返回已有的证书信息 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 2df26ebc..63779b48 100644 --- a/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go +++ b/internal/pkg/core/uploader/providers/aliyun-slb/aliyun_slb.go @@ -16,7 +16,7 @@ import ( xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" ) type AliyunSLBUploaderConfig struct { @@ -57,7 +57,7 @@ func New(config *AliyunSLBUploaderConfig) (*AliyunSLBUploader, error) { func (u *AliyunSLBUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } 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 e564bba1..f1bf2b46 100644 --- a/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go +++ b/internal/pkg/core/uploader/providers/byteplus-cdn/byteplus_cdn.go @@ -14,7 +14,7 @@ import ( xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" ) type ByteplusCDNUploaderConfig struct { @@ -48,7 +48,7 @@ func New(config *ByteplusCDNUploaderConfig) (*ByteplusCDNUploader, error) { func (u *ByteplusCDNUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } 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 83ccc0b7..2ea7d031 100644 --- a/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go +++ b/internal/pkg/core/uploader/providers/huaweicloud-elb/huaweicloud_elb.go @@ -17,7 +17,7 @@ import ( xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" ) @@ -59,7 +59,7 @@ func New(config *HuaweiCloudELBUploaderConfig) (*HuaweiCloudELBUploader, error) func (u *HuaweiCloudELBUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } @@ -85,12 +85,12 @@ func (u *HuaweiCloudELBUploader) Upload(ctx context.Context, certPem string, pri if certDetail.Certificate == certPem { isSameCert = true } else { - oldCertX509, err := x509.ParseCertificateFromPEM(certDetail.Certificate) + oldCertX509, err := certs.ParseCertificateFromPEM(certDetail.Certificate) if err != nil { continue } - isSameCert = x509.EqualCertificate(certX509, oldCertX509) + isSameCert = certs.EqualCertificate(certX509, oldCertX509) } // 如果已存在相同证书,直接返回已有的证书信息 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 0c6721a1..c0618da0 100644 --- a/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go +++ b/internal/pkg/core/uploader/providers/huaweicloud-scm/huaweicloud_scm.go @@ -13,7 +13,7 @@ import ( xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk" ) @@ -55,7 +55,7 @@ func New(config *HuaweiCloudSCMUploaderConfig) (*HuaweiCloudSCMUploader, error) func (u *HuaweiCloudSCMUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } @@ -94,12 +94,12 @@ func (u *HuaweiCloudSCMUploader) Upload(ctx context.Context, certPem string, pri if *exportCertificateResp.Certificate == certPem { isSameCert = true } else { - oldCertX509, err := x509.ParseCertificateFromPEM(*exportCertificateResp.Certificate) + oldCertX509, err := certs.ParseCertificateFromPEM(*exportCertificateResp.Certificate) if err != nil { continue } - isSameCert = x509.EqualCertificate(certX509, oldCertX509) + isSameCert = certs.EqualCertificate(certX509, oldCertX509) } // 如果已存在相同证书,直接返回已有的证书信息 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 d5912988..a599cbe2 100644 --- a/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go +++ b/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go @@ -10,7 +10,7 @@ import ( "github.com/qiniu/go-sdk/v7/auth" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" qiniuEx "github.com/usual2970/certimate/internal/pkg/vendors/qiniu-sdk" ) @@ -49,7 +49,7 @@ func New(config *QiniuSSLCertUploaderConfig) (*QiniuSSLCertUploader, error) { func (u *QiniuSSLCertUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } 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 a8bc7173..223c108e 100644 --- a/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go +++ b/internal/pkg/core/uploader/providers/ucloud-ussl/ucloud_ussl.go @@ -16,7 +16,7 @@ import ( uAuth "github.com/ucloud/ucloud-sdk-go/ucloud/auth" "github.com/usual2970/certimate/internal/pkg/core/uploader" - x509util "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" usdkSsl "github.com/usual2970/certimate/internal/pkg/vendors/ucloud-sdk/ussl" ) @@ -94,7 +94,7 @@ func (u *UCloudUSSLUploader) Upload(ctx context.Context, certPem string, privkey func (u *UCloudUSSLUploader) getExistCert(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := x509util.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } 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 7a766895..23f05281 100644 --- a/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go +++ b/internal/pkg/core/uploader/providers/volcengine-cdn/volcengine_cdn.go @@ -15,7 +15,7 @@ import ( ve "github.com/volcengine/volcengine-go-sdk/volcengine" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" ) type VolcEngineCDNUploaderConfig struct { @@ -49,7 +49,7 @@ func New(config *VolcEngineCDNUploaderConfig) (*VolcEngineCDNUploader, error) { func (u *VolcEngineCDNUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } 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 b71c5d9d..c7861cb6 100644 --- a/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go +++ b/internal/pkg/core/uploader/providers/volcengine-live/volcengine_live.go @@ -12,7 +12,7 @@ import ( ve "github.com/volcengine/volcengine-go-sdk/volcengine" "github.com/usual2970/certimate/internal/pkg/core/uploader" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" ) type VolcEngineLiveUploaderConfig struct { @@ -46,7 +46,7 @@ func New(config *VolcEngineLiveUploaderConfig) (*VolcEngineLiveUploader, error) func (u *VolcEngineLiveUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { // 解析证书内容 - certX509, err := x509.ParseCertificateFromPEM(certPem) + certX509, err := certs.ParseCertificateFromPEM(certPem) if err != nil { return nil, err } @@ -75,12 +75,12 @@ func (u *VolcEngineLiveUploader) Upload(ctx context.Context, certPem string, pri if certificate == certPem { isSameCert = true } else { - oldCertX509, err := x509.ParseCertificateFromPEM(certificate) + oldCertX509, err := certs.ParseCertificateFromPEM(certificate) if err != nil { continue } - isSameCert = x509.EqualCertificate(certX509, oldCertX509) + isSameCert = certs.EqualCertificate(certX509, oldCertX509) } // 如果已存在相同证书,直接返回已有的证书信息 diff --git a/internal/pkg/utils/x509/common.go b/internal/pkg/utils/certs/common.go similarity index 97% rename from internal/pkg/utils/x509/common.go rename to internal/pkg/utils/certs/common.go index f5557962..fe5d041a 100644 --- a/internal/pkg/utils/x509/common.go +++ b/internal/pkg/utils/certs/common.go @@ -1,4 +1,4 @@ -package x509 +package certs import ( "crypto/x509" diff --git a/internal/pkg/utils/x509/converter.go b/internal/pkg/utils/certs/converter.go similarity index 97% rename from internal/pkg/utils/x509/converter.go rename to internal/pkg/utils/certs/converter.go index c5522f27..a3272c45 100644 --- a/internal/pkg/utils/x509/converter.go +++ b/internal/pkg/utils/certs/converter.go @@ -1,4 +1,4 @@ -package x509 +package certs import ( "crypto/ecdsa" diff --git a/internal/pkg/utils/x509/parser.go b/internal/pkg/utils/certs/parser.go similarity index 99% rename from internal/pkg/utils/x509/parser.go rename to internal/pkg/utils/certs/parser.go index d9604526..d03d6395 100644 --- a/internal/pkg/utils/x509/parser.go +++ b/internal/pkg/utils/certs/parser.go @@ -1,4 +1,4 @@ -package x509 +package certs import ( "crypto/ecdsa" diff --git a/internal/pkg/utils/x509/transformer.go b/internal/pkg/utils/certs/transformer.go similarity index 99% rename from internal/pkg/utils/x509/transformer.go rename to internal/pkg/utils/certs/transformer.go index 6170d88a..6918a3b4 100644 --- a/internal/pkg/utils/x509/transformer.go +++ b/internal/pkg/utils/certs/transformer.go @@ -1,4 +1,4 @@ -package x509 +package certs import ( "bytes" diff --git a/internal/pkg/utils/fs/fs.go b/internal/pkg/utils/files/files.go similarity index 79% rename from internal/pkg/utils/fs/fs.go rename to internal/pkg/utils/files/files.go index 47b0cafb..654bf164 100644 --- a/internal/pkg/utils/fs/fs.go +++ b/internal/pkg/utils/files/files.go @@ -1,4 +1,4 @@ -package fs +package files import ( "os" @@ -7,7 +7,7 @@ import ( xerrors "github.com/pkg/errors" ) -// 与 [WriteFile] 类似,但写入的是字符串内容。 +// 与 [Write] 类似,但写入的是字符串内容。 // // 入参: // - path: 文件路径。 @@ -15,8 +15,8 @@ import ( // // 出参: // - 错误。 -func WriteFileString(path string, content string) error { - return WriteFile(path, []byte(content)) +func WriteString(path string, content string) error { + return Write(path, []byte(content)) } // 将数据写入指定路径的文件。 @@ -29,7 +29,7 @@ func WriteFileString(path string, content string) error { // // 出参: // - 错误。 -func WriteFile(path string, data []byte) error { +func Write(path string, data []byte) error { dir := filepath.Dir(path) err := os.MkdirAll(dir, os.ModePerm) diff --git a/internal/workflow/node-processor/apply_node.go b/internal/workflow/node-processor/apply_node.go index 7ed5861a..a08e0c3d 100644 --- a/internal/workflow/node-processor/apply_node.go +++ b/internal/workflow/node-processor/apply_node.go @@ -7,7 +7,7 @@ import ( "github.com/usual2970/certimate/internal/applicant" "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/utils/x509" + "github.com/usual2970/certimate/internal/pkg/utils/certs" "github.com/usual2970/certimate/internal/repository" ) @@ -89,7 +89,7 @@ func (a *applyNode) Run(ctx context.Context) error { Outputs: a.node.Outputs, } - certX509, err := x509.ParseCertificateFromPEM(applyResult.CertificateChain) + certX509, err := certs.ParseCertificateFromPEM(applyResult.CertificateFullChain) if err != nil { a.AddOutput(ctx, a.node.Name, "解析证书失败", err.Error()) return err @@ -98,7 +98,7 @@ func (a *applyNode) Run(ctx context.Context) error { certificate := &domain.Certificate{ Source: domain.CertificateSourceTypeWorkflow, SubjectAltNames: strings.Join(certX509.DNSNames, ";"), - Certificate: applyResult.CertificateChain, + Certificate: applyResult.CertificateFullChain, PrivateKey: applyResult.PrivateKey, IssuerCertificate: applyResult.IssuerCertificate, ACMECertUrl: applyResult.ACMECertUrl, From d712f07b96f6498a4da5f9d274f74680fec55b8f Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 15 Jan 2025 23:04:43 +0800 Subject: [PATCH 5/5] refactor: reimplement webhook deployer --- go.mod | 2 -- go.sum | 7 ---- .../deployer/providers/webhook/webhook.go | 34 ++++++++----------- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index e6ba637a..89e2287e 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,6 @@ require ( github.com/go-acme/lego/v4 v4.21.0 github.com/go-resty/resty/v2 v2.16.3 github.com/go-viper/mapstructure/v2 v2.2.1 - github.com/gojek/heimdall/v7 v7.0.3 github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.128 github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 github.com/nikoksr/notify v1.1.0 @@ -143,7 +142,6 @@ require ( github.com/go-jose/go-jose/v4 v4.0.4 // indirect github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect github.com/goccy/go-json v0.10.4 // indirect - github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf // indirect github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/go-querystring v1.1.0 // indirect diff --git a/go.sum b/go.sum index 52df7389..eda86588 100644 --- a/go.sum +++ b/go.sum @@ -81,7 +81,6 @@ 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/DataDog/datadog-go v3.7.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3 h1:cb3br57K508pQEFgBxn9GDhPS9HefpyMPK1RzmtMNzk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.3/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.3 h1:xir5X8TS8UBVPWg2jHL+cSTf0jZgqYQSA54TscSt1/0= @@ -263,7 +262,6 @@ github.com/blinkbean/dingtalk v1.1.3 h1:MbidFZYom7DTFHD/YIs+eaI7kRy52kmWE/sy0xjo github.com/blinkbean/dingtalk v1.1.3/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto= github.com/byteplus-sdk/byteplus-sdk-golang v1.0.35 h1:bM18V4iw9ylRc2LahQaq3k3gjEVJdyQYvptLVZaCa54= github.com/byteplus-sdk/byteplus-sdk-golang v1.0.35/go.mod h1:7iCaE+dR9EycrJU0GQyMhptbInLbQhsKXiDKDjNi8Vs= -github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= @@ -420,10 +418,6 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j 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= -github.com/gojek/heimdall/v7 v7.0.3 h1:+5sAhl8S0m+qRRL8IVeHCJudFh/XkG3wyO++nvOg+gc= -github.com/gojek/heimdall/v7 v7.0.3/go.mod h1:Z43HtMid7ysSjmsedPTXAki6jcdcNVnjn5pmsTyiMic= -github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf h1:5xRGbUdOmZKoDXkGx5evVLehuCMpuO1hl701bEQqXOM= -github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf/go.mod h1:QzhUKaYKJmcbTnCYCAVQrroCOY7vOOI8cSQ4NbuhYf0= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= @@ -809,7 +803,6 @@ github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5J github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= diff --git a/internal/pkg/core/deployer/providers/webhook/webhook.go b/internal/pkg/core/deployer/providers/webhook/webhook.go index 24d60494..694befc5 100644 --- a/internal/pkg/core/deployer/providers/webhook/webhook.go +++ b/internal/pkg/core/deployer/providers/webhook/webhook.go @@ -1,15 +1,13 @@ package webhook import ( - "bytes" "context" "encoding/json" "errors" - "io" "strings" "time" - "github.com/gojek/heimdall/v7/httpclient" + "github.com/go-resty/resty/v2" xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/core/deployer" @@ -27,7 +25,7 @@ type WebhookDeployerConfig struct { type WebhookDeployer struct { config *WebhookDeployerConfig logger logger.Logger - httpClient *httpclient.Client + httpClient *resty.Client } var _ deployer.Deployer = (*WebhookDeployer)(nil) @@ -45,7 +43,10 @@ func NewWithLogger(config *WebhookDeployerConfig, logger logger.Logger) (*Webhoo return nil, errors.New("logger is nil") } - client := httpclient.NewClient(httpclient.WithHTTPTimeout(30 * time.Second)) + client := resty.New(). + SetTimeout(30 * time.Second). + SetRetryCount(3). + SetRetryWaitTime(5 * time.Second) return &WebhookDeployer{ config: config, @@ -72,25 +73,20 @@ func (d *WebhookDeployer) Deploy(ctx context.Context, certPem string, privkeyPem replaceJsonValueRecursively(webhookData, "${CERTIFICATE}", certPem) replaceJsonValueRecursively(webhookData, "${PRIVATE_KEY}", privkeyPem) - reqBody, _ := json.Marshal(&webhookData) - resp, err := d.httpClient.Post(d.config.WebhookUrl, bytes.NewReader(reqBody), map[string][]string{"Content-Type": {"application/json"}}) + resp, err := d.httpClient.R(). + SetContext(ctx). + SetHeader("Content-Type", "application/json"). + SetBody(webhookData). + Post(d.config.WebhookUrl) if err != nil { return nil, xerrors.Wrap(err, "failed to send webhook request") - } - defer resp.Body.Close() - - respBody, err := io.ReadAll(resp.Body) - if err != nil { - return nil, xerrors.Wrap(err, "failed to read response body") + } else if resp.StatusCode() != 200 { + return nil, xerrors.Errorf("unexpected webhook response status code: %d", resp.StatusCode()) } - d.logger.Logt("Webhook Response", string(respBody)) + d.logger.Logt("Webhook request sent", resp.String()) - return &deployer.DeployResult{ - ExtendedData: map[string]any{ - "responseText": string(respBody), - }, - }, nil + return &deployer.DeployResult{}, nil } func replaceJsonValueRecursively(data interface{}, oldStr, newStr string) interface{} {