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

This commit is contained in:
Fu Diwei
2026-03-03 10:01:34 +08:00
committed by RHQYZ
parent 51e9d5dd65
commit 95c861ead9
12 changed files with 161 additions and 10 deletions
@@ -0,0 +1,28 @@
package certifiers
import (
"fmt"
"github.com/go-acme/lego/v4/challenge"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core/certifier/challengers/dns01/todaynic"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
ACMEDns01Registries.MustRegister(domain.ACMEDns01ProviderTypeTodayNIC, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
credentials := domain.AccessConfigForTodayNIC{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := todaynic.NewChallenger(&todaynic.ChallengerConfig{
UserId: credentials.UserId,
ApiKey: credentials.ApiKey,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return provider, err
})
}
+5
View File
@@ -539,6 +539,11 @@ type AccessConfigForTelegramBot struct {
ChatId string `json:"chatId,omitempty"`
}
type AccessConfigForTodayNIC struct {
UserId string `json:"userId"`
ApiKey string `json:"apiKey"`
}
type AccessConfigForTencentCloud struct {
SecretId string `json:"secretId"`
SecretKey string `json:"secretKey"`
+2
View File
@@ -108,6 +108,7 @@ const (
AccessProviderTypeTechnitiumDNS = AccessProviderType("technitiumdns")
AccessProviderTypeTelegramBot = AccessProviderType("telegrambot")
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
AccessProviderTypeTodayNIC = AccessProviderType("todaynic")
AccessProviderTypeUCloud = AccessProviderType("ucloud")
AccessProviderTypeUniCloud = AccessProviderType("unicloud")
AccessProviderTypeUpyun = AccessProviderType("upyun")
@@ -220,6 +221,7 @@ const (
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns")
ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo")
ACMEDns01ProviderTypeTodayNIC = ACMEDns01ProviderType(AccessProviderTypeTodayNIC)
ACMEDns01ProviderTypeUCloud = ACMEDns01ProviderType(AccessProviderTypeUCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeUCloudUDNR]
ACMEDns01ProviderTypeUCloudUDNR = ACMEDns01ProviderType(AccessProviderTypeUCloud + "-udnr")
ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel)
@@ -0,0 +1,40 @@
package todaynic
import (
"errors"
"time"
"github.com/go-acme/lego/v4/providers/dns/todaynic"
"github.com/certimate-go/certimate/pkg/core/certifier"
)
type ChallengerConfig struct {
UserId string `json:"userId"`
ApiKey string `json:"apiKey"`
DnsPropagationTimeout int `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int `json:"dnsTTL,omitempty"`
}
func NewChallenger(config *ChallengerConfig) (certifier.ACMEChallenger, error) {
if config == nil {
return nil, errors.New("the configuration of the acme challenge provider is nil")
}
providerConfig := todaynic.NewDefaultConfig()
providerConfig.AuthUserID = config.UserId
providerConfig.APIKey = config.ApiKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = config.DnsTTL
}
provider, err := todaynic.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

@@ -96,6 +96,7 @@ import AccessConfigFieldsProviderSynologyDSM from "./AccessConfigFieldsProviderS
import AccessConfigFieldsProviderTechnitiumDNS from "./AccessConfigFieldsProviderTechnitiumDNS";
import AccessConfigFieldsProviderTelegramBot from "./AccessConfigFieldsProviderTelegramBot";
import AccessConfigFieldsProviderTencentCloud from "./AccessConfigFieldsProviderTencentCloud";
import AccessConfigFieldsProviderTodayNIC from "./AccessConfigFieldsProviderTodayNIC";
import AccessConfigFieldsProviderUCloud from "./AccessConfigFieldsProviderUCloud";
import AccessConfigFieldsProviderUniCloud from "./AccessConfigFieldsProviderUniCloud";
import AccessConfigFieldsProviderUpyun from "./AccessConfigFieldsProviderUpyun";
@@ -208,6 +209,7 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
[ACCESS_PROVIDERS.TECHNITIUMDNS]: AccessConfigFieldsProviderTechnitiumDNS,
[ACCESS_PROVIDERS.TELEGRAMBOT]: AccessConfigFieldsProviderTelegramBot,
[ACCESS_PROVIDERS.TENCENTCLOUD]: AccessConfigFieldsProviderTencentCloud,
[ACCESS_PROVIDERS.TODAYNIC]: AccessConfigFieldsProviderTodayNIC,
[ACCESS_PROVIDERS.UCLOUD]: AccessConfigFieldsProviderUCloud,
[ACCESS_PROVIDERS.UNICLOUD]: AccessConfigFieldsProviderUniCloud,
[ACCESS_PROVIDERS.UPYUN]: AccessConfigFieldsProviderUpyun,
@@ -0,0 +1,58 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Tips from "@/components/Tips";
import { useFormNestedFieldsContext } from "./_context";
const AccessConfigFormFieldsProviderTodayNIC = () => {
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, "userId"]} initialValue={initialValues.userId} label={t("access.form.todaynic_user_id.label")} rules={[formRule]}>
<Input autoComplete="new-password" placeholder={t("access.form.todaynic_user_id.placeholder")} />
</Form.Item>
<Form.Item name={[parentNamePath, "apiKey"]} initialValue={initialValues.apiKey} label={t("access.form.todaynic_api_key.label")} rules={[formRule]}>
<Input.Password autoComplete="new-password" placeholder={t("access.form.todaynic_api_key.placeholder")} />
</Form.Item>
<Form.Item>
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("access.form.todaynic_agent.guide") }}></span>} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
userId: "",
apiKey: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
userId: z.string().nonempty(t("access.form.todaynic_user_id.placeholder")),
apiKey: z.string().nonempty(t("access.form.todaynic_api_key.placeholder")),
});
};
const _default = Object.assign(AccessConfigFormFieldsProviderTodayNIC, {
getInitialValues,
getSchema,
});
export default _default;
+8 -4
View File
@@ -112,6 +112,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
TECHNITIUMDNS: "technitiumdns",
TELEGRAMBOT: "telegrambot",
TENCENTCLOUD: "tencentcloud",
TODAYNIC: "todaynic",
UCLOUD: "ucloud",
UNICLOUD: "unicloud",
UPYUN: "upyun",
@@ -232,11 +233,12 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[ACCESS_PROVIDERS.VULTR, "provider.vultr", "/imgs/providers/vultr.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.CMCCCLOUD, "provider.cmcccloud", "/imgs/providers/cmcccloud.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.QINGCLOUD, "provider.qingcloud", "/imgs/providers/qingcloud.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.WESTCN, "provider.westcn", "/imgs/providers/westcn.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS["35CN"], "provider.35cn", "/imgs/providers/35cn.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.XINNET, "provider.xinnet", "/imgs/providers/xinnet.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS["51DNSCOM"], "provider.51dnscom", "/imgs/providers/51dnscom.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.DNSLA, "provider.dnsla", "/imgs/providers/dnsla.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.TODAYNIC, "provider.todaynic", "/imgs/providers/todaynic.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.WESTCN, "provider.westcn", "/imgs/providers/westcn.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.XINNET, "provider.xinnet", "/imgs/providers/xinnet.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.POWERDNS, "provider.powerdns", "/imgs/providers/powerdns.svg", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.TECHNITIUMDNS, "provider.technitiumdns", "/imgs/providers/technitiumdns.png", [ACCESS_USAGES.DNS]],
[ACCESS_PROVIDERS.RFC2136, "provider.rfc2136", "/imgs/providers/rfc.png", [ACCESS_USAGES.DNS]],
@@ -403,6 +405,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
TENCENTCLOUD: `${ACCESS_PROVIDERS.TENCENTCLOUD}`, // 兼容旧值,等同于 `TENCENTCLOUD_DNS`
TENCENTCLOUD_DNS: `${ACCESS_PROVIDERS.TENCENTCLOUD}-dns`,
TENCENTCLOUD_EO: `${ACCESS_PROVIDERS.TENCENTCLOUD}-eo`,
TODAYNIC: `${ACCESS_PROVIDERS.TODAYNIC}`,
VERCEL: `${ACCESS_PROVIDERS.VERCEL}`,
VOLCENGINE: `${ACCESS_PROVIDERS.VOLCENGINE}`, // 兼容旧值,等同于 `VOLCENGINE_DNS`
VOLCENGINE_DNS: `${ACCESS_PROVIDERS.VOLCENGINE}-dns`,
@@ -472,11 +475,12 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
[ACME_DNS01_PROVIDERS.RAINYUN, "provider.rainyun"],
[ACME_DNS01_PROVIDERS.UCLOUD_UDNR, "provider.ucloud_udnr"],
[ACME_DNS01_PROVIDERS.QINGCLOUD_DNS, "provider.qingcloud_dns"],
[ACME_DNS01_PROVIDERS.WESTCN, "provider.westcn"],
[ACME_DNS01_PROVIDERS["35CN"], "provider.35cn"],
[ACME_DNS01_PROVIDERS.XINNET, "provider.xinnet"],
[ACME_DNS01_PROVIDERS["51DNSCOM"], "provider.51dnscom"],
[ACME_DNS01_PROVIDERS.DNSLA, "provider.dnsla"],
[ACME_DNS01_PROVIDERS.TODAYNIC, "provider.todaynic"],
[ACME_DNS01_PROVIDERS.WESTCN, "provider.westcn"],
[ACME_DNS01_PROVIDERS.XINNET, "provider.xinnet"],
[ACME_DNS01_PROVIDERS.CPANEL, "provider.cpanel"],
[ACME_DNS01_PROVIDERS.POWERDNS, "provider.powerdns"],
[ACME_DNS01_PROVIDERS.TECHNITIUMDNS, "provider.technitiumdns"],
+10 -5
View File
@@ -645,6 +645,11 @@
"access.form.tencentcloud_secret_key.label": "Tencent Cloud SecretKey",
"access.form.tencentcloud_secret_key.placeholder": "Please enter Tencent Cloud SecretKey",
"access.form.tencentcloud_secret_key.tooltip": "For more information, see <a href=\"https://www.tencentcloud.com/zh/document/product/598/34228\" target=\"_blank\">https://www.tencentcloud.com/zh/document/product/598/34228</a>",
"access.form.todaynic_user_id.label": "TodayNIC.com agent user ID",
"access.form.todaynic_user_id.placeholder": "Please enter TodayNIC.com agent user ID",
"access.form.todaynic_api_key.label": "TodayNIC.com agent API key",
"access.form.todaynic_api_key.placeholder": "Please enter TodayNIC.com agent API key",
"access.form.todaynic_agent.guide": "TodayNIC.com API only supports calls from agents. Learn more about this: <br><a href=\"https://docs.apipost.net/docs/detail/49dcef10a876000?target_id=371b384\" target=\"_blank\">https://docs.apipost.net/docs/detail/49dcef10a876000?target_id=371b384</a>",
"access.form.ucloud_private_key.label": "UCloud API private key",
"access.form.ucloud_private_key.placeholder": "Please enter UCloud API private key",
"access.form.ucloud_private_key.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/uaccount/api_manage\" target=\"_blank\">https://console.ucloud-global.com/uaccount/api_manage</a>",
@@ -724,10 +729,10 @@
"access.form.westcn_api_password.label": "West.cn agent API password",
"access.form.westcn_api_password.placeholder": "Please enter West.cn agent API password",
"access.form.westcn_agent.guide": "West.cn API only supports calls from agents. Learn more about this: <br><a href=\"https://www.west.cn/CustomerCenter/doc/apiv2.html#12u3001u8eabu4efdu9a8cu8bc10a3ca20id3d12u3001u8eabu4efdu9a8cu8bc13e203ca3e\" target=\"_blank\">https://www.west.cn/CustomerCenter/doc/apiv2.html</a>",
"access.form.xinnet_agent_id.label": "Xinnet agent ID",
"access.form.xinnet_agent_id.placeholder": "Please enter Xinnet agent ID",
"access.form.xinnet_api_password.label": "Xinnet agent API password",
"access.form.xinnet_api_password.placeholder": "Please enter Xinnet agent API password",
"access.form.xinnet_agent.guide": "Xinnet API only supports calls from agents. Learn more about this: <br><a href=\"https://apidoc.xin.cn/doc-7283837\" target=\"_blank\">https://apidoc.xin.cn/doc-7283837</a>",
"access.form.xinnet_agent_id.label": "Xinnet.com agent ID",
"access.form.xinnet_agent_id.placeholder": "Please enter Xinnet.com agent ID",
"access.form.xinnet_api_password.label": "Xinnet.com agent API password",
"access.form.xinnet_api_password.placeholder": "Please enter Xinnet.com agent API password",
"access.form.xinnet_agent.guide": "Xinnet.com API only supports calls from agents. Learn more about this: <br><a href=\"https://apidoc.xin.cn/doc-7283837\" target=\"_blank\">https://apidoc.xin.cn/doc-7283837</a>",
"access.form.zerossl_eab.guide": "Learn more about using EAB key in ZeroSSL: <br><a href=\"https://zerossl.com/documentation/acme/\" target=\"_blank\">https://zerossl.com/documentation/acme/</a>"
}
+2 -1
View File
@@ -195,6 +195,7 @@
"provider.ucloud_uewaf": "UCloud - UEWAF (Enterprise Web Application Firewall)",
"provider.ucloud_upathx": "UCloud - UPathX (Global Dynamic Acceleration)",
"provider.ucloud_us3": "UCloud - US3 (Object-based Storage)",
"provider.todaynic": "TodayNIC.com",
"provider.unicloud": "uniCloud (DCloud)",
"provider.unicloud_webhost": "uniCloud - Web Host",
"provider.upyun": "UPYUN",
@@ -221,7 +222,7 @@
"provider.webhook": "Webhook",
"provider.wecombot": "WeCom Bot",
"provider.westcn": "West.cn",
"provider.xinnet": "Xinnet",
"provider.xinnet": "Xinnet.com",
"provider.zerossl": "ZeroSSL",
"provider.category.all": "All",
+5
View File
@@ -644,6 +644,11 @@
"access.form.tencentcloud_secret_key.label": "腾讯云 SecretKey",
"access.form.tencentcloud_secret_key.placeholder": "请输入腾讯云 SecretKey",
"access.form.tencentcloud_secret_key.tooltip": "这是什么?请参阅 <a href=\"https://cloud.tencent.com/document/product/598/40488\" target=\"_blank\">https://cloud.tencent.com/document/product/598/40488</a>",
"access.form.todaynic_user_id.label": "时代互联代理商用户 ID",
"access.form.todaynic_user_id.placeholder": "请输入时代互联代理商用户 ID",
"access.form.todaynic_api_key.label": "时代互联代理商 API Key",
"access.form.todaynic_api_key.placeholder": "请输入时代互联代理商 API Key",
"access.form.todaynic_agent.guide": "时代互联 API 仅支持代理商调用。点击下方链接了解更多:<br><a href=\"https://docs.apipost.net/docs/detail/49dcef10a876000?target_id=371b384\" target=\"_blank\">https://docs.apipost.net/docs/detail/49dcef10a876000?target_id=371b384</a>",
"access.form.ucloud_private_key.label": "优刻得 API 私钥",
"access.form.ucloud_private_key.placeholder": "请输入优刻得 API 私钥",
"access.form.ucloud_private_key.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/uaccount/api_manage\" target=\"_blank\">https://console.ucloud.cn/uaccount/api_manage</a>",
+1
View File
@@ -187,6 +187,7 @@
"provider.tencentcloud_ssl_upload": "腾讯云 - 上传到 SSL 证书服务",
"provider.tencentcloud_vod": "腾讯云 - 云点播 VOD",
"provider.tencentcloud_waf": "腾讯云 - Web 应用防火墙 WAF",
"provider.todaynic": "时代互联",
"provider.ucloud": "优刻得",
"provider.ucloud_ualb": "优刻得 - 应用型负载均衡 ALB",
"provider.ucloud_ucdn": "优刻得 - 云分发 UCDN",