mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
feat(provider): new acme dns-01 provider: regru
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
package certifiers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/certimate-go/certimate/pkg/core/certifier/challengers/dns01/regru"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ACMEDns01Registries.MustRegister(domain.ACMEDns01ProviderTypeRegru, func(options *ProviderFactoryOptions) (core.ACMEChallenger, error) {
|
||||
credentials := domain.AccessConfigForRegru{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := regru.NewChallenger(®ru.ChallengerConfig{
|
||||
Username: credentials.Username,
|
||||
Password: credentials.Password,
|
||||
MtlsCertificate: credentials.MtlsCertificate,
|
||||
MtlsPrivateKey: credentials.MtlsPrivateKey,
|
||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||
DnsTTL: options.DnsTTL,
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
}
|
||||
@@ -485,6 +485,13 @@ type AccessConfigForRatPanel struct {
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForRegru struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
MtlsCertificate string `json:"mtlsCertificate,omitempty"`
|
||||
MtlsPrivateKey string `json:"mtlsPrivateKey,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForRFC2136 struct {
|
||||
Host string `json:"host"`
|
||||
Port int32 `json:"port"`
|
||||
|
||||
@@ -104,6 +104,7 @@ const (
|
||||
AccessProviderTypeQingCloud = AccessProviderType("qingcloud")
|
||||
AccessProviderTypeRainYun = AccessProviderType("rainyun")
|
||||
AccessProviderTypeRatPanel = AccessProviderType("ratpanel")
|
||||
AccessProviderTypeRegru = AccessProviderType("regru")
|
||||
AccessProviderTypeRFC2136 = AccessProviderType("rfc2136")
|
||||
AccessProviderTypeS3 = AccessProviderType("s3")
|
||||
AccessProviderTypeSafeLine = AccessProviderType("safeline")
|
||||
@@ -243,6 +244,7 @@ const (
|
||||
ACMEDns01ProviderTypeQingCloud = ACMEDns01ProviderType(AccessProviderTypeQingCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeQingCloudDNS]
|
||||
ACMEDns01ProviderTypeQingCloudDNS = ACMEDns01ProviderType(AccessProviderTypeQingCloud + "-dns")
|
||||
ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun)
|
||||
ACMEDns01ProviderTypeRegru = ACMEDns01ProviderType(AccessProviderTypeRegru)
|
||||
ACMEDns01ProviderTypeRFC2136 = ACMEDns01ProviderType(AccessProviderTypeRFC2136)
|
||||
ACMEDns01ProviderTypeSpaceship = ACMEDns01ProviderType(AccessProviderTypeSpaceship)
|
||||
ACMEDns01ProviderTypeTechnitiumDNS = ACMEDns01ProviderType(AccessProviderTypeTechnitiumDNS)
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package regru
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v5/providers/dns/regru"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core/certifier"
|
||||
)
|
||||
|
||||
type ChallengerConfig struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
MtlsCertificate string `json:"mtlsCertificate,omitempty"`
|
||||
MtlsPrivateKey string `json:"mtlsPrivateKey,omitempty"`
|
||||
DnsPropagationTimeout int `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallenger(config *ChallengerConfig) (certifier.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := regru.NewDefaultConfig()
|
||||
providerConfig.Username = config.Username
|
||||
providerConfig.Password = config.Password
|
||||
providerConfig.TLSCert = config.MtlsCertificate
|
||||
providerConfig.TLSKey = config.MtlsPrivateKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
|
||||
provider, err := regru.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-20 0 740 210" width="200" height="200"><path fill-rule="evenodd" clip-rule="evenodd" d="M485.533 39.512c14.29 0 20.146 1.513 29.814 5.06 11.33 4.158 20.263 13.032 24.449 24.287 7.432 19.99 7.531 46.628 0 66.884-4.186 11.255-13.119 20.13-24.449 24.287-9.552 3.505-14.503 5.265-31.067 5.27-12.205.003-26.537-5.27-30.95-9.578l-2.912 2.443c3.869 4.611 5.99 13.314 5.99 19.333V210h-37V41.985h33.449a16.465 16.465 0 0 1-1.897 7.636l2.793 1.017s10.946-11.126 31.78-11.126Zm11.514 33.203c-8.603-3.697-20.857-3.62-29.279 0-5.036 2.164-9.006 6.783-10.867 12.641-1.243 3.918-2.019 8.295-2.333 12.782a53.916 53.916 0 0 0-.158 4.408c-.019 6.176.815 12.349 2.491 17.629 1.861 5.858 5.831 10.479 10.867 12.643 8.602 3.697 20.856 3.62 29.279 0 5.036-2.164 9.007-6.784 10.867-12.643 1.694-5.335 2.517-11.522 2.491-17.68a53.497 53.497 0 0 0-.151-4.244c-.311-4.548-1.094-8.968-2.34-12.895-1.86-5.858-5.832-10.477-10.867-12.641Z" fill="#181818"></path><path d="M622.233 113.643a68.127 68.127 0 0 1 4.412 16.759l.281 3.029h3.808l.3-3.029a83.96 83.96 0 0 1 4.046-16.905l25.177-71.486H700l-57.151 144.205v.002c-3.399 8.222-7.926 14.186-13.449 17.72l-.057.038c-5.37 3.69-13.927 5.562-25.433 5.562h-33.589v-33.542H599.8c3.127 0 6.075-1.464 7.966-3.955l1.292-1.702-55.651-128.328h39.807l29.019 71.632Z" fill="#181818"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M169.215 44.782c18.951-6.953 46.522-7.101 65.878 0 11.33 4.158 20.264 13.032 24.449 24.287 4.79 12.882 6.533 28.526 5.152 43.443h-24.583v.037h-65.179c.41 2.577.982 5.059 1.715 7.37 1.861 5.858 5.831 10.479 10.867 12.643 8.602 3.697 20.856 3.62 29.279 0 3.278-1.408 6.105-3.858 8.192-7.013h37.642a83.21 83.21 0 0 1-3.085 10.404c-4.185 11.255-13.119 20.13-24.449 24.287-18.952 6.953-46.522 7.102-65.878 0-11.33-4.157-20.264-13.032-24.449-24.287-7.433-19.989-7.532-46.628 0-66.884 4.185-11.255 13.118-20.13 24.449-24.287Zm47.578 27.677c-8.603-3.697-20.857-3.62-29.279 0-5.036 2.164-9.006 6.784-10.867 12.643-.743 2.343-1.319 4.85-1.729 7.447h54.471c-.412-2.606-.988-5.114-1.729-7.447-1.86-5.859-5.832-10.479-10.867-12.643Z" fill="#181818"></path><path d="M378.648 128.502c3.45 0 6.899.517 9.713 1.549a11.17 11.17 0 0 1 6.643 6.593c2.193 5.897 2.193 13.821 0 19.717a11.17 11.17 0 0 1-6.643 6.593c-5.627 2.064-13.797 2.065-19.425 0a11.169 11.169 0 0 1-6.642-6.593c-2.193-5.897-2.193-13.82 0-19.717a11.171 11.171 0 0 1 6.642-6.593c2.814-1.032 6.263-1.549 9.712-1.549Z" fill="#181818"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M65.903.003C77.23.086 88.545 1.779 97.93 5.13c11.33 4.046 20.264 12.682 24.449 23.636 6.612 17.304 6.612 40.557 0 57.862-3.242 8.485-11.757 20.051-24.617 24.331-11.68 3.887-22.044 4.364-30.822 4.401-9.654.042-24.818-1.286-31.185-7l-2.208 1.853c2.048 3.298 3.161 6.122 3.454 9.975 1.257 16.537 3.009 42.355 3.01 42.387H0V0h65.903v.003ZM37 82.796h29.677v-.034c4.638-.157 9.18-.952 12.953-2.371 5.035-1.895 9.006-5.94 10.866-11.068 2.38-6.563 2.264-17.01 0-23.253-1.86-5.128-5.83-9.171-10.866-11.066-3.817-1.436-8.353-2.22-12.953-2.373v-.03H37v50.195Z" fill="#181818"></path><path d="M377.154 75.518h-52.901v86.984h-37.099v-49.298c0-17.391 0-29.957 1.338-39.77 1.369-10.035 4.055-16.39 8.984-21.248 4.929-4.859 11.376-7.506 21.556-8.856 9.958-1.32 22.703-1.32 40.344-1.32h17.778v33.508Z" fill="#181818"></path></svg>
|
||||
@@ -88,6 +88,7 @@ import AccessConfigFieldsProviderQingCloud from "./AccessConfigFieldsProviderQin
|
||||
import AccessConfigFieldsProviderQiniu from "./AccessConfigFieldsProviderQiniu";
|
||||
import AccessConfigFieldsProviderRainYun from "./AccessConfigFieldsProviderRainYun";
|
||||
import AccessConfigFieldsProviderRatPanel from "./AccessConfigFieldsProviderRatPanel";
|
||||
import AccessConfigFieldsProviderRegru from "./AccessConfigFieldsProviderRegru";
|
||||
import AccessConfigFieldsProviderRFC2136 from "./AccessConfigFieldsProviderRFC2136";
|
||||
import AccessConfigFieldsProviderS3 from "./AccessConfigFieldsProviderS3";
|
||||
import AccessConfigFieldsProviderSafeLine from "./AccessConfigFieldsProviderSafeLine";
|
||||
@@ -207,6 +208,7 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
|
||||
[ACCESS_PROVIDERS.QINIU]: AccessConfigFieldsProviderQiniu,
|
||||
[ACCESS_PROVIDERS.RAINYUN]: AccessConfigFieldsProviderRainYun,
|
||||
[ACCESS_PROVIDERS.RATPANEL]: AccessConfigFieldsProviderRatPanel,
|
||||
[ACCESS_PROVIDERS.REGRU]: AccessConfigFieldsProviderRegru,
|
||||
[ACCESS_PROVIDERS.RFC2136]: AccessConfigFieldsProviderRFC2136,
|
||||
[ACCESS_PROVIDERS.S3]: AccessConfigFieldsProviderS3,
|
||||
[ACCESS_PROVIDERS.SAFELINE]: AccessConfigFieldsProviderSafeLine,
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import FileTextInput from "@/components/FileTextInput";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const AccessConfigFormFieldsProviderRegru = () => {
|
||||
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.regru_username.label")} rules={[formRule]}>
|
||||
<Input autoComplete="new-password" placeholder={t("access.form.regru_username.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name={[parentNamePath, "password"]} initialValue={initialValues.password} label={t("access.form.regru_password.label")} rules={[formRule]}>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.regru_password.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "mtlsCertificate"]}
|
||||
initialValue={initialValues.mtlsCertificate}
|
||||
label={t("access.form.regru_mtls_certificate.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.regru_mtls_certificate.tooltip") }}></span>}
|
||||
>
|
||||
<FileTextInput autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.regru_mtls_certificate.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "mtlsPrivateKey"]}
|
||||
initialValue={initialValues.mtlsPrivateKey}
|
||||
label={t("access.form.regru_mtls_private_key.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.regru_mtls_private_key.tooltip") }}></span>}
|
||||
>
|
||||
<FileTextInput autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.regru_mtls_private_key.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
username: "",
|
||||
password: "",
|
||||
mtlsCertificate: "",
|
||||
mtlsPrivateKey: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z
|
||||
.object({
|
||||
username: z.string().nonempty(),
|
||||
password: z.string().nonempty(),
|
||||
mtlsCertificate: z.string().nullish(),
|
||||
mtlsPrivateKey: z.string().nullish(),
|
||||
})
|
||||
.superRefine((values, ctx) => {
|
||||
const scMtlsCertificate = z.string().nonempty();
|
||||
const spMtlsCertificate = scMtlsCertificate.safeParse(values.mtlsCertificate);
|
||||
const scMtlsPrivateKey = z.string().nonempty();
|
||||
const spMtlsPrivateKey = scMtlsPrivateKey.safeParse(values.mtlsPrivateKey);
|
||||
if (!spMtlsCertificate.success && spMtlsPrivateKey.success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("access.form.regru_mtls_certificate.errmsg.invalid"),
|
||||
path: ["mtlsCertificate"],
|
||||
});
|
||||
} else if (spMtlsCertificate.success && !spMtlsPrivateKey.success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("access.form.regru_mtls_private_key.errmsg.invalid"),
|
||||
path: ["mtlsPrivateKey"],
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProviderRegru, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@@ -104,6 +104,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
QINIU: "qiniu",
|
||||
RAINYUN: "rainyun",
|
||||
RATPANEL: "ratpanel",
|
||||
REGRU: "regru",
|
||||
RFC2136: "rfc2136",
|
||||
S3: "s3",
|
||||
SAFELINE: "safeline",
|
||||
@@ -240,6 +241,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.NS1, "provider.ns1", "/imgs/providers/ns1.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.OVHCLOUD, "provider.ovhcloud", "/imgs/providers/ovhcloud.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.PORKBUN, "provider.porkbun", "/imgs/providers/porkbun.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.REGRU, "provider.regru", "/imgs/providers/regru.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.SPACESHIP, "provider.spaceship", "/imgs/providers/spaceship.png", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.VULTR, "provider.vultr", "/imgs/providers/vultr.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.CMCCCLOUD, "provider.cmcccloud", "/imgs/providers/cmcccloud.svg", [ACCESS_USAGES.DNS]],
|
||||
@@ -417,6 +419,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
|
||||
QINGCLOUD: `${ACCESS_PROVIDERS.QINGCLOUD}`, // 兼容旧值,等同于 `QINGCLOUD_DNS`
|
||||
QINGCLOUD_DNS: `${ACCESS_PROVIDERS.QINGCLOUD}-dns`,
|
||||
RAINYUN: `${ACCESS_PROVIDERS.RAINYUN}`,
|
||||
REGRU: `${ACCESS_PROVIDERS.REGRU}`,
|
||||
RFC2136: `${ACCESS_PROVIDERS.RFC2136}`,
|
||||
SPACESHIP: `${ACCESS_PROVIDERS.SPACESHIP}`,
|
||||
UCLOUD: `${ACCESS_PROVIDERS.UCLOUD}`, // 兼容旧值,等同于 `UCLOUD_UDNR`
|
||||
@@ -490,6 +493,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
|
||||
[ACME_DNS01_PROVIDERS.NS1, "provider.ns1"],
|
||||
[ACME_DNS01_PROVIDERS.OVHCLOUD, "provider.ovhcloud"],
|
||||
[ACME_DNS01_PROVIDERS.PORKBUN, "provider.porkbun"],
|
||||
[ACME_DNS01_PROVIDERS.REGRU, "provider.regru"],
|
||||
[ACME_DNS01_PROVIDERS.SPACESHIP, "provider.spaceship"],
|
||||
[ACME_DNS01_PROVIDERS.VERCEL, "provider.vercel"],
|
||||
[ACME_DNS01_PROVIDERS.VULTR, "provider.vultr"],
|
||||
|
||||
@@ -1074,6 +1074,30 @@
|
||||
"placeholder": "Please enter RatPanel access token",
|
||||
"tooltip": "For more information, see <a href=\"https://ratpanel.github.io/advanced/api.html\" target=\"_blank\">https://ratpanel.github.io/advanced/api.html</a>"
|
||||
},
|
||||
"regru_username": {
|
||||
"label": "Рег.ру username",
|
||||
"placeholder": "Please enter Рег.ру username"
|
||||
},
|
||||
"regru_password": {
|
||||
"label": "Рег.ру password",
|
||||
"placeholder": "Please enter Рег.ру password or API password"
|
||||
},
|
||||
"regru_mtls_certificate": {
|
||||
"label": "Рег.ру mTLS certificate (Optional)",
|
||||
"placeholder": "Please enter Рег.ру mTLS certificate",
|
||||
"errmsg": {
|
||||
"invalid": "Invalid certificate"
|
||||
},
|
||||
"tooltip": "For more information, see <a href=\"https://www.reg.ru/user/account/#/settings/api/\" target=\"_blank\">https://www.reg.ru/user/account/#/settings/api/api/</a>"
|
||||
},
|
||||
"regru_mtls_private_key": {
|
||||
"label": "Рег.ру mTLS private key (Optional)",
|
||||
"placeholder": "Please enter Рег.ру mTLS private key",
|
||||
"errmsg": {
|
||||
"invalid": "Invalid private key"
|
||||
},
|
||||
"tooltip": "For more information, see <a href=\"https://www.reg.ru/user/account/#/settings/api/\" target=\"_blank\">https://www.reg.ru/user/account/#/settings/api/api/</a>"
|
||||
},
|
||||
"rfc2136_host": {
|
||||
"label": "DNS server host",
|
||||
"placeholder": "Please enter DNS server host"
|
||||
|
||||
@@ -198,6 +198,7 @@
|
||||
"ratpanel": "AcePanel (aka RatPanel)",
|
||||
"ratpanel_common": "AcePanel",
|
||||
"ratpanel_console": "AcePanel - Console itself",
|
||||
"regru": "Рег.ру (Reg.ru)",
|
||||
"rfc2136": "Dynamic DNS Updates (RFC 2136)",
|
||||
"s3": "Object storage (S3-compatible)",
|
||||
"s3_upload": "Upload to S3-compatible object storage",
|
||||
|
||||
@@ -1072,6 +1072,30 @@
|
||||
"placeholder": "请输入耗子面板 AccessToken",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://ratpanel.github.io/advanced/api.html\" target=\"_blank\">https://ratpanel.github.io/advanced/api.html</a>"
|
||||
},
|
||||
"regru_username": {
|
||||
"label": "Рег.ру 用户名",
|
||||
"placeholder": "请输入 Рег.ру 用户名"
|
||||
},
|
||||
"regru_password": {
|
||||
"label": "Рег.ру 密码",
|
||||
"placeholder": "请输入 Рег.ру 密码或 API 密码"
|
||||
},
|
||||
"regru_mtls_certificate": {
|
||||
"label": "Рег.ру SSL 双向认证证书(可选)",
|
||||
"placeholder": "请输入 Рег.ру SSL 双向认证证书",
|
||||
"errmsg": {
|
||||
"invalid": "无效的证书"
|
||||
},
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://www.reg.ru/user/account/#/settings/api/\" target=\"_blank\">https://www.reg.ru/user/account/#/settings/api/api/</a>"
|
||||
},
|
||||
"regru_mtls_private_key": {
|
||||
"label": "Рег.ру SSL 双向认证私钥(可选)",
|
||||
"placeholder": "请输入 Рег.ру SSL 双向认证私钥",
|
||||
"errmsg": {
|
||||
"invalid": "无效的私钥"
|
||||
},
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://www.reg.ru/user/account/#/settings/api/\" target=\"_blank\">https://www.reg.ru/user/account/#/settings/api/api/</a>"
|
||||
},
|
||||
"rfc2136_host": {
|
||||
"label": "DNS 服务器地址",
|
||||
"placeholder": "请输入 DNS 服务器地址"
|
||||
|
||||
@@ -198,6 +198,7 @@
|
||||
"ratpanel": "耗子面板(又名:AcePanel)",
|
||||
"ratpanel_common": "耗子面板",
|
||||
"ratpanel_console": "耗子面板 - 面板自身",
|
||||
"regru": "Рег.ру(REG.ru)",
|
||||
"rfc2136": "Dynamic DNS Updates (RFC 2136)",
|
||||
"s3": "对象存储(S3 兼容)",
|
||||
"s3_upload": "上传到 S3 兼容的对象存储",
|
||||
|
||||
Reference in New Issue
Block a user