feat(provider): new acme dns-01 provider: hostingde

This commit is contained in:
Fu Diwei
2025-11-03 15:51:40 +08:00
parent 3ba22a2296
commit 494a55ff3e
13 changed files with 143 additions and 1 deletions
@@ -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/hostingde"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := ACMEDns01Registries.Register(domain.ACMEDns01ProviderTypeHostingde, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
credentials := domain.AccessConfigForHostingde{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := hostingde.NewChallengeProvider(&hostingde.ChallengeProviderConfig{
ApiKey: credentials.ApiKey,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return provider, err
}); err != nil {
panic(err)
}
}
+4
View File
@@ -247,6 +247,10 @@ type AccessConfigForHetzner struct {
ApiToken string `json:"apiToken"`
}
type AccessConfigForHostingde struct {
ApiKey string `json:"apiKey"`
}
type AccessConfigForHostinger struct {
ApiToken string `json:"apiToken"`
}
+2
View File
@@ -54,6 +54,7 @@ const (
AccessProviderTypeGoEdge = AccessProviderType("goedge")
AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices")
AccessProviderTypeHetzner = AccessProviderType("hetzner")
AccessProviderTypeHostingde = AccessProviderType("hostingde")
AccessProviderTypeHostinger = AccessProviderType("hostinger")
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
AccessProviderTypeIONOS = AccessProviderType("ionos")
@@ -165,6 +166,7 @@ const (
ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname)
ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy)
ACMEDns01ProviderTypeHetzner = ACMEDns01ProviderType(AccessProviderTypeHetzner)
ACMEDns01ProviderTypeHostingde = ACMEDns01ProviderType(AccessProviderTypeHostingde)
ACMEDns01ProviderTypeHostinger = ACMEDns01ProviderType(AccessProviderTypeHostinger)
ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS]
ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns")
@@ -0,0 +1,38 @@
package hostingde
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/hostingde"
"github.com/certimate-go/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
ApiKey string `json:"apiKey"`
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 := hostingde.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := hostingde.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}
+1 -1
View File
@@ -1 +1 @@
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1684" width="200" height="200"><path d="M482.687 74.101c10.109-5.627 19.662-12.497 30.785-15.998 8.506-2.192 16.685 2.323 23.883 6.38 117.253 68.08 235.062 135.312 352.118 203.753-40.338 22.115-79.662 46.063-119.805 68.506-27.677 15.148-62.814 13.74-89.771-2.388-48.289-28.135-96.871-55.78-145.127-84.014-8.997-5.692-21.232-5.889-30.654-1.21-49.728 28.724-99.456 57.58-149.282 86.173-27.056 15.834-61.963 15.867-89.314 0.687a26252.906 26252.906 0 0 1-113.785-65.628c-0.229-1.178-0.72-3.533-0.949-4.744 110.776-63.533 221.256-127.722 331.9-191.517z" fill="#72AF2D" p-id="1685"></path><path d="M115.552 719.744c0.49-135.148-0.622-270.329 0.556-405.477 32.617 19.367 65.595 38.08 98.441 57.088 12.76 7.427 26.27 14.199 36.74 24.864 15.769 16.39 26.042 38.67 25.845 61.637 0.033 54.57 0.131 109.172-0.065 163.774-1.047 12.203 3.304 25.65 14.493 31.963 40.567 23.72 81.396 47.045 122.095 70.6 14.362 8.638 29.771 15.9 42.4 27.057 18.156 17.11 28.756 41.777 29.116 66.707-0.033 44.559-0.196 89.15 0.066 133.709-10.175-3.468-18.877-9.848-28.201-14.984-108.55-62.716-217.167-125.366-325.652-188.148-10.207-5.66-17.143-16.947-15.834-28.79z" fill="#118CCF" p-id="1686"></path><path d="M815.143 367.397c30.753-17.47 61.015-35.824 92.095-52.705 0.196 135.017-0.066 270.035 0.13 405.052 0.819 11.582-5.3 23.163-15.637 28.627-110.416 63.86-220.896 127.558-331.312 191.386-7.328 4.45-14.722 8.8-22.508 12.432-0.098-44.82 0.065-89.64-0.098-134.428-0.426-31.538 17.47-62.16 44.558-78.06 49.074-28.43 98.245-56.664 147.286-85.159 8.245-4.515 15.311-13.053 14.919-22.9 0.13-55.683 0.065-111.398 0-167.08-0.033-23.784 8.048-47.732 24.21-65.398 12.497-14.362 30.36-22.083 46.357-31.767z" fill="#DA4525" p-id="1687"></path></svg>
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M482.687 74.101c10.109-5.627 19.662-12.497 30.785-15.998 8.506-2.192 16.685 2.323 23.883 6.38 117.253 68.08 235.062 135.312 352.118 203.753-40.338 22.115-79.662 46.063-119.805 68.506-27.677 15.148-62.814 13.74-89.771-2.388-48.289-28.135-96.871-55.78-145.127-84.014-8.997-5.692-21.232-5.889-30.654-1.21-49.728 28.724-99.456 57.58-149.282 86.173-27.056 15.834-61.963 15.867-89.314 0.687a26252.906 26252.906 0 0 1-113.785-65.628c-0.229-1.178-0.72-3.533-0.949-4.744 110.776-63.533 221.256-127.722 331.9-191.517z" fill="#72AF2D"></path><path d="M115.552 719.744c0.49-135.148-0.622-270.329 0.556-405.477 32.617 19.367 65.595 38.08 98.441 57.088 12.76 7.427 26.27 14.199 36.74 24.864 15.769 16.39 26.042 38.67 25.845 61.637 0.033 54.57 0.131 109.172-0.065 163.774-1.047 12.203 3.304 25.65 14.493 31.963 40.567 23.72 81.396 47.045 122.095 70.6 14.362 8.638 29.771 15.9 42.4 27.057 18.156 17.11 28.756 41.777 29.116 66.707-0.033 44.559-0.196 89.15 0.066 133.709-10.175-3.468-18.877-9.848-28.201-14.984-108.55-62.716-217.167-125.366-325.652-188.148-10.207-5.66-17.143-16.947-15.834-28.79z" fill="#118CCF"></path><path d="M815.143 367.397c30.753-17.47 61.015-35.824 92.095-52.705 0.196 135.017-0.066 270.035 0.13 405.052 0.819 11.582-5.3 23.163-15.637 28.627-110.416 63.86-220.896 127.558-331.312 191.386-7.328 4.45-14.722 8.8-22.508 12.432-0.098-44.82 0.065-89.64-0.098-134.428-0.426-31.538 17.47-62.16 44.558-78.06 49.074-28.43 98.245-56.664 147.286-85.159 8.245-4.515 15.311-13.053 14.919-22.9 0.13-55.683 0.065-111.398 0-167.08-0.033-23.784 8.048-47.732 24.21-65.398 12.497-14.362 30.36-22.083 46.357-31.767z" fill="#DA4525"></path></svg>
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-11 -1 209 219" fill="none"><g xmlns="http://www.w3.org/2000/svg"><path d="M101.56 201.22c-33.24 4.25-67.28-14-85.84-41.13C4.99995 144.35-.0900482 125.88-.482179e-4 106.88.0799518 89 4.58995 63 18.08 49.82c20.53-20.07 23.61 26.94 25 38.24 2.313 18.253 4.647 36.503 7 54.75 3.67 28.48 17.41 63.09 51.48 58.41z" fill="#fbb03b"/><path d="M171.78 214.88C143.1 224.8 108.92 210.21 94.2399 184.19c-9.45-17.153-11.481-37.423-5.6199-56.11C94.38 110.52 104.62 117.42 109.62 130.42c10.38 26.65 21.2 99.58 62.16 84.46z" fill="#1b4964"/><path d="M77.1499 15.46C110.32 11.22 144.36 29.15 162.96 56.26c10.78 15.72 15.76 34.14 15.7 53.12C178.6 126.38 173.98 156.63 159.29 167.96c-20.67 15.94-22.57-30.53-23.94-41.26C133.02 108.44 130.74 90.18 128.35 71.93 124.65 43.57 110.56 10.87 77.1499 15.46z" fill="#e6302b"/><path d="M5.44995 3.12C33.54-6.59 66.94 7.22 82 32.26 91.443 48.38 94.238 67.545 89.79 85.69c-4.36 17.38-14.7901 19.81-21.7001 3.23C56.9099 62.15 47.34-11.92 5.44995 3.12z" fill="#8cc63f"/></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+4
View File
@@ -53,6 +53,7 @@ import AccessConfigFieldsProviderGoDaddy from "./forms/AccessConfigFieldsProvide
import AccessConfigFieldsProviderGoEdge from "./forms/AccessConfigFieldsProviderGoEdge";
import AccessConfigFieldsProviderGoogleTrustServices from "./forms/AccessConfigFieldsProviderGoogleTrustServices";
import AccessConfigFieldsProviderHetzner from "./forms/AccessConfigFieldsProviderHetzner";
import AccessConfigFieldsProviderHostingde from "./forms/AccessConfigFieldsProviderHostingde";
import AccessConfigFieldsProviderHostinger from "./forms/AccessConfigFieldsProviderHostinger";
import AccessConfigFieldsProviderHuaweiCloud from "./forms/AccessConfigFieldsProviderHuaweiCloud";
import AccessConfigFieldsProviderIONOS from "./forms/AccessConfigFieldsProviderIONOS";
@@ -266,6 +267,9 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, on
case ACCESS_PROVIDERS.HETZNER: {
return <AccessConfigFieldsProviderHetzner />;
}
case ACCESS_PROVIDERS.HOSTINGDE: {
return <AccessConfigFieldsProviderHostingde />;
}
case ACCESS_PROVIDERS.HOSTINGER: {
return <AccessConfigFieldsProviderHostinger />;
}
@@ -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 AccessConfigFormFieldsProviderHostingde = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const initialValues = getInitialValues();
return (
<>
<Form.Item
name={[parentNamePath, "apiKey"]}
initialValue={initialValues.apiKey}
label={t("access.form.hostingde_api_key.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.hostingde_api_key.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.hostingde_api_key.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
apiKey: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
apiKey: z.string().nonempty(t("access.form.hostingde_api_key.placeholder")),
});
};
const _default = Object.assign(AccessConfigFormFieldsProviderHostingde, {
getInitialValues,
getSchema,
});
export default _default;
+4
View File
@@ -57,6 +57,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
GOEDGE: "goedge",
GOOGLETRUSTSERVICES: "googletrustservices",
HETZNER: "hetzner",
HOSTINGDE: "hostingDE",
HOSTINGER: "hostinger",
HUAWEICLOUD: "huaweicloud",
IONOS: "ionos",
@@ -182,6 +183,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[ACCESS_PROVIDERS.GNAME, "provider.gname", "/imgs/providers/gname.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.GODADDY, "provider.godaddy", "/imgs/providers/godaddy.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.HETZNER, "provider.hetzner", "/imgs/providers/hetzner.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.HOSTINGDE, "provider.hostingde", "/imgs/providers/hostingde.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.HOSTINGER, "provider.hostinger", "/imgs/providers/hostinger.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.IONOS, "provider.ionos", "/imgs/providers/ionos.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.LINODE, "provider.linode", "/imgs/providers/linode.svg", [ACCESS_USAGES.DNS]],
@@ -321,6 +323,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
GNAME: `${ACCESS_PROVIDERS.GNAME}`,
GODADDY: `${ACCESS_PROVIDERS.GODADDY}`,
HETZNER: `${ACCESS_PROVIDERS.HETZNER}`,
HOSTINGDE: `${ACCESS_PROVIDERS.HOSTINGDE}`,
HOSTINGER: `${ACCESS_PROVIDERS.HOSTINGER}`,
HUAWEICLOUD: `${ACCESS_PROVIDERS.HUAWEICLOUD}`, // 兼容旧值,等同于 `HUAWEICLOUD_DNS`
HUAWEICLOUD_DNS: `${ACCESS_PROVIDERS.HUAWEICLOUD}-dns`,
@@ -387,6 +390,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
[ACME_DNS01_PROVIDERS.GNAME, "provider.gname"],
[ACME_DNS01_PROVIDERS.GODADDY, "provider.godaddy"],
[ACME_DNS01_PROVIDERS.HETZNER, "provider.hetzner"],
[ACME_DNS01_PROVIDERS.HOSTINGDE, "provider.hostingde"],
[ACME_DNS01_PROVIDERS.HOSTINGER, "provider.hostinger"],
[ACME_DNS01_PROVIDERS.IONOS, "provider.ionos"],
[ACME_DNS01_PROVIDERS.LINODE, "provider.linode"],
+3
View File
@@ -296,6 +296,9 @@
"access.form.hetzner_api_token.label": "Hetzner API token",
"access.form.hetzner_api_token.placeholder": "Please enter Hetzner API token",
"access.form.hetzner_api_token.tooltip": "For more information, see <a href=\"https://docs.hetzner.com/cloud/api/getting-started/generating-api-token\" target=\"_blank\">https://docs.hetzner.com/cloud/api/getting-started/generating-api-token</a>",
"access.form.hostingde_api_key.label": "hosting.de API key",
"access.form.hostingde_api_key.placeholder": "Please enter hosting.de API key",
"access.form.hostingde_api_key.tooltip": "For more information, see <a href=\"https://www.hosting.de/api/#requests-and-authentication\" target=\"_blank\">https://www.hosting.de/api/#requests-and-authentication</a>",
"access.form.hostinger_api_token.label": "Hostinger API token",
"access.form.hostinger_api_token.placeholder": "Please enter Hostinger API token",
"access.form.hostinger_api_token.tooltip": "For more information, see <a href=\"https://developers.hostinger.com/#description/authentication\" target=\"_blank\">https://developers.hostinger.com/#description/authentication</a>",
+1
View File
@@ -94,6 +94,7 @@
"provider.goedge": "GoEdge",
"provider.googletrustservices": "Google Trust Services",
"provider.hetzner": "Hetzner",
"provider.hostingde": "hosting.de",
"provider.hostinger": "Hostinger",
"provider.huaweicloud": "Huawei Cloud",
"provider.huaweicloud.cdn": "Huawei Cloud - CDN (Content Delivery Network)",
+3
View File
@@ -295,6 +295,9 @@
"access.form.hetzner_api_token.label": "Hetzner API Token",
"access.form.hetzner_api_token.placeholder": "请输入 Hetzner API Token",
"access.form.hetzner_api_token.tooltip": "这是什么?请参阅 <a href=\"https://docs.hetzner.com/cloud/api/getting-started/generating-api-token\" target=\"_blank\">https://docs.hetzner.com/cloud/api/getting-started/generating-api-token</a>",
"access.form.hostingde_api_key.label": "hosting.de API Key",
"access.form.hostingde_api_key.placeholder": "请输入 hosting.de API Key",
"access.form.hostingde_api_key.tooltip": "这是什么?请参阅 <a href=\"https://www.hosting.de/api/#requests-and-authentication\" target=\"_blank\">https://www.hosting.de/api/#requests-and-authentication</a>",
"access.form.hostinger_api_token.label": "Hostinger API Token",
"access.form.hostinger_api_token.placeholder": "请输入 Hostinger API Token",
"access.form.hostinger_api_token.tooltip": "这是什么?请参阅 <a href=\"https://developers.hostinger.com/#description/authentication\" target=\"_blank\">https://developers.hostinger.com/#description/authentication</a>",
+1
View File
@@ -94,6 +94,7 @@
"provider.goedge": "GoEdge",
"provider.googletrustservices": "Google Trust Services",
"provider.hetzner": "Hetzner",
"provider.hostingde": "hosting.de",
"provider.hostinger": "Hostinger",
"provider.huaweicloud": "华为云",
"provider.huaweicloud.cdn": "华为云 - 内容分发网络 CDN",