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 00000000..3808329a Binary files /dev/null and b/ui/public/imgs/providers/gcore.png differ diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index e10d2a89..0b5632c3 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -21,6 +21,7 @@ import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig"; import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig"; import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; import AccessFormEdgioConfig from "./AccessFormEdgioConfig"; +import AccessFormGcoreConfig from "./AccessFormGcoreConfig"; import AccessFormGnameConfig from "./AccessFormGnameConfig"; import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; @@ -114,6 +115,8 @@ const AccessForm = forwardRef(({ 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",