diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index d4d630c4..90a3cf72 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -28,6 +28,7 @@ import ( pNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom" pNameSilo "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo" pNetcup "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/netcup" + pNetlify "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/netlify" pNS1 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1" pPorkbun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/porkbun" pPowerDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns" @@ -420,6 +421,21 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi return applicant, err } + case domain.ACMEDns01ProviderTypeNetlify: + { + access := domain.AccessConfigForNetlify{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pNetlify.NewChallengeProvider(&pNetlify.ChallengeProviderConfig{ + ApiToken: access.ApiToken, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ACMEDns01ProviderTypeNS1: { access := domain.AccessConfigForNS1{} diff --git a/internal/domain/access.go b/internal/domain/access.go index c18f846e..35dd9b0a 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -205,6 +205,10 @@ type AccessConfigForNetcup struct { ApiPassword string `json:"apiPassword"` } +type AccessConfigForNetlify struct { + ApiToken string `json:"apiToken"` +} + type AccessConfigForNS1 struct { ApiKey string `json:"apiKey"` } diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 4de70cd3..2c80f6cb 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -53,6 +53,7 @@ const ( AccessProviderTypeNameDotCom = AccessProviderType("namedotcom") AccessProviderTypeNameSilo = AccessProviderType("namesilo") AccessProviderTypeNetcup = AccessProviderType("netcup") + AccessProviderTypeNetlify = AccessProviderType("netlify") AccessProviderTypeNS1 = AccessProviderType("ns1") AccessProviderTypePorkbun = AccessProviderType("porkbun") AccessProviderTypePowerDNS = AccessProviderType("powerdns") @@ -132,6 +133,7 @@ const ( ACMEDns01ProviderTypeNameDotCom = ACMEDns01ProviderType(AccessProviderTypeNameDotCom) ACMEDns01ProviderTypeNameSilo = ACMEDns01ProviderType(AccessProviderTypeNameSilo) ACMEDns01ProviderTypeNetcup = ACMEDns01ProviderType(AccessProviderTypeNetcup) + ACMEDns01ProviderTypeNetlify = ACMEDns01ProviderType(AccessProviderTypeNetlify) ACMEDns01ProviderTypeNS1 = ACMEDns01ProviderType(AccessProviderTypeNS1) ACMEDns01ProviderTypePorkbun = ACMEDns01ProviderType(AccessProviderTypePorkbun) ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS) diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/netlify/netlify.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/netlify/netlify.go new file mode 100644 index 00000000..f590372b --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/netlify/netlify.go @@ -0,0 +1,36 @@ +package netcup + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/netlify" +) + +type ChallengeProviderConfig struct { + ApiToken string `json:"apiToken"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := netlify.NewDefaultConfig() + providerConfig.Token = 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 := netlify.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/netlify.png b/ui/public/imgs/providers/netlify.png new file mode 100644 index 00000000..a04eeeba Binary files /dev/null and b/ui/public/imgs/providers/netlify.png differ diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index b7d374d3..0dd2828a 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -47,6 +47,7 @@ import AccessFormNamecheapConfig from "./AccessFormNamecheapConfig"; import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig"; import AccessFormNameSiloConfig from "./AccessFormNameSiloConfig"; import AccessFormNetcupConfig from "./AccessFormNetcupConfig"; +import AccessFormNetlifyConfig from "./AccessFormNetlifyConfig"; import AccessFormNS1Config from "./AccessFormNS1Config"; import AccessFormPorkbunConfig from "./AccessFormPorkbunConfig"; import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig"; @@ -245,6 +246,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.NETCUP: return ; + case ACCESS_PROVIDERS.NETLIFY: + return ; case ACCESS_PROVIDERS.NS1: return ; case ACCESS_PROVIDERS.PORKBUN: diff --git a/ui/src/components/access/AccessFormNetlifyConfig.tsx b/ui/src/components/access/AccessFormNetlifyConfig.tsx new file mode 100644 index 00000000..7fa4c8ae --- /dev/null +++ b/ui/src/components/access/AccessFormNetlifyConfig.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForNetlify } from "@/domain/access"; + +type AccessFormNetlifyConfigFieldValues = Nullish; + +export type AccessFormNetlifyConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormNetlifyConfigFieldValues; + onValuesChange?: (values: AccessFormNetlifyConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormNetlifyConfigFieldValues => { + return { + apiToken: "", + }; +}; + +const AccessFormNetlifyConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormNetlifyConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiToken: z.string().nonempty(t("access.form.netlify_api_token.placeholder")).trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormNetlifyConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index e0cce59d..0d516385 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -42,6 +42,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForNameDotCom | AccessConfigForNameSilo | AccessConfigForNetcup + | AccessConfigForNetlify | AccessConfigForPorkbun | AccessConfigForPowerDNS | AccessConfigForProxmoxVE @@ -256,6 +257,10 @@ export type AccessConfigForNetcup = { apiPassword: string; }; +export type AccessConfigForNetlify = { + apiToken: string; +}; + export type AccessConfigForNS1 = { apiKey: string; }; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 5cc50534..12afaa85 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -44,6 +44,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ NAMEDOTCOM: "namedotcom", NAMESILO: "namesilo", NETCUP: "netcup", + NETLIFY: "netlify", NS1: "ns1", PORKBUN: "porkbun", POWERDNS: "powerdns", @@ -133,8 +134,9 @@ export const accessProvidersMap: Maphttps://www.namesilo.com/support/v2/articles/account-options/api-manager", + "access.form.netlify_api_token.label": "netlify API token", + "access.form.netlify_api_token.placeholder": "Please enter netlify API token", + "access.form.netlify_api_token.tooltip": "For more information, see https://docs.netlify.com/api/get-started/#authentication", "access.form.netcup_customer_number.label": "netcup customer number", "access.form.netcup_customer_number.placeholder": "Please enter netcup customer number", "access.form.netcup_customer_number.tooltip": "For more information, see https://helpcenter.netcup.com/en/wiki/general/ccp-login/", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index 1f7e5589..30c818dd 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -91,6 +91,7 @@ "provider.namedotcom": "Name.com", "provider.namesilo": "NameSilo", "provider.netcup": "netcup", + "provider.netlify": "netlify", "provider.ns1": "NS1 (IBM NS1 Connect)", "provider.porkbun": "Porkbun", "provider.powerdns": "PowerDNS", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index fd993178..f73065ea 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -256,6 +256,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.netlify_api_token.label": "netlify API Token", + "access.form.netlify_api_token.placeholder": "请输入 netlify API Token", + "access.form.netlify_api_token.tooltip": "这是什么?请参阅 https://docs.netlify.com/api/get-started/#authentication", "access.form.netcup_customer_number.label": "netcup 客户编号", "access.form.netcup_customer_number.placeholder": "请输入 netcup 客户编号", "access.form.netcup_customer_number.tooltip": "这是什么?请参阅 https://helpcenter.netcup.com/en/wiki/general/ccp-login/", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index 739fcebf..bf9470d0 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -91,6 +91,7 @@ "provider.namedotcom": "Name.com", "provider.namesilo": "NameSilo", "provider.netcup": "netcup", + "provider.netlify": "netlify", "provider.ns1": "NS1 (IBM NS1 Connect)", "provider.porkbun": "Porkbun", "provider.powerdns": "PowerDNS",