diff --git a/go.mod b/go.mod
index 8623edb0..7c8723af 100644
--- a/go.mod
+++ b/go.mod
@@ -99,14 +99,18 @@ require (
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
+ github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
+ github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
+ github.com/nrdcg/desec v0.10.0 // indirect
github.com/nrdcg/mailinabox v0.2.0 // indirect
github.com/nrdcg/porkbun v0.4.0 // indirect
+ github.com/peterhellberg/link v1.2.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/qiniu/dyn v1.3.0 // indirect
github.com/qiniu/x v1.10.5 // indirect
diff --git a/go.sum b/go.sum
index 41ce2a0e..98e8211f 100644
--- a/go.sum
+++ b/go.sum
@@ -494,6 +494,7 @@ github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOj
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
@@ -503,6 +504,8 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
+github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
+github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@@ -646,6 +649,8 @@ github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nikoksr/notify v1.3.0 h1:UxzfxzAYGQD9a5JYLBTVx0lFMxeHCke3rPCkfWdPgLs=
github.com/nikoksr/notify v1.3.0/go.mod h1:Xor2hMmkvrCfkCKvXGbcrESez4brac2zQjhd6U2BbeM=
+github.com/nrdcg/desec v0.10.0 h1:qrEDiqnsvNU9QE7lXIXi/tIHAfyaFXKxF2/8/52O8uM=
+github.com/nrdcg/desec v0.10.0/go.mod h1:5+4vyhMRTs49V9CNoODF/HwT8Mwxv9DJ6j+7NekUnBs=
github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk=
github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc=
github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=
@@ -677,6 +682,8 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 h1:2nosf3P75OZv2/ZO/9Px5ZgZ5gbKrzA3joN1QMfOGMQ=
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ=
github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=
+github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=
+github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go
index 5119f86d..0af5422c 100644
--- a/internal/applicant/providers.go
+++ b/internal/applicant/providers.go
@@ -14,6 +14,7 @@ import (
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"
pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud"
+ pDeSEC "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec"
pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla"
pDynv6 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6"
pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore"
@@ -172,6 +173,21 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
+ case domain.ApplyDNSProviderTypeDeSEC:
+ {
+ access := domain.AccessConfigForDeSEC{}
+ if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ applicant, err := pDeSEC.NewChallengeProvider(&pDeSEC.ChallengeProviderConfig{
+ Token: access.Token,
+ DnsPropagationTimeout: options.DnsPropagationTimeout,
+ DnsTTL: options.DnsTTL,
+ })
+ return applicant, err
+ }
+
case domain.ApplyDNSProviderTypeDNSLA:
{
access := domain.AccessConfigForDNSLA{}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index 60726bd6..ac4cc71c 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -98,6 +98,10 @@ type AccessConfigForCMCCCloud struct {
AccessKeySecret string `json:"accessKeySecret"`
}
+type AccessConfigForDeSEC struct {
+ Token string `json:"token"`
+}
+
type AccessConfigForDNSLA struct {
ApiId string `json:"apiId"`
ApiSecret string `json:"apiSecret"`
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index 7b3a8770..64013967 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -26,7 +26,7 @@ const (
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留)
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留)
- AccessProviderTypeDeSEC = AccessProviderType("desec") // deSEC(预留)
+ AccessProviderTypeDeSEC = AccessProviderType("desec")
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
@@ -44,7 +44,7 @@ const (
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
AccessProviderTypeNameSilo = AccessProviderType("namesilo")
AccessProviderTypeNS1 = AccessProviderType("ns1")
- AccessProviderTypePorkbun = AccessProviderType("porkbun") // Porkbun(预留)
+ AccessProviderTypePorkbun = AccessProviderType("porkbun")
AccessProviderTypePowerDNS = AccessProviderType("powerdns")
AccessProviderTypeQiniu = AccessProviderType("qiniu")
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec/desec.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec/desec.go
new file mode 100644
index 00000000..7a997117
--- /dev/null
+++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec/desec.go
@@ -0,0 +1,36 @@
+package desec
+
+import (
+ "time"
+
+ "github.com/go-acme/lego/v4/challenge"
+ "github.com/go-acme/lego/v4/providers/dns/desec"
+)
+
+type ChallengeProviderConfig struct {
+ Token string `json:"token"`
+ 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 := desec.NewDefaultConfig()
+ providerConfig.Token = config.Token
+ if config.DnsPropagationTimeout != 0 {
+ providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
+ }
+ if config.DnsTTL != 0 {
+ providerConfig.TTL = int(config.DnsTTL)
+ }
+
+ provider, err := desec.NewDNSProviderConfig(providerConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ return provider, nil
+}
diff --git a/ui/public/imgs/providers/desec.svg b/ui/public/imgs/providers/desec.svg
new file mode 100644
index 00000000..b20baa59
--- /dev/null
+++ b/ui/public/imgs/providers/desec.svg
@@ -0,0 +1,87 @@
+
+
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index 6a948b9a..dbf63692 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -23,6 +23,7 @@ import AccessFormCdnflyConfig from "./AccessFormCdnflyConfig";
import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig";
import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig";
import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig";
+import AccessFormDeSECConfig from "./AccessFormDeSECConfig";
import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig";
import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig";
import AccessFormDynv6Config from "./AccessFormDynv6Config";
@@ -131,6 +132,8 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.CMCCCLOUD:
return ;
+ case ACCESS_PROVIDERS.DESEC:
+ return ;
case ACCESS_PROVIDERS.DNSLA:
return ;
case ACCESS_PROVIDERS.DOGECLOUD:
diff --git a/ui/src/components/access/AccessFormDeSECConfig.tsx b/ui/src/components/access/AccessFormDeSECConfig.tsx
new file mode 100644
index 00000000..4ba65a69
--- /dev/null
+++ b/ui/src/components/access/AccessFormDeSECConfig.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 AccessConfigForDeSEC } from "@/domain/access";
+
+type AccessFormDeSECConfigFieldValues = Nullish;
+
+export type AccessFormDeSECConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormDeSECConfigFieldValues;
+ onValuesChange?: (values: AccessFormDeSECConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormDeSECConfigFieldValues => {
+ return {
+ token: "",
+ };
+};
+
+const AccessFormDeSECConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDeSECConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ token: z
+ .string()
+ .min(1, t("access.form.desec_token.placeholder"))
+ .max(256, t("common.errmsg.string_max", { max: 256 }))
+ .trim(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+ }
+ >
+
+
+
+ );
+};
+
+export default AccessFormDeSECConfig;
diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts
index 9c33ff4e..0bbab303 100644
--- a/ui/src/domain/access.ts
+++ b/ui/src/domain/access.ts
@@ -20,6 +20,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForCloudflare
| AccessConfigForClouDNS
| AccessConfigForCMCCCloud
+ | AccessConfigForDeSEC
| AccessConfigForDNSLA
| AccessConfigForDogeCloud
| AccessConfigForDynv6
@@ -124,6 +125,10 @@ export type AccessConfigForCMCCCloud = {
accessKeySecret: string;
};
+export type AccessConfigForDeSEC = {
+ token: string;
+};
+
export type AccessConfigForDNSLA = {
apiId: string;
apiSecret: string;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index 37a3f8a0..f26eba63 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -100,6 +100,7 @@ export const accessProvidersMap: Maphttps://ecloud.10086.cn/op-help-center/doc/article/49739",
+ "access.form.desec_token.label": "deSEC token",
+ "access.form.desec_token.placeholder": "Please enter deSEC token",
+ "access.form.desec_token.tooltip": "For more information, see https://desec.readthedocs.io/en/latest/auth/tokens.html",
"access.form.dnsla_api_id.label": "DNS.LA API ID",
"access.form.dnsla_api_id.placeholder": "Please enter DNS.LA API ID",
"access.form.dnsla_api_id.tooltip": "For more information, see https://www.dns.la/docs/ApiDoc",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index 4a5ead78..aa60d5e8 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -120,6 +120,9 @@
"access.form.cmcccloud_access_key_secret.label": "移动云 AccessKeySecret",
"access.form.cmcccloud_access_key_secret.placeholder": "请输入移动云 AccessKeySecret",
"access.form.cmcccloud_access_key_secret.tooltip": "这是什么?请参阅 https://ecloud.10086.cn/op-help-center/doc/article/49739",
+ "access.form.desec_token.label": "deSEC Token",
+ "access.form.desec_token.placeholder": "请输入 deSEC Token",
+ "access.form.desec_token.tooltip": "这是什么?请参阅 https://desec.readthedocs.io/en/latest/auth/tokens.html",
"access.form.dnsla_api_id.label": "DNS.LA API ID",
"access.form.dnsla_api_id.placeholder": "请输入 DNS.LA API ID",
"access.form.dnsla_api_id.tooltip": "这是什么?请参阅 https://www.dns.la/docs/ApiDoc",