diff --git a/README.md b/README.md
index 0c9b73a2..5aea2374 100644
--- a/README.md
+++ b/README.md
@@ -95,6 +95,7 @@ make local.run
| [AWS Route53](https://aws.amazon.com/route53/) | |
| [Azure](https://azure.microsoft.com/) | |
| [CloudFlare](https://www.cloudflare.com/) | |
+| [ClouDNS](https://www.cloudns.net//) | |
| [GoDaddy](https://www.godaddy.com/) | |
| [Name.com](https://www.name.com/) | |
| [NameSilo](https://www.namesilo.com/) | |
diff --git a/README_EN.md b/README_EN.md
index b915cb95..c360ce63 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -94,6 +94,7 @@ The following DNS providers are supported:
| [AWS Route53](https://aws.amazon.com/route53/) | |
| [Azure DNS](https://azure.microsoft.com/) | |
| [CloudFlare](https://www.cloudflare.com/) | |
+| [ClouDNS](https://www.cloudns.net//) | |
| [GoDaddy](https://www.godaddy.com/) | |
| [Name.com](https://www.name.com/) | |
| [NameSilo](https://www.namesilo.com/) | |
diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go
index 75d9d3ce..8329ff54 100644
--- a/internal/applicant/providers.go
+++ b/internal/applicant/providers.go
@@ -11,6 +11,7 @@ import (
providerAWSRoute53 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53"
providerAzureDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns"
providerCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare"
+ providerClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns"
providerGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy"
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"
@@ -114,6 +115,22 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
+ case domain.ApplyDNSProviderTypeClouDNS:
+ {
+ access := domain.AccessConfigForClouDNS{}
+ if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil {
+ return nil, fmt.Errorf("failed to decode provider access config: %w", err)
+ }
+
+ applicant, err := providerClouDNS.NewChallengeProvider(&providerClouDNS.ClouDNSApplicantConfig{
+ AuthId: access.AuthId,
+ AuthPassword: access.AuthPassword,
+ DnsPropagationTimeout: options.DnsPropagationTimeout,
+ DnsTTL: options.DnsTTL,
+ })
+ return applicant, err
+ }
+
case domain.ApplyDNSProviderTypeGoDaddy:
{
access := domain.AccessConfigForGoDaddy{}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index 7f49c897..70128622 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -63,6 +63,11 @@ type AccessConfigForCloudflare struct {
DnsApiToken string `json:"dnsApiToken"`
}
+type AccessConfigForClouDNS struct {
+ AuthId string `json:"authId"`
+ AuthPassword string `json:"authPassword"`
+}
+
type AccessConfigForDogeCloud struct {
AccessKey string `json:"accessKey"`
SecretKey string `json:"secretKey"`
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index ea950bbd..71e048b3 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -16,6 +16,7 @@ const (
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
AccessProviderTypeCloudflare = AccessProviderType("cloudflare")
+ AccessProviderTypeClouDNS = AccessProviderType("cloudns")
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
AccessProviderTypeEdgio = AccessProviderType("edgio")
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
@@ -53,6 +54,7 @@ const (
ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType("aws-route53")
ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns")
ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare")
+ ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns")
ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy")
ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS]
ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns")
diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns/cloudns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns/cloudns.go
new file mode 100644
index 00000000..09aac6df
--- /dev/null
+++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns/cloudns.go
@@ -0,0 +1,39 @@
+package cloudns
+
+import (
+ "errors"
+ "time"
+
+ "github.com/go-acme/lego/v4/challenge"
+ "github.com/go-acme/lego/v4/providers/dns/cloudns"
+)
+
+type ClouDNSApplicantConfig struct {
+ AuthId string `json:"authId"`
+ AuthPassword string `json:"authPassword"`
+ DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
+ DnsTTL int32 `json:"dnsTTL,omitempty"`
+}
+
+func NewChallengeProvider(config *ClouDNSApplicantConfig) (challenge.Provider, error) {
+ if config == nil {
+ return nil, errors.New("config is nil")
+ }
+
+ providerConfig := cloudns.NewDefaultConfig()
+ providerConfig.AuthID = config.AuthId
+ providerConfig.AuthPassword = config.AuthPassword
+ if config.DnsPropagationTimeout != 0 {
+ providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
+ }
+ if config.DnsTTL != 0 {
+ providerConfig.TTL = int(config.DnsTTL)
+ }
+
+ provider, err := cloudns.NewDNSProviderConfig(providerConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ return provider, nil
+}
diff --git a/ui/.eslintrc.cjs b/ui/.eslintrc.cjs
index 6bd9b5fd..d82915f5 100644
--- a/ui/.eslintrc.cjs
+++ b/ui/.eslintrc.cjs
@@ -84,6 +84,7 @@ module.exports = {
pathGroupsExcludedImportTypes: ["builtin"],
alphabetize: {
order: "asc",
+ caseInsensitive: true,
},
},
],
diff --git a/ui/public/imgs/providers/cloudns.svg b/ui/public/imgs/providers/cloudns.svg
new file mode 100644
index 00000000..44cf1ed2
--- /dev/null
+++ b/ui/public/imgs/providers/cloudns.svg
@@ -0,0 +1,99 @@
+
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index 9671ffa2..e007eb89 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -10,21 +10,22 @@ import { ACCESS_PROVIDERS } from "@/domain/provider";
import { useAntdForm, useAntdFormName } from "@/hooks";
import AccessFormACMEHttpReqConfig from "./AccessFormACMEHttpReqConfig";
-import AccessFormAWSConfig from "./AccessFormAWSConfig";
import AccessFormAliyunConfig from "./AccessFormAliyunConfig";
+import AccessFormAWSConfig from "./AccessFormAWSConfig";
import AccessFormAzureConfig from "./AccessFormAzureConfig";
import AccessFormBaiduCloudConfig from "./AccessFormBaiduCloudConfig";
import AccessFormBytePlusConfig from "./AccessFormBytePlusConfig";
import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig";
+import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig";
import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig";
import AccessFormEdgioConfig from "./AccessFormEdgioConfig";
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 AccessFormNS1Config from "./AccessFormNS1Config";
import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig";
import AccessFormQiniuConfig from "./AccessFormQiniuConfig";
import AccessFormRainYunConfig from "./AccessFormRainYunConfig";
@@ -101,6 +102,8 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.CLOUDFLARE:
return ;
+ case ACCESS_PROVIDERS.CLOUDNS:
+ return ;
case ACCESS_PROVIDERS.DOGECLOUD:
return ;
case ACCESS_PROVIDERS.GODADDY:
diff --git a/ui/src/components/access/AccessFormClouDNSConfig.tsx b/ui/src/components/access/AccessFormClouDNSConfig.tsx
new file mode 100644
index 00000000..4472eeea
--- /dev/null
+++ b/ui/src/components/access/AccessFormClouDNSConfig.tsx
@@ -0,0 +1,76 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { type AccessConfigForClouDNS } from "@/domain/access";
+
+type AccessFormClouDNSConfigFieldValues = Nullish;
+
+export type AccessFormClouDNSConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormClouDNSConfigFieldValues;
+ onValuesChange?: (values: AccessFormClouDNSConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormClouDNSConfigFieldValues => {
+ return {
+ authId: "",
+ authPassword: "",
+ };
+};
+
+const AccessFormClouDNSConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormClouDNSConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ authId: z
+ .string()
+ .trim()
+ .min(1, t("access.form.cloudns_auth_id.placeholder"))
+ .max(64, t("common.errmsg.string_max", { max: 64 })),
+ authPassword: z
+ .string()
+ .min(1, t("access.form.cloudns_auth_password.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 AccessFormClouDNSConfig;
diff --git a/ui/src/components/notification/NotifyChannelEditForm.tsx b/ui/src/components/notification/NotifyChannelEditForm.tsx
index 2fa9b5b3..d818ee4c 100644
--- a/ui/src/components/notification/NotifyChannelEditForm.tsx
+++ b/ui/src/components/notification/NotifyChannelEditForm.tsx
@@ -10,8 +10,8 @@ import NotifyChannelEditFormEmailFields from "./NotifyChannelEditFormEmailFields
import NotifyChannelEditFormLarkFields from "./NotifyChannelEditFormLarkFields";
import NotifyChannelEditFormServerChanFields from "./NotifyChannelEditFormServerChanFields";
import NotifyChannelEditFormTelegramFields from "./NotifyChannelEditFormTelegramFields";
-import NotifyChannelEditFormWeComFields from "./NotifyChannelEditFormWeComFields";
import NotifyChannelEditFormWebhookFields from "./NotifyChannelEditFormWebhookFields";
+import NotifyChannelEditFormWeComFields from "./NotifyChannelEditFormWeComFields";
type NotifyChannelEditFormFieldValues = NotifyChannelsSettingsContent[keyof NotifyChannelsSettingsContent];
diff --git a/ui/src/components/workflow/WorkflowElement.tsx b/ui/src/components/workflow/WorkflowElement.tsx
index d2272ef0..3aa70ff3 100644
--- a/ui/src/components/workflow/WorkflowElement.tsx
+++ b/ui/src/components/workflow/WorkflowElement.tsx
@@ -64,4 +64,3 @@ const WorkflowElement = ({ node, disabled, branchId, branchIndex }: WorkflowElem
};
export default memo(WorkflowElement);
-
diff --git a/ui/src/components/workflow/node/ApplyNode.tsx b/ui/src/components/workflow/node/ApplyNode.tsx
index 556188d9..6e090616 100644
--- a/ui/src/components/workflow/node/ApplyNode.tsx
+++ b/ui/src/components/workflow/node/ApplyNode.tsx
@@ -8,8 +8,8 @@ import { useZustandShallowSelector } from "@/hooks";
import { useContactEmailsStore } from "@/stores/contact";
import { useWorkflowStore } from "@/stores/workflow";
-import ApplyNodeConfigForm, { type ApplyNodeConfigFormInstance } from "./ApplyNodeConfigForm";
import SharedNode, { type SharedNodeProps } from "./_SharedNode";
+import ApplyNodeConfigForm, { type ApplyNodeConfigFormInstance } from "./ApplyNodeConfigForm";
export type ApplyNodeProps = SharedNodeProps;
diff --git a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx
index 2280fbb8..1792163e 100644
--- a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx
@@ -21,10 +21,10 @@ import {
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
-import ModalForm from "@/components/ModalForm";
-import MultipleInput from "@/components/MultipleInput";
import AccessEditModal from "@/components/access/AccessEditModal";
import AccessSelect from "@/components/access/AccessSelect";
+import ModalForm from "@/components/ModalForm";
+import MultipleInput from "@/components/MultipleInput";
import ApplyDNSProviderSelect from "@/components/provider/ApplyDNSProviderSelect";
import { ACCESS_USAGES, APPLY_DNS_PROVIDERS, accessProvidersMap, applyDNSProvidersMap } from "@/domain/provider";
import { type WorkflowNodeConfigForApply } from "@/domain/workflow";
diff --git a/ui/src/components/workflow/node/ConditionNode.tsx b/ui/src/components/workflow/node/ConditionNode.tsx
index d84e8550..56639692 100644
--- a/ui/src/components/workflow/node/ConditionNode.tsx
+++ b/ui/src/components/workflow/node/ConditionNode.tsx
@@ -2,8 +2,8 @@ import { memo } from "react";
import { MoreOutlined as MoreOutlinedIcon } from "@ant-design/icons";
import { Button, Card, Popover } from "antd";
-import AddNode from "./AddNode";
import SharedNode, { type SharedNodeProps } from "./_SharedNode";
+import AddNode from "./AddNode";
export type ConditionNodeProps = SharedNodeProps & {
branchId: string;
diff --git a/ui/src/components/workflow/node/DeployNode.tsx b/ui/src/components/workflow/node/DeployNode.tsx
index db5830da..9eca1248 100644
--- a/ui/src/components/workflow/node/DeployNode.tsx
+++ b/ui/src/components/workflow/node/DeployNode.tsx
@@ -8,8 +8,8 @@ import { type WorkflowNodeConfigForDeploy, WorkflowNodeType } from "@/domain/wor
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
-import DeployNodeConfigForm, { type DeployNodeConfigFormInstance } from "./DeployNodeConfigForm";
import SharedNode, { type SharedNodeProps } from "./_SharedNode";
+import DeployNodeConfigForm, { type DeployNodeConfigFormInstance } from "./DeployNodeConfigForm";
export type DeployNodeProps = SharedNodeProps;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
index 77d110c6..3d85e6de 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
@@ -5,11 +5,11 @@ import { Alert, Button, Divider, Flex, Form, type FormInstance, Select, Switch,
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
-import Show from "@/components/Show";
import AccessEditModal from "@/components/access/AccessEditModal";
import AccessSelect from "@/components/access/AccessSelect";
import DeployProviderPicker from "@/components/provider/DeployProviderPicker";
import DeployProviderSelect from "@/components/provider/DeployProviderSelect";
+import Show from "@/components/Show";
import { ACCESS_USAGES, DEPLOY_PROVIDERS, accessProvidersMap, deployProvidersMap } from "@/domain/provider";
import { type WorkflowNode, type WorkflowNodeConfigForDeploy } from "@/domain/workflow";
import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks";
diff --git a/ui/src/components/workflow/node/ExecuteResultNode.tsx b/ui/src/components/workflow/node/ExecuteResultNode.tsx
index b53b2a8c..69a0949c 100644
--- a/ui/src/components/workflow/node/ExecuteResultNode.tsx
+++ b/ui/src/components/workflow/node/ExecuteResultNode.tsx
@@ -8,8 +8,8 @@ import {
import { Button, Card, Popover, theme } from "antd";
import { WorkflowNodeType } from "@/domain/workflow";
-import AddNode from "./AddNode";
import SharedNode, { type SharedNodeProps } from "./_SharedNode";
+import AddNode from "./AddNode";
export type ConditionNodeProps = SharedNodeProps & {
branchId: string;
diff --git a/ui/src/components/workflow/node/NotifyNode.tsx b/ui/src/components/workflow/node/NotifyNode.tsx
index 1b739e8f..4ac35ab5 100644
--- a/ui/src/components/workflow/node/NotifyNode.tsx
+++ b/ui/src/components/workflow/node/NotifyNode.tsx
@@ -8,8 +8,8 @@ import { type WorkflowNodeConfigForNotify, WorkflowNodeType } from "@/domain/wor
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
-import NotifyNodeConfigForm, { type NotifyNodeConfigFormInstance } from "./NotifyNodeConfigForm";
import SharedNode, { type SharedNodeProps } from "./_SharedNode";
+import NotifyNodeConfigForm, { type NotifyNodeConfigFormInstance } from "./NotifyNodeConfigForm";
export type NotifyNodeProps = SharedNodeProps;
diff --git a/ui/src/components/workflow/node/StartNode.tsx b/ui/src/components/workflow/node/StartNode.tsx
index 1df65bc1..900793fa 100644
--- a/ui/src/components/workflow/node/StartNode.tsx
+++ b/ui/src/components/workflow/node/StartNode.tsx
@@ -7,8 +7,8 @@ import { WORKFLOW_TRIGGERS, type WorkflowNodeConfigForStart, WorkflowNodeType }
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
-import StartNodeConfigForm, { type StartNodeConfigFormInstance } from "./StartNodeConfigForm";
import SharedNode, { type SharedNodeProps } from "./_SharedNode";
+import StartNodeConfigForm, { type StartNodeConfigFormInstance } from "./StartNodeConfigForm";
export type StartNodeProps = SharedNodeProps;
diff --git a/ui/src/components/workflow/node/UploadNode.tsx b/ui/src/components/workflow/node/UploadNode.tsx
index 3d0e20c3..25850743 100644
--- a/ui/src/components/workflow/node/UploadNode.tsx
+++ b/ui/src/components/workflow/node/UploadNode.tsx
@@ -8,8 +8,8 @@ import { WorkflowNodeType } from "@/domain/workflow";
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
-import UploadNodeConfigForm, { type UploadNodeConfigFormInstance } from "./UploadNodeConfigForm";
import SharedNode, { type SharedNodeProps } from "./_SharedNode";
+import UploadNodeConfigForm, { type UploadNodeConfigFormInstance } from "./UploadNodeConfigForm";
export type UploadNodeProps = SharedNodeProps;
diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts
index 8efe8b27..14b31be6 100644
--- a/ui/src/domain/access.ts
+++ b/ui/src/domain/access.ts
@@ -15,6 +15,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForBaiduCloud
| AccessConfigForBytePlus
| AccessConfigForCloudflare
+ | AccessConfigForClouDNS
| AccessConfigForDogeCloud
| AccessConfigForEdgio
| AccessConfigForGoDaddy
@@ -75,6 +76,11 @@ export type AccessConfigForCloudflare = {
dnsApiToken: string;
};
+export type AccessConfigForClouDNS = {
+ authId: string;
+ authPassword: string;
+};
+
export type AccessConfigForDogeCloud = {
accessKey: string;
secretKey: string;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index b6004053..9a05d180 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -11,6 +11,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
BAIDUCLOUD: "baiducloud",
BYTEPLUS: "byteplus",
CLOUDFLARE: "cloudflare",
+ CLOUDNS: "cloudns",
DOGECLOUD: "dogecloud",
GODADDY: "godaddy",
EDGIO: "edgio",
@@ -71,6 +72,7 @@ export const accessProvidersMap: Maphttps://developers.cloudflare.com/fundamentals/api/get-started/create-token/",
+ "access.form.cloudns_auth_id.label": "ClouDNS API user ID",
+ "access.form.cloudns_auth_id.placeholder": "Please enter ClouDNS API user ID",
+ "access.form.cloudns_auth_id.tooltip": "For more information, see https://www.cloudns.net/wiki/article/42/",
+ "access.form.cloudns_auth_password.label": "ClouDNS API user password",
+ "access.form.cloudns_auth_password.placeholder": "Please enter ClouDNS API user password",
+ "access.form.cloudns_auth_password.tooltip": "For more information, see https://www.cloudns.net/wiki/article/42/",
"access.form.dogecloud_access_key.label": "Doge Cloud AccessKey",
"access.form.dogecloud_access_key.placeholder": "Please enter Doge Cloud AccessKey",
"access.form.dogecloud_access_key.tooltip": "For more information, see https://console.dogecloud.com/",
diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json
index 40c4837d..dbe271e7 100644
--- a/ui/src/i18n/locales/en/nls.common.json
+++ b/ui/src/i18n/locales/en/nls.common.json
@@ -55,6 +55,7 @@
"common.provider.byteplus": "BytePlus",
"common.provider.byteplus.cdn": "BytePlus - CDN (Content Delivery Network)",
"common.provider.cloudflare": "Cloudflare",
+ "common.provider.cloudns": "ClouDNS",
"common.provider.dogecloud": "Doge Cloud",
"common.provider.dogecloud.cdn": "Doge Cloud - CDN (Content Delivery Network)",
"common.provider.edgio": "Edgio",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index 8be0c1cc..7d51ad43 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -72,6 +72,12 @@
"access.form.cloudflare_dns_api_token.label": "Cloudflare API Token",
"access.form.cloudflare_dns_api_token.placeholder": "请输入 Cloudflare API Token",
"access.form.cloudflare_dns_api_token.tooltip": "这是什么?请参阅 https://developers.cloudflare.com/fundamentals/api/get-started/create-token/",
+ "access.form.cloudns_auth_id.label": "ClouDNS API 用户 ID",
+ "access.form.cloudns_auth_id.placeholder": "请输入 ClouDNS API 用户 ID",
+ "access.form.cloudns_auth_id.tooltip": "这是什么?请参阅 https://www.cloudns.net/wiki/article/42/",
+ "access.form.cloudns_auth_password.label": "ClouDNS API 用户密码",
+ "access.form.cloudns_auth_password.placeholder": "请输入 ClouDNS API 用户密码",
+ "access.form.cloudns_auth_password.tooltip": "这是什么?请参阅 https://www.cloudns.net/wiki/article/42/",
"access.form.dogecloud_access_key.label": "多吉云 AccessKey",
"access.form.dogecloud_access_key.placeholder": "请输入多吉云 AccessKey",
"access.form.dogecloud_access_key.tooltip": "这是什么?请参阅 https://console.dogecloud.com/",
diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json
index e7e742b8..45072e74 100644
--- a/ui/src/i18n/locales/zh/nls.common.json
+++ b/ui/src/i18n/locales/zh/nls.common.json
@@ -55,6 +55,7 @@
"common.provider.byteplus": "BytePlus",
"common.provider.byteplus.cdn": "BytePlus - 内容分发网络 CDN",
"common.provider.cloudflare": "Cloudflare",
+ "common.provider.cloudns": "ClouDNS",
"common.provider.dogecloud": "多吉云",
"common.provider.dogecloud.cdn": "多吉云 - 内容分发网络 CDN",
"common.provider.edgio": "Edgio",
diff --git a/ui/src/router.tsx b/ui/src/router.tsx
index 31265f6b..0bfa8b41 100644
--- a/ui/src/router.tsx
+++ b/ui/src/router.tsx
@@ -1,9 +1,9 @@
import { createHashRouter } from "react-router-dom";
-import AuthLayout from "./pages/AuthLayout";
-import ConsoleLayout from "./pages/ConsoleLayout";
import AccessList from "./pages/accesses/AccessList";
+import AuthLayout from "./pages/AuthLayout";
import CertificateList from "./pages/certificates/CertificateList";
+import ConsoleLayout from "./pages/ConsoleLayout";
import Dashboard from "./pages/dashboard/Dashboard";
import Login from "./pages/login/Login";
import Settings from "./pages/settings/Settings";