feat(ui): allow select dns-01 provider on application

This commit is contained in:
Fu Diwei 2025-03-25 19:52:09 +08:00
parent 51c1b193e5
commit fd8ac3ae37
8 changed files with 67 additions and 33 deletions

View File

@ -27,6 +27,6 @@ type Uploader interface {
// 表示证书上传结果的数据结构,包含上传后的证书 ID、名称和其他数据。 // 表示证书上传结果的数据结构,包含上传后的证书 ID、名称和其他数据。
type UploadResult struct { type UploadResult struct {
CertId string `json:"certId"` CertId string `json:"certId"`
CertName string `json:"certName"` CertName string `json:"certName,omitzero"`
ExtendedData map[string]any `json:"extendedData,omitempty"` ExtendedData map[string]any `json:"extendedData,omitempty"`
} }

View File

@ -21,9 +21,9 @@ const AccessSelect = ({ filter, ...props }: AccessTypeSelectProps) => {
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: AccessModel }>>([]); const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: AccessModel }>>([]);
useEffect(() => { useEffect(() => {
const items = filter != null ? accesses.filter(filter) : accesses; const filteredItems = filter != null ? accesses.filter(filter) : accesses;
setOptions( setOptions(
items.map((item) => ({ filteredItems.map((item) => ({
key: item.id, key: item.id,
value: item.id, value: item.id,
label: item.name, label: item.name,

View File

@ -1,22 +1,32 @@
import { memo } from "react"; import { memo, useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Avatar, Select, type SelectProps, Space, Tag, Typography } from "antd"; import { Avatar, Select, type SelectProps, Space, Tag, Typography } from "antd";
import { ACCESS_USAGES, accessProvidersMap } from "@/domain/provider"; import { ACCESS_USAGES, type AccessProvider, accessProvidersMap } from "@/domain/provider";
export type AccessProviderSelectProps = Omit< export type AccessProviderSelectProps = Omit<
SelectProps, SelectProps,
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender" "filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
>; > & {
filter?: (record: AccessProvider) => boolean;
};
const AccessProviderSelect = (props: AccessProviderSelectProps) => { const AccessProviderSelect = ({ filter, ...props }: AccessProviderSelectProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const options = Array.from(accessProvidersMap.values()).map((item) => ({ const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: AccessProvider }>>([]);
useEffect(() => {
const allItems = Array.from(accessProvidersMap.values());
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
setOptions(
filteredItems.map((item) => ({
key: item.type, key: item.type,
value: item.type, value: item.type,
label: t(item.name), label: t(item.name),
})); data: item,
}))
);
}, [filter]);
const renderOption = (key: string) => { const renderOption = (key: string) => {
const provider = accessProvidersMap.get(key); const provider = accessProvidersMap.get(key);

View File

@ -1,22 +1,32 @@
import { memo } from "react"; import { memo, useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Avatar, Select, type SelectProps, Space, Typography } from "antd"; import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
import { applyDNSProvidersMap } from "@/domain/provider"; import { type ApplyDNSProvider, applyDNSProvidersMap } from "@/domain/provider";
export type ApplyDNSProviderSelectProps = Omit< export type ApplyDNSProviderSelectProps = Omit<
SelectProps, SelectProps,
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender" "filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
>; > & {
filter?: (record: ApplyDNSProvider) => boolean;
};
const ApplyDNSProviderSelect = (props: ApplyDNSProviderSelectProps) => { const ApplyDNSProviderSelect = ({ filter, ...props }: ApplyDNSProviderSelectProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const options = Array.from(applyDNSProvidersMap.values()).map((item) => ({ const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: ApplyDNSProvider }>>([]);
useEffect(() => {
const allItems = Array.from(applyDNSProvidersMap.values());
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
setOptions(
filteredItems.map((item) => ({
key: item.type, key: item.type,
value: item.type, value: item.type,
label: t(item.name), label: t(item.name),
})); data: item,
}))
);
}, [filter]);
const renderOption = (key: string) => { const renderOption = (key: string) => {
const provider = applyDNSProvidersMap.get(key); const provider = applyDNSProvidersMap.get(key);

View File

@ -1,22 +1,32 @@
import { memo } from "react"; import { memo, useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Avatar, Select, type SelectProps, Space, Typography } from "antd"; import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
import { deployProvidersMap } from "@/domain/provider"; import { type DeployProvider, deployProvidersMap } from "@/domain/provider";
export type DeployProviderSelectProps = Omit< export type DeployProviderSelectProps = Omit<
SelectProps, SelectProps,
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender" "filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
>; > & {
filter?: (record: DeployProvider) => boolean;
};
const DeployProviderSelect = (props: DeployProviderSelectProps) => { const DeployProviderSelect = ({ filter, ...props }: DeployProviderSelectProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const options = Array.from(deployProvidersMap.values()).map((item) => ({ const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: DeployProvider }>>([]);
useEffect(() => {
const allItems = Array.from(deployProvidersMap.values());
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
setOptions(
filteredItems.map((item) => ({
key: item.type, key: item.type,
value: item.type, value: item.type,
label: t(item.name), label: t(item.name),
})); data: item,
}))
);
}, [filter]);
const renderOption = (key: string) => { const renderOption = (key: string) => {
const provider = deployProvidersMap.get(key); const provider = deployProvidersMap.get(key);

View File

@ -400,7 +400,6 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
</label> </label>
<Form.Item name="providerAccessId" rules={[formRule]}> <Form.Item name="providerAccessId" rules={[formRule]}>
<AccessSelect <AccessSelect
placeholder={t("workflow_node.deploy.form.provider_access.placeholder")}
filter={(record) => { filter={(record) => {
if (fieldProvider) { if (fieldProvider) {
return deployProvidersMap.get(fieldProvider)?.provider === record.provider; return deployProvidersMap.get(fieldProvider)?.provider === record.provider;
@ -409,6 +408,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
const provider = accessProvidersMap.get(record.provider); const provider = accessProvidersMap.get(record.provider);
return !!provider?.usages?.includes(ACCESS_USAGES.DEPLOY); return !!provider?.usages?.includes(ACCESS_USAGES.DEPLOY);
}} }}
placeholder={t("workflow_node.deploy.form.provider_access.placeholder")}
/> />
</Form.Item> </Form.Item>
</Form.Item> </Form.Item>

View File

@ -31,6 +31,8 @@
"workflow_node.apply.form.contact_email.label": "Contact email", "workflow_node.apply.form.contact_email.label": "Contact email",
"workflow_node.apply.form.contact_email.placeholder": "Please enter contact email", "workflow_node.apply.form.contact_email.placeholder": "Please enter contact email",
"workflow_node.apply.form.contact_email.tooltip": "Contact information required for SSL certificate application. Please pay attention to the <a href=\"https://letsencrypt.org/docs/rate-limits/\" target=\"_blank\">rate limits</a>.", "workflow_node.apply.form.contact_email.tooltip": "Contact information required for SSL certificate application. Please pay attention to the <a href=\"https://letsencrypt.org/docs/rate-limits/\" target=\"_blank\">rate limits</a>.",
"workflow_node.apply.form.challenge_type.label": "Challenge type",
"workflow_node.apply.form.challenge_type.placeholder": "Please select challenge type",
"workflow_node.apply.form.provider.label": "DNS provider", "workflow_node.apply.form.provider.label": "DNS provider",
"workflow_node.apply.form.provider.placeholder": "Please select DNS provider of the domains", "workflow_node.apply.form.provider.placeholder": "Please select DNS provider of the domains",
"workflow_node.apply.form.provider_access.label": "DNS provider authorization", "workflow_node.apply.form.provider_access.label": "DNS provider authorization",
@ -75,7 +77,7 @@
"workflow_node.apply.form.skip_before_expiry_days.prefix": "If the certificate expiration time exceeds", "workflow_node.apply.form.skip_before_expiry_days.prefix": "If the certificate expiration time exceeds",
"workflow_node.apply.form.skip_before_expiry_days.suffix": ", skip to re-apply.", "workflow_node.apply.form.skip_before_expiry_days.suffix": ", skip to re-apply.",
"workflow_node.apply.form.skip_before_expiry_days.unit": "days", "workflow_node.apply.form.skip_before_expiry_days.unit": "days",
"workflow_node.apply.form.skip_before_expiry_days.tooltip": "Be careful not to exceed the validity period limit of the CA, otherwise the certificate may never be renewed.", "workflow_node.apply.form.skip_before_expiry_days.tooltip": "Be careful not to exceed the validity period limit of the issued certificate, otherwise the certificate may never be renewed.",
"workflow_node.deploy.label": "Deployment", "workflow_node.deploy.label": "Deployment",
"workflow_node.deploy.search.provider.placeholder": "Search deploy target ...", "workflow_node.deploy.search.provider.placeholder": "Search deploy target ...",

View File

@ -31,6 +31,8 @@
"workflow_node.apply.form.contact_email.label": "联系邮箱", "workflow_node.apply.form.contact_email.label": "联系邮箱",
"workflow_node.apply.form.contact_email.placeholder": "请输入联系邮箱", "workflow_node.apply.form.contact_email.placeholder": "请输入联系邮箱",
"workflow_node.apply.form.contact_email.tooltip": "申请签发 SSL 证书时所需的联系方式。请注意 Let's Encrypt 账户注册的速率限制。<a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">点此了解更多</a>。", "workflow_node.apply.form.contact_email.tooltip": "申请签发 SSL 证书时所需的联系方式。请注意 Let's Encrypt 账户注册的速率限制。<a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">点此了解更多</a>。",
"workflow_node.apply.form.challenge_type.label": "质询方式",
"workflow_node.apply.form.challenge_type.placeholder": "请选择质询方式",
"workflow_node.apply.form.provider.label": "DNS 提供商", "workflow_node.apply.form.provider.label": "DNS 提供商",
"workflow_node.apply.form.provider.placeholder": "请选择 DNS 提供商", "workflow_node.apply.form.provider.placeholder": "请选择 DNS 提供商",
"workflow_node.apply.form.provider_access.label": "DNS 提供商授权", "workflow_node.apply.form.provider_access.label": "DNS 提供商授权",
@ -75,7 +77,7 @@
"workflow_node.apply.form.skip_before_expiry_days.prefix": "当上次签发的证书距到期时间超过", "workflow_node.apply.form.skip_before_expiry_days.prefix": "当上次签发的证书距到期时间超过",
"workflow_node.apply.form.skip_before_expiry_days.suffix": "时,跳过重新申请。", "workflow_node.apply.form.skip_before_expiry_days.suffix": "时,跳过重新申请。",
"workflow_node.apply.form.skip_before_expiry_days.unit": "天", "workflow_node.apply.form.skip_before_expiry_days.unit": "天",
"workflow_node.apply.form.skip_before_expiry_days.tooltip": "注意不要超过 CA 的证书有效期限制,否则证书可能永远不会续期。", "workflow_node.apply.form.skip_before_expiry_days.tooltip": "注意不要超过颁发的证书最大有效期,否则证书可能永远不会续期。",
"workflow_node.deploy.label": "部署证书", "workflow_node.deploy.label": "部署证书",
"workflow_node.deploy.search.provider.placeholder": "搜索部署目标……", "workflow_node.deploy.search.provider.placeholder": "搜索部署目标……",