From e2a148c25f22aef5764586b2cc6f31205a1306b4 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 17 Feb 2025 20:48:32 +0800 Subject: [PATCH] feat: add gcore dns-01 applicant --- README.md | 1 + README_EN.md | 1 + internal/applicant/providers.go | 16 +++++ internal/domain/access.go | 4 ++ internal/domain/provider.go | 3 +- .../acme-dns-01/lego-providers/gcore/gcore.go | 37 +++++++++++ ui/public/imgs/providers/gcore.png | Bin 0 -> 8101 bytes ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormGcoreConfig.tsx | 61 ++++++++++++++++++ ui/src/domain/access.ts | 5 ++ ui/src/domain/provider.ts | 4 ++ ui/src/i18n/locales/en/nls.access.json | 3 + ui/src/i18n/locales/zh/nls.access.json | 3 + 13 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore/gcore.go create mode 100644 ui/public/imgs/providers/gcore.png create mode 100644 ui/src/components/access/AccessFormGcoreConfig.tsx diff --git a/README.md b/README.md index 32b5ce17..1768b501 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ make local.run | [Azure](https://azure.microsoft.com/) | | | [CloudFlare](https://www.cloudflare.com/) | | | [ClouDNS](https://www.cloudns.net//) | | +| [Gcore](https://gcore.com/) | | | [GNAME](https://www.gname.com/) | | | [GoDaddy](https://www.godaddy.com/) | | | [Name.com](https://www.name.com/) | | diff --git a/README_EN.md b/README_EN.md index 1966512b..a13509a8 100644 --- a/README_EN.md +++ b/README_EN.md @@ -95,6 +95,7 @@ The following DNS providers are supported: | [Azure DNS](https://azure.microsoft.com/) | | | [CloudFlare](https://www.cloudflare.com/) | | | [ClouDNS](https://www.cloudns.net//) | | +| [Gcore](https://gcore.com/) | | | [GNAME](https://www.gname.com/) | | | [GoDaddy](https://www.godaddy.com/) | | | [Name.com](https://www.name.com/) | | diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index 1866637e..6c89b794 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -12,6 +12,7 @@ import ( pAzureDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns" pCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare" pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns" + pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore" pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname" pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy" pHuaweiCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud" @@ -132,6 +133,21 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) { return applicant, err } + case domain.ApplyDNSProviderTypeGcore: + { + access := domain.AccessConfigForGcore{} + if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pGcore.NewChallengeProvider(&pGcore.GcoreApplicantConfig{ + ApiToken: access.ApiToken, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ApplyDNSProviderTypeGname: { access := domain.AccessConfigForGname{} diff --git a/internal/domain/access.go b/internal/domain/access.go index d4f4f714..4136a276 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -86,6 +86,10 @@ type AccessConfigForEdgio struct { ClientSecret string `json:"clientSecret"` } +type AccessConfigForGcore struct { + ApiToken string `json:"apiToken"` +} + type AccessConfigForGname struct { AppId string `json:"appId"` AppKey string `json:"appKey"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 70b90af5..8fe8da0f 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -30,7 +30,7 @@ const ( AccessProviderTypeEdgio = AccessProviderType("edgio") AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留) AccessProviderTypeGname = AccessProviderType("gname") - AccessProviderTypeGcore = AccessProviderType("gcore") // Gcore(预留) + AccessProviderTypeGcore = AccessProviderType("gcore") AccessProviderTypeGoDaddy = AccessProviderType("godaddy") AccessProviderTypeGoEdge = AccessProviderType("goedge") // GoEdge(预留) AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") @@ -69,6 +69,7 @@ const ( ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns") ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare") ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns") + ApplyDNSProviderTypeGcore = ApplyDNSProviderType("gcore") ApplyDNSProviderTypeGname = ApplyDNSProviderType("gname") ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy") ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS] diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore/gcore.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore/gcore.go new file mode 100644 index 00000000..1179d95d --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore/gcore.go @@ -0,0 +1,37 @@ +package gcore + +import ( + "errors" + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/gcore" +) + +type GcoreApplicantConfig struct { + ApiToken string `json:"apiToken"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *GcoreApplicantConfig) (challenge.Provider, error) { + if config == nil { + return nil, errors.New("config is nil") + } + + providerConfig := gcore.NewDefaultConfig() + providerConfig.APIToken = config.ApiToken + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := gcore.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/gcore.png b/ui/public/imgs/providers/gcore.png new file mode 100644 index 0000000000000000000000000000000000000000..3808329abe3cc982de7bfe21486b5e9c2c288986 GIT binary patch literal 8101 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 className, return ; case ACCESS_PROVIDERS.DOGECLOUD: return ; + case ACCESS_PROVIDERS.GCORE: + return ; case ACCESS_PROVIDERS.GNAME: return ; case ACCESS_PROVIDERS.GODADDY: diff --git a/ui/src/components/access/AccessFormGcoreConfig.tsx b/ui/src/components/access/AccessFormGcoreConfig.tsx new file mode 100644 index 00000000..8619f7fe --- /dev/null +++ b/ui/src/components/access/AccessFormGcoreConfig.tsx @@ -0,0 +1,61 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForGcore } from "@/domain/access"; + +type AccessFormGcoreConfigFieldValues = Nullish; + +export type AccessFormGcoreConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormGcoreConfigFieldValues; + onValuesChange?: (values: AccessFormGcoreConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormGcoreConfigFieldValues => { + return { + apiToken: "", + }; +}; + +const AccessFormGcoreConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormGcoreConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiToken: z + .string() + .min(1, t("access.form.gcore_api_token.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormGcoreConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 548716b4..1779c64c 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -18,6 +18,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForClouDNS | AccessConfigForDogeCloud | AccessConfigForEdgio + | AccessConfigForGcore | AccessConfigForGname | AccessConfigForGoDaddy | AccessConfigForHuaweiCloud @@ -101,6 +102,10 @@ export type AccessConfigForEdgio = { clientSecret: string; }; +export type AccessConfigForGcore = { + apiToken: string; +}; + export type AccessConfigForGname = { appId: string; appKey: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 75b954eb..4ccd7f7b 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -15,6 +15,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ CLOUDFLARE: "cloudflare", CLOUDNS: "cloudns", DOGECLOUD: "dogecloud", + GCORE: "gcore", GNAME: "gname", GODADDY: "godaddy", EDGIO: "edgio", @@ -79,6 +80,7 @@ export const accessProvidersMap: Maphttps://docs.edg.io/applications/v7/rest_api/authentication#administering-api-clients", + "access.form.gcore_api_token.label": "Gcore API token", + "access.form.gcore_api_token.placeholder": "Please enter Gcore API token", + "access.form.gcore_api_token.tooltip": "For more information, see https://api.gcore.com/docs/iam#section/Authentication", "access.form.gname_app_id.label": "GNAME AppId", "access.form.gname_app_id.placeholder": "Please enter GNAME AppId", "access.form.gname_app_id.tooltip": "For more information, see https://www.gname.com/user#/dealer_api", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index cca17a8d..e3ae142c 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -100,6 +100,9 @@ "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.gcore_api_token.label": "Gcore API Token", + "access.form.gcore_api_token.placeholder": "请输入 Gcore API Token", + "access.form.gcore_api_token.tooltip": "这是什么?请参阅 https://api.gcore.com/docs/iam#section/Authentication", "access.form.gname_app_id.label": "GNAME AppId", "access.form.gname_app_id.placeholder": "请输入 GNAME AppId", "access.form.gname_app_id.tooltip": "这是什么?请参阅 https://www.gname.com/user#/dealer_api",