diff --git a/internal/certapply/applicators/sp_gandinet.go b/internal/certapply/applicators/sp_gandinet.go
new file mode 100644
index 00000000..9ba5a1a1
--- /dev/null
+++ b/internal/certapply/applicators/sp_gandinet.go
@@ -0,0 +1,29 @@
+package applicators
+
+import (
+ "fmt"
+
+ "github.com/go-acme/lego/v4/challenge"
+
+ "github.com/certimate-go/certimate/internal/domain"
+ "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/gandinet"
+ xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
+)
+
+func init() {
+ if err := ACMEDns01Registries.Register(domain.ACMEDns01ProviderTypeGandinet, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
+ credentials := domain.AccessConfigForGandinet{}
+ if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ provider, err := gandinet.NewChallengeProvider(&gandinet.ChallengeProviderConfig{
+ PersonalAccessToken: credentials.PersonalAccessToken,
+ DnsPropagationTimeout: options.DnsPropagationTimeout,
+ DnsTTL: options.DnsTTL,
+ })
+ return provider, err
+ }); err != nil {
+ panic(err)
+ }
+}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index 3dd14b1d..422ba921 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -204,6 +204,10 @@ type AccessConfigForFlexCDN struct {
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
+type AccessConfigForGandinet struct {
+ PersonalAccessToken string `json:"personalAccessToken"`
+}
+
type AccessConfigForGcore struct {
ApiToken string `json:"apiToken"`
}
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index 53e2f44e..d2f2c5f1 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -46,11 +46,12 @@ const (
AccessProviderTypeEmail = AccessProviderType("email")
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
AccessProviderTypeFlexCDN = AccessProviderType("flexcdn")
- AccessProviderTypeGname = AccessProviderType("gname")
+ AccessProviderTypeGandinet = AccessProviderType("gandinet")
AccessProviderTypeGcore = AccessProviderType("gcore")
+ AccessProviderTypeGlobalSignAtlas = AccessProviderType("globalsignatlas")
+ AccessProviderTypeGname = AccessProviderType("gname")
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
AccessProviderTypeGoEdge = AccessProviderType("goedge")
- AccessProviderTypeGlobalSignAtlas = AccessProviderType("globalsignatlas")
AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices")
AccessProviderTypeHetzner = AccessProviderType("hetzner")
AccessProviderTypeHostinger = AccessProviderType("hostinger")
@@ -159,6 +160,7 @@ const (
ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA)
ACMEDns01ProviderTypeDuckDNS = ACMEDns01ProviderType(AccessProviderTypeDuckDNS)
ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6)
+ ACMEDns01ProviderTypeGandinet = ACMEDns01ProviderType(AccessProviderTypeGandinet)
ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore)
ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname)
ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy)
diff --git a/pkg/core/ssl-applicator/acme-dns01/providers/gandinet/gandinet.go b/pkg/core/ssl-applicator/acme-dns01/providers/gandinet/gandinet.go
new file mode 100644
index 00000000..d528c56b
--- /dev/null
+++ b/pkg/core/ssl-applicator/acme-dns01/providers/gandinet/gandinet.go
@@ -0,0 +1,38 @@
+package gandinet
+
+import (
+ "errors"
+ "time"
+
+ "github.com/go-acme/lego/v4/providers/dns/gandiv5"
+
+ "github.com/certimate-go/certimate/pkg/core"
+)
+
+type ChallengeProviderConfig struct {
+ PersonalAccessToken string `json:"personalAccessToken"`
+ DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
+ DnsTTL int32 `json:"dnsTTL,omitempty"`
+}
+
+func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
+ if config == nil {
+ return nil, errors.New("the configuration of the acme challenge provider is nil")
+ }
+
+ providerConfig := gandiv5.NewDefaultConfig()
+ providerConfig.PersonalAccessToken = config.PersonalAccessToken
+ if config.DnsPropagationTimeout != 0 {
+ providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
+ }
+ if config.DnsTTL != 0 {
+ providerConfig.TTL = int(config.DnsTTL)
+ }
+
+ provider, err := gandiv5.NewDNSProviderConfig(providerConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ return provider, nil
+}
diff --git a/pkg/core/ssl-deployer/providers/gcore-cdn/gcore_cdn.go b/pkg/core/ssl-deployer/providers/gcore-cdn/gcore_cdn.go
index a64242df..d8862215 100644
--- a/pkg/core/ssl-deployer/providers/gcore-cdn/gcore_cdn.go
+++ b/pkg/core/ssl-deployer/providers/gcore-cdn/gcore_cdn.go
@@ -18,7 +18,7 @@ import (
)
type SSLDeployerProviderConfig struct {
- // Gcore API Token。
+ // G-Core API Token。
ApiToken string `json:"apiToken"`
// CDN 资源 ID。
ResourceId int64 `json:"resourceId"`
diff --git a/pkg/core/ssl-manager/providers/gcore-cdn/gcore_cdn.go b/pkg/core/ssl-manager/providers/gcore-cdn/gcore_cdn.go
index 34539aa2..5b5b6b5a 100644
--- a/pkg/core/ssl-manager/providers/gcore-cdn/gcore_cdn.go
+++ b/pkg/core/ssl-manager/providers/gcore-cdn/gcore_cdn.go
@@ -15,7 +15,7 @@ import (
)
type SSLManagerProviderConfig struct {
- // Gcore API Token。
+ // G-Core API Token。
ApiToken string `json:"apiToken"`
}
diff --git a/ui/public/imgs/providers/gandinet.svg b/ui/public/imgs/providers/gandinet.svg
new file mode 100644
index 00000000..985e0900
--- /dev/null
+++ b/ui/public/imgs/providers/gandinet.svg
@@ -0,0 +1 @@
+
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index df9bd24c..a10a8218 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -45,6 +45,7 @@ import AccessConfigFieldsProviderDuckDNS from "./forms/AccessConfigFieldsProvide
import AccessConfigFieldsProviderDynv6 from "./forms/AccessConfigFieldsProviderDynv6";
import AccessConfigFieldsProviderEmail from "./forms/AccessConfigFieldsProviderEmail";
import AccessConfigFieldsProviderFlexCDN from "./forms/AccessConfigFieldsProviderFlexCDN";
+import AccessConfigFieldsProviderGandinet from "./forms/AccessConfigFieldsProviderGandinet";
import AccessConfigFieldsProviderGcore from "./forms/AccessConfigFieldsProviderGcore";
import AccessConfigFieldsProviderGlobalSignAtlas from "./forms/AccessConfigFieldsProviderGlobalSignAtlas";
import AccessConfigFieldsProviderGname from "./forms/AccessConfigFieldsProviderGname";
@@ -241,6 +242,9 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, on
case ACCESS_PROVIDERS.FLEXCDN: {
return ;
}
+ case ACCESS_PROVIDERS.GANDINET: {
+ return ;
+ }
case ACCESS_PROVIDERS.GCORE: {
return ;
}
diff --git a/ui/src/components/access/forms/AccessConfigFieldsProviderGandinet.tsx b/ui/src/components/access/forms/AccessConfigFieldsProviderGandinet.tsx
new file mode 100644
index 00000000..dd1064fe
--- /dev/null
+++ b/ui/src/components/access/forms/AccessConfigFieldsProviderGandinet.tsx
@@ -0,0 +1,52 @@
+import { getI18n, useTranslation } from "react-i18next";
+import { Form, Input } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { useFormNestedFieldsContext } from "./_context";
+
+const AccessConfigFormFieldsProviderGandinet = () => {
+ const { i18n, t } = useTranslation();
+
+ const { parentNamePath } = useFormNestedFieldsContext();
+ const formSchema = z.object({
+ [parentNamePath]: getSchema({ i18n }),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+ const initialValues = getInitialValues();
+
+ return (
+ <>
+
}
+ >
+
+
+ >
+ );
+};
+
+const getInitialValues = (): Nullish>> => {
+ return {
+ personalAccessToken: "",
+ };
+};
+
+const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => {
+ const { t } = i18n;
+
+ return z.object({
+ personalAccessToken: z.string().nonempty(t("access.form.gandinet_personal_access_token.placeholder")),
+ });
+};
+
+const _default = Object.assign(AccessConfigFormFieldsProviderGandinet, {
+ getInitialValues,
+ getSchema,
+});
+
+export default _default;
diff --git a/ui/src/components/access/forms/AccessConfigFieldsProviderGcore.tsx b/ui/src/components/access/forms/AccessConfigFieldsProviderGcore.tsx
index e39ac98b..e27902e4 100644
--- a/ui/src/components/access/forms/AccessConfigFieldsProviderGcore.tsx
+++ b/ui/src/components/access/forms/AccessConfigFieldsProviderGcore.tsx
@@ -40,10 +40,7 @@ const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) =
const { t } = i18n;
return z.object({
- apiToken: z
- .string()
- .min(1, t("access.form.gcore_api_token.placeholder"))
- .max(256, t("common.errmsg.string_max", { max: 256 })),
+ apiToken: z.string().nonempty(t("access.form.gcore_api_token.placeholder")),
});
};
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index fc18b4f8..50f71663 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -49,6 +49,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
DYNV6: "dynv6",
EMAIL: "email",
FLEXCDN: "flexcdn",
+ GANDINET: "gandinet",
GCORE: "gcore",
GLOBALSIGNATLAS: "globalsignatlas",
GNAME: "gname",
@@ -177,6 +178,7 @@ export const accessProvidersMap: Maphttps://flexcdn.cn/docs/api/auth",
- "access.form.gcore_api_token.label": "Gcore API token",
- "access.form.gcore_api_token.placeholder": "Please enter Gcore API token",
+ "access.form.gandinet_personal_access_token.label": "Gandi.net personal access token",
+ "access.form.gandinet_personal_access_token.placeholder": "Please enter Gandi.net personal access token",
+ "access.form.gandinet_personal_access_token.tooltip": "For more information, see https://api.gandi.net/docs/authentication/",
+ "access.form.gcore_api_token.label": "G-Core API token",
+ "access.form.gcore_api_token.placeholder": "Please enter G-Core 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",
diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json
index ffb9a0bc..7b3f7d0d 100644
--- a/ui/src/i18n/locales/en/nls.provider.json
+++ b/ui/src/i18n/locales/en/nls.provider.json
@@ -85,8 +85,9 @@
"provider.email": "Email (SMTP)",
"provider.fastly": "Fastly",
"provider.flexcdn": "FlexCDN",
- "provider.gcore": "Gcore",
- "provider.gcore.cdn": "Gcore - CDN (Content Delivery Network)",
+ "provider.gandinet": "Gandi.net",
+ "provider.gcore": "G-Core",
+ "provider.gcore.cdn": "G-Core - CDN (Content Delivery Network)",
"provider.globalsignatlas": "GlobalSign Atlas",
"provider.gname": "GNAME",
"provider.godaddy": "GoDaddy",
diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json
index 1fc17c4f..d5e57818 100644
--- a/ui/src/i18n/locales/en/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json
@@ -521,11 +521,11 @@
"workflow_node.deploy.form.flexcdn_certificate_id.label": "FlexCDN certificate ID",
"workflow_node.deploy.form.flexcdn_certificate_id.placeholder": "Please enter FlexCDN certificate ID",
"workflow_node.deploy.form.flexcdn_certificate_id.tooltip": "You can find it on FlexCDN dashboard.",
- "workflow_node.deploy.form.gcore_cdn_resource_id.label": "Gcore CDN resource ID",
- "workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "Please enter Gcore CDN resource ID",
+ "workflow_node.deploy.form.gcore_cdn_resource_id.label": "G-Core CDN resource ID",
+ "workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "Please enter G-Core CDN resource ID",
"workflow_node.deploy.form.gcore_cdn_resource_id.tooltip": "For more information, see https://cdn.gcore.com/resources/list",
- "workflow_node.deploy.form.gcore_cdn_certificate_id.label": "Gcore CDN certificate ID (Optional)",
- "workflow_node.deploy.form.gcore_cdn_certificate_id.placeholder": "Please enter Gcore CDN certificate ID",
+ "workflow_node.deploy.form.gcore_cdn_certificate_id.label": "G-Core CDN certificate ID (Optional)",
+ "workflow_node.deploy.form.gcore_cdn_certificate_id.placeholder": "Please enter G-Core CDN certificate ID",
"workflow_node.deploy.form.gcore_cdn_certificate_id.help": "",
"workflow_node.deploy.form.gcore_cdn_certificate_id.tooltip": "For more information, see https://cdn.gcore.com/ssl",
"workflow_node.deploy.form.goedge_resource_type.label": "Resource type",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index 96cf8c89..0838810f 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -260,8 +260,11 @@
"access.form.flexcdn_access_key.label": "FlexCDN AccessKey",
"access.form.flexcdn_access_key.placeholder": "请输入 FlexCDN AccessKey",
"access.form.flexcdn_access_key.tooltip": "这是什么?请参阅 https://flexcdn.cn/docs/api/auth",
- "access.form.gcore_api_token.label": "Gcore API Token",
- "access.form.gcore_api_token.placeholder": "请输入 Gcore API Token",
+ "access.form.gandinet_personal_access_token.label": "Gandi.net Personal Access Token",
+ "access.form.gandinet_personal_access_token.placeholder": "请输入 Gandi.net Personal Access Token",
+ "access.form.gandinet_personal_access_token.tooltip": "这是什么?请参阅 https://api.gandi.net/docs/authentication/",
+ "access.form.gcore_api_token.label": "G-Core API Token",
+ "access.form.gcore_api_token.placeholder": "请输入 G-Core 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",
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index 95e31d9f..bf23ceca 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -85,8 +85,9 @@
"provider.email": "邮件(SMTP)",
"provider.fastly": "Fastly",
"provider.flexcdn": "FlexCDN",
- "provider.gcore": "Gcore",
- "provider.gcore.cdn": "Gcore - 内容分发网络 CDN",
+ "provider.gandinet": "Gandi.net",
+ "provider.gcore": "G-Core",
+ "provider.gcore.cdn": "G-Core - 内容分发网络 CDN",
"provider.globalsignatlas": "GlobalSign Atlas",
"provider.gname": "GNAME",
"provider.godaddy": "GoDaddy",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index 01f4e249..238bd62d 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -519,11 +519,11 @@
"workflow_node.deploy.form.flexcdn_certificate_id.label": "FlexCDN 证书 ID",
"workflow_node.deploy.form.flexcdn_certificate_id.placeholder": "请输入 FlexCDN 证书 ID",
"workflow_node.deploy.form.flexcdn_certificate_id.tooltip": "请登录 FlexCDN 控制台查看",
- "workflow_node.deploy.form.gcore_cdn_resource_id.label": "Gcore CDN 资源 ID",
- "workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "请输入 Gcore CDN 资源 ID",
+ "workflow_node.deploy.form.gcore_cdn_resource_id.label": "G-Core CDN 资源 ID",
+ "workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "请输入 G-Core CDN 资源 ID",
"workflow_node.deploy.form.gcore_cdn_resource_id.tooltip": "这是什么?请参阅 https://cdn.gcore.com/resources/list",
- "workflow_node.deploy.form.gcore_cdn_certificate_id.label": "Gcore CDN 原证书 ID(可选)",
- "workflow_node.deploy.form.gcore_cdn_certificate_id.placeholder": "请输入 Gcore CDN 原证书 ID",
+ "workflow_node.deploy.form.gcore_cdn_certificate_id.label": "G-Core CDN 原证书 ID(可选)",
+ "workflow_node.deploy.form.gcore_cdn_certificate_id.placeholder": "请输入 G-Core CDN 原证书 ID",
"workflow_node.deploy.form.gcore_cdn_certificate_id.help": "提示:不填写时,将上传新证书;否则,将替换原证书。",
"workflow_node.deploy.form.gcore_cdn_certificate_id.tooltip": "这是什么?请参阅 https://cdn.gcore.com/ssl",
"workflow_node.deploy.form.goedge_resource_type.label": "证书部署方式",