diff --git a/README.md b/README.md
index 3ab77300..ed79b12f 100644
--- a/README.md
+++ b/README.md
@@ -86,20 +86,21 @@ make local.run
[展开查看]
-| 提供商 | 备注 |
-| :--------------------------------------------- | :-------------------------------------- |
-| [阿里云](https://www.aliyun.com/) | |
-| [腾讯云](https://cloud.tencent.com/) | |
-| [华为云](https://www.huaweicloud.com/) | |
-| [火山引擎](https://www.volcengine.com/) | |
-| [AWS Route53](https://aws.amazon.com/route53/) | |
-| [Azure](https://azure.microsoft.com/) | |
-| [CloudFlare](https://www.cloudflare.com/) | |
-| [GoDaddy](https://www.godaddy.com/) | |
-| [Name.com](https://www.name.com/) | |
-| [NameSilo](https://www.namesilo.com/) | |
-| [PowerDNS](https://www.powerdns.com/) | |
-| ACME 代理 HTTP 请求 | 可申请允许通过 HTTP 请求修改 DNS 的域名 |
+| 提供商 | 备注 |
+| :----------------------------------------------------------------- | :-------------------------------------- |
+| [阿里云](https://www.aliyun.com/) | |
+| [腾讯云](https://cloud.tencent.com/) | |
+| [华为云](https://www.huaweicloud.com/) | |
+| [火山引擎](https://www.volcengine.com/) | |
+| [AWS Route53](https://aws.amazon.com/route53/) | |
+| [Azure](https://azure.microsoft.com/) | |
+| [CloudFlare](https://www.cloudflare.com/) | |
+| [GoDaddy](https://www.godaddy.com/) | |
+| [Name.com](https://www.name.com/) | |
+| [NameSilo](https://www.namesilo.com/) | |
+| [IBM NS1 Connect](https://www.ibm.com/cn-zh/products/ns1-connect/) | |
+| [PowerDNS](https://www.powerdns.com/) | |
+| ACME 代理 HTTP 请求 | 可申请允许通过 HTTP 请求修改 DNS 的域名 |
diff --git a/README_EN.md b/README_EN.md
index f0f8338e..9c9aab55 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -85,20 +85,21 @@ The following DNS providers are supported:
[Fold/Unfold to view ...]
-| Provider | Remarks |
-| :--------------------------------------------- | :------------------------------------ |
-| [Alibaba Cloud](https://www.alibabacloud.com/) | |
-| [Tencent Cloud](https://www.tencentcloud.com/) | |
-| [Huawei Cloud](https://www.huaweicloud.com/) | |
-| [Volcengine](https://www.volcengine.com/) | |
-| [AWS Route53](https://aws.amazon.com/route53/) | |
-| [Azure DNS](https://azure.microsoft.com/) | |
-| [CloudFlare](https://www.cloudflare.com/) | |
-| [GoDaddy](https://www.godaddy.com/) | |
-| [Name.com](https://www.name.com/) | |
-| [NameSilo](https://www.namesilo.com/) | |
-| [PowerDNS](https://www.powerdns.com/) | |
-| ACME Proxy HTTP Request | Supports managing DNS by HTTP request |
+| Provider | Remarks |
+| :----------------------------------------------------------- | :------------------------------------ |
+| [Alibaba Cloud](https://www.alibabacloud.com/) | |
+| [Tencent Cloud](https://www.tencentcloud.com/) | |
+| [Huawei Cloud](https://www.huaweicloud.com/) | |
+| [Volcengine](https://www.volcengine.com/) | |
+| [AWS Route53](https://aws.amazon.com/route53/) | |
+| [Azure DNS](https://azure.microsoft.com/) | |
+| [CloudFlare](https://www.cloudflare.com/) | |
+| [GoDaddy](https://www.godaddy.com/) | |
+| [Name.com](https://www.name.com/) | |
+| [NameSilo](https://www.namesilo.com/) | |
+| [IBM NS1 Connect](https://www.ibm.com/products/ns1-connect/) | |
+| [PowerDNS](https://www.powerdns.com/) | |
+| ACME Proxy HTTP Request | Supports managing DNS by HTTP request |
diff --git a/go.mod b/go.mod
index 1ff91571..9d52261f 100644
--- a/go.mod
+++ b/go.mod
@@ -87,6 +87,7 @@ require (
go.mongodb.org/mongo-driver v1.12.0 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
+ gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
diff --git a/go.sum b/go.sum
index 8e6555c8..d41faffa 100644
--- a/go.sum
+++ b/go.sum
@@ -1369,6 +1369,8 @@ gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/ns1/ns1-go.v2 v2.13.0 h1:I5NNqI9Bi1SGK92TVkOvLTwux5LNrix/99H2datVh48=
+gopkg.in/ns1/ns1-go.v2 v2.13.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go
index 795afe08..8e42f4f2 100644
--- a/internal/applicant/providers.go
+++ b/internal/applicant/providers.go
@@ -15,6 +15,7 @@ import (
providerHuaweiCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud"
providerNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom"
providerNameSilo "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo"
+ providerNS1 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1"
providerPowerDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns"
providerTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud"
providerVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine"
@@ -167,6 +168,20 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
+ case domain.ApplyDNSProviderTypeNS1:
+ {
+ access := domain.AccessConfigForNS1{}
+ if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
+ return nil, fmt.Errorf("failed to decode provider access config: %w", err)
+ }
+
+ applicant, err := providerNS1.NewChallengeProvider(&providerNS1.NS1ApplicantConfig{
+ ApiKey: access.ApiKey,
+ PropagationTimeout: options.PropagationTimeout,
+ })
+ return applicant, err
+ }
+
case domain.ApplyDNSProviderTypePowerDNS:
{
access := domain.AccessConfigForPowerDNS{}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index 1893b4ab..dc118482 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -91,6 +91,10 @@ type AccessConfigForNameSilo struct {
ApiKey string `json:"apiKey"`
}
+type AccessConfigForNS1 struct {
+ ApiKey string `json:"apiKey"`
+}
+
type AccessConfigForPowerDNS struct {
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index b90d9b38..dead9c57 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -23,6 +23,7 @@ const (
AccessProviderTypeLocal = AccessProviderType("local")
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
AccessProviderTypeNameSilo = AccessProviderType("namesilo")
+ AccessProviderTypeNS1 = AccessProviderType("ns1")
AccessProviderTypePowerDNS = AccessProviderType("powerdns")
AccessProviderTypeQiniu = AccessProviderType("qiniu")
AccessProviderTypeSSH = AccessProviderType("ssh")
@@ -54,6 +55,7 @@ const (
ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns")
ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom")
ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo")
+ ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1")
ApplyDNSProviderTypePowerDNS = ApplyDNSProviderType("powerdns")
ApplyDNSProviderTypeTencentCloud = ApplyDNSProviderType("tencentcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeTencentCloudDNS]
ApplyDNSProviderTypeTencentCloudDNS = ApplyDNSProviderType("tencentcloud-dns")
diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1/ns1.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1/ns1.go
new file mode 100644
index 00000000..7403ecb8
--- /dev/null
+++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1/ns1.go
@@ -0,0 +1,33 @@
+package ns1
+
+import (
+ "errors"
+ "time"
+
+ "github.com/go-acme/lego/v4/challenge"
+ "github.com/go-acme/lego/v4/providers/dns/ns1"
+)
+
+type NS1ApplicantConfig struct {
+ ApiKey string `json:"apiKey"`
+ PropagationTimeout int32 `json:"propagationTimeout,omitempty"`
+}
+
+func NewChallengeProvider(config *NS1ApplicantConfig) (challenge.Provider, error) {
+ if config == nil {
+ return nil, errors.New("config is nil")
+ }
+
+ providerConfig := ns1.NewDefaultConfig()
+ providerConfig.APIKey = config.ApiKey
+ if config.PropagationTimeout != 0 {
+ providerConfig.PropagationTimeout = time.Duration(config.PropagationTimeout) * time.Second
+ }
+
+ provider, err := ns1.NewDNSProviderConfig(providerConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ return provider, nil
+}
diff --git a/ui/public/imgs/providers/ns1.svg b/ui/public/imgs/providers/ns1.svg
new file mode 100644
index 00000000..07811134
--- /dev/null
+++ b/ui/public/imgs/providers/ns1.svg
@@ -0,0 +1 @@
+
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index b398013a..040e52dd 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -21,6 +21,7 @@ import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig";
import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig";
import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig";
import AccessFormLocalConfig from "./AccessFormLocalConfig";
+import AccessFormNS1Config from "./AccessFormNS1Config";
import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig";
import AccessFormNameSiloConfig from "./AccessFormNameSiloConfig";
import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig";
@@ -111,6 +112,8 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.NAMESILO:
return ;
+ case ACCESS_PROVIDERS.NS1:
+ return ;
case ACCESS_PROVIDERS.POWERDNS:
return ;
case ACCESS_PROVIDERS.QINIU:
diff --git a/ui/src/components/access/AccessFormNS1Config.tsx b/ui/src/components/access/AccessFormNS1Config.tsx
new file mode 100644
index 00000000..5b12feb0
--- /dev/null
+++ b/ui/src/components/access/AccessFormNS1Config.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 AccessConfigForNS1 } from "@/domain/access";
+
+type AccessFormNS1ConfigFieldValues = Nullish;
+
+export type AccessFormNS1ConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormNS1ConfigFieldValues;
+ onValuesChange?: (values: AccessFormNS1ConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormNS1ConfigFieldValues => {
+ return {
+ apiKey: "",
+ };
+};
+
+const AccessFormNS1Config = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormNS1ConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ apiKey: z
+ .string()
+ .min(1, t("access.form.ns1_api_key.placeholder"))
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .trim(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+ }
+ >
+
+
+
+ );
+};
+
+export default AccessFormNS1Config;
diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts
index 01d97d04..ed2bd310 100644
--- a/ui/src/domain/access.ts
+++ b/ui/src/domain/access.ts
@@ -102,6 +102,10 @@ export type AccessConfigForNameSilo = {
apiKey: string;
};
+export type AccessConfigForNS1 = {
+ apiKey: string;
+};
+
export type AccessConfigForPowerDNS = {
apiUrl: string;
apiKey: string;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index 42250e9d..ba1a10f2 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -18,6 +18,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
LOCAL: "local",
NAMEDOTCOM: "namedotcom",
NAMESILO: "namesilo",
+ NS1: "ns1",
POWERDNS: "powerdns",
QINIU: "qiniu",
SSH: "ssh",
@@ -68,6 +69,7 @@ export const accessProvidersMap: Map [
diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json
index 66228013..d609d6e7 100644
--- a/ui/src/i18n/locales/en/nls.access.json
+++ b/ui/src/i18n/locales/en/nls.access.json
@@ -103,6 +103,9 @@
"access.form.namesilo_api_key.label": "NameSilo API key",
"access.form.namesilo_api_key.placeholder": "Please enter NameSilo API key",
"access.form.namesilo_api_key.tooltip": "For more information, see https://www.namesilo.com/support/v2/articles/account-options/api-manager",
+ "access.form.ns1_api_key.label": "NS1 API key",
+ "access.form.ns1_api_key.placeholder": "Please enter NS1 API key",
+ "access.form.ns1_api_key.tooltip": "For more information, see https://www.ibm.com/docs/en/ns1-connect?topic=introduction-using-api",
"access.form.powerdns_api_url.label": "PowerDNS API URL",
"access.form.powerdns_api_url.placeholder": "Please enter PowerDNS API URL",
"access.form.powerdns_api_url.tooltip": "For more information, see https://doc.powerdns.com/authoritative/http-api/index.html#endpoints-and-objects-in-the-api",
diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json
index dc8ea864..7b0199a9 100644
--- a/ui/src/i18n/locales/en/nls.common.json
+++ b/ui/src/i18n/locales/en/nls.common.json
@@ -66,6 +66,7 @@
"common.provider.local": "Local deployment",
"common.provider.namedotcom": "Name.com",
"common.provider.namesilo": "NameSilo",
+ "common.provider.ns1": "NS1 (IBM NS1 Connect)",
"common.provider.powerdns": "PowerDNS",
"common.provider.qiniu": "Qiniu",
"common.provider.qiniu.cdn": "Qiniu - Content Delivery Network (CDN)",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index e24c125b..b9107581 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -45,14 +45,14 @@
"access.form.aws_secret_access_key.label": "AWS SecretAccessKey",
"access.form.aws_secret_access_key.placeholder": "请输入 AWS SecretAccessKey",
"access.form.aws_secret_access_key.tooltip": "这是什么?请参阅 https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_credentials_access-keys.html",
- "access.form.azure_tenant_id.label": "Azure TenantId",
- "access.form.azure_tenant_id.placeholder": "请输入 Azure TenantId",
+ "access.form.azure_tenant_id.label": "Azure 租户 ID",
+ "access.form.azure_tenant_id.placeholder": "请输入 Azure 租户 ID",
"access.form.azure_tenant_id.tooltip": "这是什么?请参阅 https://learn.microsoft.com/zh-cn/azure/azure-portal/get-subscription-tenant-id",
- "access.form.azure_client_id.label": "Azure ClientId",
- "access.form.azure_client_id.placeholder": "请输入 Azure ClientId",
+ "access.form.azure_client_id.label": "Azure 客户端 ID",
+ "access.form.azure_client_id.placeholder": "请输入 Azure 客户端 ID",
"access.form.azure_client_id.tooltip": "这是什么?请参阅 https://learn.microsoft.com/zh-cn/azure/azure-monitor/logs/api/register-app-for-token",
- "access.form.azure_client_secret.label": "Azure ClientSecret",
- "access.form.azure_client_secret.placeholder": "请输入 Azure ClientSecret",
+ "access.form.azure_client_secret.label": "Azure 客户端密码",
+ "access.form.azure_client_secret.placeholder": "请输入 Azure 客户端密码",
"access.form.azure_client_secret.tooltip": "这是什么?请参阅 https://learn.microsoft.com/zh-cn/azure/azure-monitor/logs/api/register-app-for-token",
"access.form.azure_cloud_name.label": "Azure 主权云环境(可选)",
"access.form.azure_cloud_name.placeholder": "请输入 Azure 主权云环境(例如:public)",
@@ -103,6 +103,9 @@
"access.form.namesilo_api_key.label": "NameSilo API Key",
"access.form.namesilo_api_key.placeholder": "请输入 NameSilo API Key",
"access.form.namesilo_api_key.tooltip": "这是什么?请参阅 https://www.namesilo.com/support/v2/articles/account-options/api-manager",
+ "access.form.ns1_api_key.label": "NS1 API Key",
+ "access.form.ns1_api_key.placeholder": "请输入 NS1 API Key",
+ "access.form.ns1_api_key.tooltip": "这是什么?请参阅 https://www.ibm.com/docs/zh/ns1-connect?topic=introduction-using-api",
"access.form.powerdns_api_url.label": "PowerDNS API URL",
"access.form.powerdns_api_url.placeholder": "请输入 PowerDNS API URL",
"access.form.powerdns_api_url.tooltip": "这是什么?请参阅 https://doc.powerdns.com/authoritative/http-api/index.html#endpoints-and-objects-in-the-api",
diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json
index 80d6ccc9..0951152a 100644
--- a/ui/src/i18n/locales/zh/nls.common.json
+++ b/ui/src/i18n/locales/zh/nls.common.json
@@ -66,6 +66,7 @@
"common.provider.local": "本地部署",
"common.provider.namedotcom": "Name.com",
"common.provider.namesilo": "NameSilo",
+ "common.provider.ns1": "NS1(IBM NS1 Connect)",
"common.provider.powerdns": "PowerDNS",
"common.provider.qiniu": "七牛云",
"common.provider.qiniu.cdn": "七牛云 - 内容分发网络 CDN",