diff --git a/internal/certapply/config.go b/internal/certapply/config.go index 8b36d778..d47753fb 100644 --- a/internal/certapply/config.go +++ b/internal/certapply/config.go @@ -15,6 +15,7 @@ import ( var acmeDirUrls = map[string]string{ string(domain.CAProviderTypeLetsEncrypt): "https://acme-v02.api.letsencrypt.org/directory", string(domain.CAProviderTypeLetsEncryptStaging): "https://acme-staging-v02.api.letsencrypt.org/directory", + string(domain.CAProviderTypeActalisSSL): "https://acme-api.actalis.com/acme/directory", string(domain.CAProviderTypeBuypass): "https://api.buypass.com/acme/directory", string(domain.CAProviderTypeGoogleTrustServices): "https://dv.acme-v02.api.pki.goog/directory", string(domain.CAProviderTypeSSLCom): "https://acme.ssl.com/sslcom-dv-rsa", diff --git a/internal/domain/access.go b/internal/domain/access.go index 5f745e3c..ceb569b8 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -44,6 +44,10 @@ type AccessConfigForACMEHttpReq struct { Password string `json:"password,omitempty"` } +type AccessConfigForActalisSSL struct { + AccessConfigForACMEExternalAccountBinding +} + type AccessConfigForAliyun struct { AccessKeyId string `json:"accessKeyId"` AccessKeySecret string `json:"accessKeySecret"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 379b6276..fec6d3bf 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -13,6 +13,7 @@ const ( AccessProviderTypeACMECA = AccessProviderType("acmeca") AccessProviderTypeACMEDNS = AccessProviderType("acmedns") AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq") + AccessProviderTypeActalisSSL = AccessProviderType("actalisssl") AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留) AccessProviderTypeAliyun = AccessProviderType("aliyun") AccessProviderTypeAPISIX = AccessProviderType("apisix") @@ -105,6 +106,7 @@ NOTICE: If you add new constant, please keep ASCII order. */ const ( CAProviderTypeACMECA = CAProviderType(AccessProviderTypeACMECA) + CAProviderTypeActalisSSL = CAProviderType(AccessProviderTypeActalisSSL) CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass) CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices) CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt) diff --git a/ui/public/imgs/providers/actalisssl.png b/ui/public/imgs/providers/actalisssl.png new file mode 100644 index 00000000..3897c6b4 Binary files /dev/null and b/ui/public/imgs/providers/actalisssl.png differ diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index ed643447..69112aa5 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -15,6 +15,7 @@ import AccessConfigFieldsProvider1Panel from "./forms/AccessConfigFieldsProvider import AccessConfigFieldsProviderACMECA from "./forms/AccessConfigFieldsProviderACMECA"; import AccessConfigFieldsProviderACMEDNS from "./forms/AccessConfigFieldsProviderACMEDNS"; import AccessConfigFieldsProviderACMEHttpReq from "./forms/AccessConfigFieldsProviderACMEHttpReq"; +import AccessConfigFieldsProviderActalisSSL from "./forms/AccessConfigFieldsProviderActalisSSL"; import AccessConfigFieldsProviderAliyun from "./forms/AccessConfigFieldsProviderAliyun"; import AccessConfigFieldsProviderAPISIX from "./forms/AccessConfigFieldsProviderAPISIX"; import AccessConfigFieldsProviderAWS from "./forms/AccessConfigFieldsProviderAWS"; @@ -141,6 +142,9 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, .. case ACCESS_PROVIDERS.ACMEHTTPREQ: { return ; } + case ACCESS_PROVIDERS.ACTALISSSL: { + return ; + } case ACCESS_PROVIDERS.ALIYUN: { return ; } diff --git a/ui/src/components/access/forms/AccessConfigFieldsProviderActalisSSL.tsx b/ui/src/components/access/forms/AccessConfigFieldsProviderActalisSSL.tsx new file mode 100644 index 00000000..e0d95932 --- /dev/null +++ b/ui/src/components/access/forms/AccessConfigFieldsProviderActalisSSL.tsx @@ -0,0 +1,70 @@ +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 AccessConfigFormFieldsProviderActalisSSL = () => { + const { i18n, t } = useTranslation(); + + const { parentNamePath } = useFormNestedFieldsContext(); + const formSchema = z.object({ + [parentNamePath]: getSchema({ i18n }), + }); + const formRule = createSchemaFieldRule(formSchema); + const initialValues = getInitialValues(); + + return ( + <> + } + > + + + + } + > + + + + ); +}; + +const getInitialValues = (): Nullish>> => { + return { + eabKid: "", + eabHmacKey: "", + }; +}; + +const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => { + const { t } = i18n; + + return z.object({ + eabKid: z + .string() + .min(1, t("access.form.actalisssl_eab_kid.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })), + eabHmacKey: z + .string() + .min(1, t("access.form.actalisssl_eab_hmac_key.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })), + }); +}; + +const _default = Object.assign(AccessConfigFormFieldsProviderActalisSSL, { + getInitialValues, + getSchema, +}); + +export default _default; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index bbb87fd7..8a934aa9 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -19,6 +19,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ ACMECA: "acmeca", ACMEDNS: "acmedns", ACMEHTTPREQ: "acmehttpreq", + ACTALISSSL: "actalisssl", ALIYUN: "aliyun", APISIX: "apisix", AWS: "aws", @@ -188,6 +189,7 @@ export const accessProvidersMap: Map = new [ [CA_PROVIDERS.LETSENCRYPT, "builtin"], [CA_PROVIDERS.LETSENCRYPTSTAGING, "builtin"], + [CA_PROVIDERS.ACTALISSSL], [CA_PROVIDERS.BUYPASS], [CA_PROVIDERS.GOOGLETRUSTSERVICES], [CA_PROVIDERS.SSLCOM], diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index 10522775..2603421f 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -78,6 +78,12 @@ "access.form.acmehttpreq_password.label": "HTTP Basic Auth password (Optional)", "access.form.acmehttpreq_password.placeholder": "Please enter HTTP Basic Auth password", "access.form.acmehttpreq_password.tooltip": "For more information, see https://go-acme.github.io/lego/dns/httpreq/", + "access.form.actalisssl_eab_kid.label": "ACME EAB KID", + "access.form.actalisssl_eab_kid.placeholder": "Please enter ACME EAB KID", + "access.form.actalisssl_eab_kid.tooltip": "For more information, see https://www.actalis.com/manage-with-acme", + "access.form.actalisssl_eab_hmac_key.label": "ACME EAB HMAC key", + "access.form.actalisssl_eab_hmac_key.placeholder": "Please enter ACME EAB HMAC key", + "access.form.actalisssl_eab_hmac_key.tooltip": "For more information, see https://www.actalis.com/manage-with-acme", "access.form.aliyun_access_key_id.label": "Aliyun AccessKeyId", "access.form.aliyun_access_key_id.placeholder": "Please enter Aliyun AccessKeyId", "access.form.aliyun_access_key_id.tooltip": "For more information, see https://www.alibabacloud.com/help/en/acr/create-and-obtain-an-accesskey-pair", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index b620226e..49cf3367 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -5,6 +5,7 @@ "provider.acmeca": "ACME Custom CA Endpoint", "provider.acmedns": "ACME-DNS", "provider.acmehttpreq": "ACME Custom HTTP Endpoint", + "provider.actalisssl": "Actalis SSL", "provider.aliyun": "Alibaba Cloud", "provider.aliyun.alb": "Alibaba Cloud - ALB (Application Load Balancer)", "provider.aliyun.apigw": "Alibaba Cloud - API Gateway", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 5df865f4..1cda5e6b 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -77,6 +77,12 @@ "access.form.acmehttpreq_password.label": "HTTP 基本认证密码(可选)", "access.form.acmehttpreq_password.placeholder": "请输入 HTTP 基本认证密码", "access.form.acmehttpreq_password.tooltip": "这是什么?请参阅 https://go-acme.github.io/lego/dns/httpreq/", + "access.form.actalisssl_eab_kid.label": "ACME EAB KID", + "access.form.actalisssl_eab_kid.placeholder": "请输入 ACME EAB KID", + "access.form.actalisssl_eab_kid.tooltip": "这是什么?请参阅 https://www.actalis.com/manage-with-acme", + "access.form.actalisssl_eab_hmac_key.label": "ACME EAB HMAC Key", + "access.form.actalisssl_eab_hmac_key.placeholder": "请输入 ACME EAB HMAC Key", + "access.form.actalisssl_eab_hmac_key.tooltip": "这是什么?请参阅 https://www.actalis.com/manage-with-acme", "access.form.aliyun_access_key_id.label": "阿里云 AccessKeyId", "access.form.aliyun_access_key_id.placeholder": "请输入阿里云 AccessKeyId", "access.form.aliyun_access_key_id.tooltip": "这是什么?请参阅 https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index 6d9f78d6..85f1e578 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -5,6 +5,7 @@ "provider.acmeca": "ACME 自定义 CA 端点", "provider.acmedns": "ACME-DNS", "provider.acmehttpreq": "ACME 自定义 HTTP 端点", + "provider.actalisssl": "Actalis SSL", "provider.aliyun": "阿里云", "provider.aliyun.alb": "阿里云 - 应用型负载均衡 ALB", "provider.aliyun.apigw": "阿里云 - API 网关", diff --git a/ui/src/pages/settings/SettingsSSLProvider.tsx b/ui/src/pages/settings/SettingsSSLProvider.tsx index 4170af57..5d97aa49 100644 --- a/ui/src/pages/settings/SettingsSSLProvider.tsx +++ b/ui/src/pages/settings/SettingsSSLProvider.tsx @@ -108,6 +108,76 @@ const SSLProviderEditFormLetsEncryptStagingConfig = () => { ); }; +const SSLProviderEditFormActalisSSLConfig = () => { + const { t } = useTranslation(); + + const { pending, settings, updateSettings } = useContext(SSLProviderContext); + + const formSchema = z.object({ + eabKid: z + .string(t("access.form.actalisssl_eab_kid.placeholder")) + .min(1, t("access.form.actalisssl_eab_kid.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })), + eabHmacKey: z + .string(t("access.form.actalisssl_eab_hmac_key.placeholder")) + .min(1, t("access.form.actalisssl_eab_hmac_key.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })), + }); + const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + initialValues: settings?.content?.config?.[CA_PROVIDERS.ACTALISSSL], + onSubmit: async (values) => { + const newSettings = produce(settings, (draft) => { + draft.content ??= {} as SSLProviderSettingsContent; + draft.content.provider = CA_PROVIDERS.ACTALISSSL; + + draft.content.config ??= {} as SSLProviderSettingsContent["config"]; + draft.content.config[CA_PROVIDERS.ACTALISSSL] = values; + }); + await updateSettings(newSettings); + + setFormChanged(false); + }, + }); + + const [formChanged, setFormChanged] = useState(false); + useEffect(() => { + setFormChanged(settings?.content?.provider !== CA_PROVIDERS.ACTALISSSL); + }, [settings?.content?.provider]); + + const handleFormChange = () => { + setFormChanged(true); + }; + + return ( +
+ } + > + + + + } + > + + + + + + +
+ ); +}; + const SSLProviderEditFormBuypassConfig = () => { const { t } = useTranslation(); @@ -466,6 +536,7 @@ const SettingsSSLProvider = () => { const providers = [ [CA_PROVIDERS.LETSENCRYPT, "provider.letsencrypt", "letsencrypt.org", "/imgs/providers/letsencrypt.svg"], [CA_PROVIDERS.LETSENCRYPTSTAGING, "provider.letsencryptstaging", "letsencrypt.org", "/imgs/providers/letsencrypt.svg"], + [CA_PROVIDERS.ACTALISSSL, "provider.actalisssl", "actalis.com", "/imgs/providers/actalisssl.png"], [CA_PROVIDERS.BUYPASS, "provider.buypass", "buypass.com", "/imgs/providers/buypass.png"], [CA_PROVIDERS.GOOGLETRUSTSERVICES, "provider.googletrustservices", "pki.goog", "/imgs/providers/google.svg"], [CA_PROVIDERS.SSLCOM, "provider.sslcom", "ssl.com", "/imgs/providers/sslcom.svg"], @@ -486,6 +557,8 @@ const SettingsSSLProvider = () => { return ; case CA_PROVIDERS.LETSENCRYPTSTAGING: return ; + case CA_PROVIDERS.ACTALISSSL: + return ; case CA_PROVIDERS.BUYPASS: return ; case CA_PROVIDERS.GOOGLETRUSTSERVICES: