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

This commit is contained in:
Fu Diwei
2025-10-22 22:25:13 +08:00
parent c2a2ad6cb0
commit f4ea0a071f
13 changed files with 163 additions and 7 deletions
@@ -0,0 +1,30 @@
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/bookmyname"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
if err := ACMEDns01Registries.Register(domain.ACMEDns01ProviderTypeBookMyName, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
credentials := domain.AccessConfigForBookMyName{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := bookmyname.NewChallengeProvider(&bookmyname.ChallengeProviderConfig{
Username: credentials.Username,
Password: credentials.Password,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return provider, err
}); err != nil {
panic(err)
}
}
+8 -3
View File
@@ -93,15 +93,20 @@ type AccessConfigForBaotaWAF struct {
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForBytePlus struct {
AccessKey string `json:"accessKey"`
SecretKey string `json:"secretKey"`
type AccessConfigForBookMyName struct {
Username string `json:"username"`
Password string `json:"password"`
}
type AccessConfigForBunny struct {
ApiKey string `json:"apiKey"`
}
type AccessConfigForBytePlus struct {
AccessKey string `json:"accessKey"`
SecretKey string `json:"secretKey"`
}
type AccessConfigForCacheFly struct {
ApiToken string `json:"apiToken"`
}
+2 -1
View File
@@ -23,7 +23,7 @@ const (
AccessProviderTypeBaishan = AccessProviderType("baishan")
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
AccessProviderTypeBaotaWAF = AccessProviderType("baotawaf")
AccessProviderTypeBookMyName = AccessProviderType("bookmyname") // BookMyName(预留)
AccessProviderTypeBookMyName = AccessProviderType("bookmyname")
AccessProviderTypeBunny = AccessProviderType("bunny")
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
AccessProviderTypeCacheFly = AccessProviderType("cachefly")
@@ -144,6 +144,7 @@ const (
ACMEDns01ProviderTypeAzureDNS = ACMEDns01ProviderType(AccessProviderTypeAzure + "-dns")
ACMEDns01ProviderTypeBaiduCloud = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeBaiduCloudDNS]
ACMEDns01ProviderTypeBaiduCloudDNS = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud + "-dns")
ACMEDns01ProviderTypeBookMyName = ACMEDns01ProviderType(AccessProviderTypeBookMyName)
ACMEDns01ProviderTypeBunny = ACMEDns01ProviderType(AccessProviderTypeBunny)
ACMEDns01ProviderTypeCloudflare = ACMEDns01ProviderType(AccessProviderTypeCloudflare)
ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS)
@@ -0,0 +1,40 @@
package bookmyname
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/bookmyname"
"github.com/certimate-go/certimate/pkg/core"
)
type ChallengeProviderConfig struct {
Username string `json:"username"`
Password string `json:"password"`
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 := bookmyname.NewDefaultConfig()
providerConfig.Username = config.Username
providerConfig.Password = config.Password
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := bookmyname.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}
@@ -1,4 +1,4 @@
package namedotcom
package hetzner
import (
"errors"
@@ -21,7 +21,7 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger,
}
providerConfig := hetzner.NewDefaultConfig()
providerConfig.APIKey = config.ApiToken
providerConfig.APIToken = config.ApiToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

+4
View File
@@ -24,6 +24,7 @@ import AccessConfigFieldsProviderBaiduCloud from "./forms/AccessConfigFieldsProv
import AccessConfigFieldsProviderBaishan from "./forms/AccessConfigFieldsProviderBaishan";
import AccessConfigFieldsProviderBaotaPanel from "./forms/AccessConfigFieldsProviderBaotaPanel";
import AccessConfigFieldsProviderBaotaWAF from "./forms/AccessConfigFieldsProviderBaotaWAF";
import AccessConfigFieldsProviderBookMyName from "./forms/AccessConfigFieldsProviderBookMyName";
import AccessConfigFieldsProviderBunny from "./forms/AccessConfigFieldsProviderBunny";
import AccessConfigFieldsProviderBytePlus from "./forms/AccessConfigFieldsProviderBytePlus";
import AccessConfigFieldsProviderCacheFly from "./forms/AccessConfigFieldsProviderCacheFly";
@@ -174,6 +175,9 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, on
case ACCESS_PROVIDERS.BAOTAWAF: {
return <AccessConfigFieldsProviderBaotaWAF />;
}
case ACCESS_PROVIDERS.BOOKMYNAME: {
return <AccessConfigFieldsProviderBookMyName />;
}
case ACCESS_PROVIDERS.BUNNY: {
return <AccessConfigFieldsProviderBunny />;
}
@@ -0,0 +1,62 @@
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 AccessConfigFormFieldsProviderBookMyName = () => {
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, "username"]}
initialValue={initialValues.username}
label={t("access.form.bookmyname_username.label")}
rules={[formRule]}
>
<Input autoComplete="new-password" placeholder={t("access.form.bookmyname_username.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "password"]}
initialValue={initialValues.password}
label={t("access.form.bookmyname_password.label")}
rules={[formRule]}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.bookmyname_password.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
username: "",
password: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
username: z.string().nonempty(t("access.form.bookmyname_username.placeholder")),
password: z.string().nonempty(t("access.form.bookmyname_password.placeholder")),
});
};
const _default = Object.assign(AccessConfigFormFieldsProviderBookMyName, {
getInitialValues,
getSchema,
});
export default _default;
+4
View File
@@ -28,6 +28,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
BAISHAN: "baishan",
BAOTAPANEL: "baotapanel",
BAOTAWAF: "baotawaf",
BOOKMYNAME: "bookmyname",
BUNNY: "bunny",
BYTEPLUS: "byteplus",
CACHEFLY: "cachefly",
@@ -164,6 +165,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[ACCESS_PROVIDERS.KONG, "provider.kong", "/imgs/providers/kong.png", [ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.PROXMOXVE, "provider.proxmoxve", "/imgs/providers/proxmoxve.svg", [ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.BOOKMYNAME, "provider.bookmyname", "/imgs/providers/bookmyname.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.CLOUDFLARE, "provider.cloudflare", "/imgs/providers/cloudflare.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.CLOUDNS, "provider.cloudns", "/imgs/providers/cloudns.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.CONSTELLIX, "provider.constellix", "/imgs/providers/constellix.png", [ACCESS_USAGES.DNS]],
@@ -292,6 +294,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
AZURE_DNS: `${ACCESS_PROVIDERS.AZURE}-dns`,
BAIDUCLOUD: `${ACCESS_PROVIDERS.BAIDUCLOUD}`, // 兼容旧值,等同于 `BAIDUCLOUD_DNS`
BAIDUCLOUD_DNS: `${ACCESS_PROVIDERS.BAIDUCLOUD}-dns`,
BOOKMYNAME: `${ACCESS_PROVIDERS.BOOKMYNAME}`,
BUNNY: `${ACCESS_PROVIDERS.BUNNY}`,
CLOUDFLARE: `${ACCESS_PROVIDERS.CLOUDFLARE}`,
CLOUDNS: `${ACCESS_PROVIDERS.CLOUDNS}`,
@@ -357,6 +360,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
[ACME_DNS01_PROVIDERS.JDCLOUD_DNS, "provider.jdcloud.dns"],
[ACME_DNS01_PROVIDERS.AWS_ROUTE53, "provider.aws.route53"],
[ACME_DNS01_PROVIDERS.AZURE_DNS, "provider.azure.dns"],
[ACME_DNS01_PROVIDERS.BOOKMYNAME, "provider.bookmyname"],
[ACME_DNS01_PROVIDERS.BUNNY, "provider.bunny"],
[ACME_DNS01_PROVIDERS.CLOUDFLARE, "provider.cloudflare"],
[ACME_DNS01_PROVIDERS.CLOUDNS, "provider.cloudns"],
+4
View File
@@ -136,6 +136,10 @@
"access.form.baotawaf_api_key.label": "aaWAF API key",
"access.form.baotawaf_api_key.placeholder": "Please enter aaWAF API key",
"access.form.baotawaf_api_key.tooltip": "For more information, see <a href=\"https://github.com/aaPanel/aaWAF/blob/main/API.md\" target=\"_blank\">https://github.com/aaPanel/aaWAF/blob/main/API.md</a>",
"access.form.bookmyname_username.label": "BookMyName username",
"access.form.bookmyname_username.placeholder": "Please enter BookMyName username",
"access.form.bookmyname_password.label": "BookMyName password",
"access.form.bookmyname_password.placeholder": "Please enter BookMyName password",
"access.form.bunny_api_key.label": "Bunny API key",
"access.form.bunny_api_key.placeholder": "Please enter Bunny API key",
"access.form.bunny_api_key.tooltip": "For more information, see <a href=\"https://docs.bunny.net/reference/bunnynet-api-overview\" target=\"_blank\">https://docs.bunny.net/reference/bunnynet-api-overview</a>",
+1
View File
@@ -49,6 +49,7 @@
"provider.baotawaf": "aaWAF (aka BaotaWAF)",
"provider.baotawaf.console": "aaWAF (aka BaotaWAF) - Console itself",
"provider.baotawaf.site": "aaWAF (aka BaotaWAF) - Website",
"provider.bookmyname": "BookMyName",
"provider.bunny": "Bunny",
"provider.bunny.cdn": "Bunny - CDN (Content Delivery Network)",
"provider.byteplus": "BytePlus",
+5 -1
View File
@@ -133,8 +133,12 @@
"access.form.baotawaf_server_url.placeholder": "请输入堡塔云 WAF 服务地址",
"access.form.baotawaf_server_url.help": "提示:请勿包含安全入口后缀。",
"access.form.baotawaf_api_key.label": "堡塔云 WAF 接口密钥",
"access.form.baotawaf_api_key.placeholder": "请输入 堡塔云 WAF 接口密钥",
"access.form.baotawaf_api_key.placeholder": "请输入堡塔云 WAF 接口密钥",
"access.form.baotawaf_api_key.tooltip": "这是什么?请参阅 <a href=\"https://github.com/aaPanel/aaWAF/blob/main/API.md\" target=\"_blank\">https://github.com/aaPanel/aaWAF/blob/main/API.md</a>",
"access.form.bookmyname_username.label": "BookMyName 用户名",
"access.form.bookmyname_username.placeholder": "请输入 BookMyName 用户名",
"access.form.bookmyname_password.label": "BookMyName 用户密码",
"access.form.bookmyname_password.placeholder": "请输入 BookMyName 用户密码",
"access.form.bunny_api_key.label": "Bunny API Key",
"access.form.bunny_api_key.placeholder": "请输入 Bunny API Key",
"access.form.bunny_api_key.tooltip": "这是什么?请参阅 <a href=\"https://docs.bunny.net/reference/bunnynet-api-overview\" target=\"_blank\">https://docs.bunny.net/reference/bunnynet-api-overview</a>",
+1
View File
@@ -49,6 +49,7 @@
"provider.baotawaf": "堡塔云 WAF",
"provider.baotawaf.console": "堡塔云 WAF - 面板自身",
"provider.baotawaf.site": "堡塔云 WAF - 网站",
"provider.bookmyname": "BookMyName",
"provider.bunny": "Bunny",
"provider.bunny.cdn": "Bunny - 内容分发网络 CDN",
"provider.byteplus": "BytePlus",