mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 09:21:56 +08:00
feat: support overwriting the default config of notifiers
This commit is contained in:
parent
11b413d0dc
commit
3c2fbd720f
@ -86,7 +86,7 @@ const AccessFormEmailConfig = ({ form: formInst, formName, disabled, initialValu
|
||||
onValuesChange={handleFormChange}
|
||||
>
|
||||
<div className="flex space-x-2">
|
||||
<div className="w-2/5">
|
||||
<div className="w-3/5">
|
||||
<Form.Item name="smtpHost" label={t("access.form.email_smtp_host.label")} rules={[formRule]}>
|
||||
<Input placeholder={t("access.form.email_smtp_host.placeholder")} />
|
||||
</Form.Item>
|
||||
@ -97,14 +97,12 @@ const AccessFormEmailConfig = ({ form: formInst, formName, disabled, initialValu
|
||||
<InputNumber className="w-full" placeholder={t("access.form.email_smtp_port.placeholder")} min={1} max={65535} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="w-1/5">
|
||||
<Form.Item name="smtpTls" label={t("access.form.email_smtp_tls.label")} rules={[formRule]}>
|
||||
<Switch onChange={handleTlsSwitchChange} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Form.Item name="smtpTls" label={t("access.form.email_smtp_tls.label")} rules={[formRule]}>
|
||||
<Switch onChange={handleTlsSwitchChange} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="username" label={t("access.form.email_username.label")} rules={[formRule]}>
|
||||
<Input autoComplete="new-password" placeholder={t("access.form.email_username.placeholder")} />
|
||||
</Form.Item>
|
||||
|
@ -31,7 +31,7 @@ const AccessFormTelegramConfig = ({ form: formInst, formName, disabled, initialV
|
||||
.max(256, t("common.errmsg.string_max", { max: 256 })),
|
||||
defaultChatId: z
|
||||
.preprocess(
|
||||
(v) => Number(v),
|
||||
(v) => (v == null || v === "" ? undefined : Number(v)),
|
||||
z
|
||||
.number()
|
||||
.nullish()
|
||||
|
@ -34,6 +34,9 @@ export type NotifyChannelEditFormInstance = {
|
||||
validateFields: FormInstance<NotifyChannelEditFormFieldValues>["validateFields"];
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
const NotifyChannelEditForm = forwardRef<NotifyChannelEditFormInstance, NotifyChannelEditFormProps>(
|
||||
({ className, style, channel, disabled, initialValues, onValuesChange }, ref) => {
|
||||
const { form: formInst, formProps } = useAntdForm({
|
||||
|
@ -218,8 +218,6 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
||||
};
|
||||
|
||||
const handleProviderAccessSelect = (value: string) => {
|
||||
if (fieldProviderAccessId === value) return;
|
||||
|
||||
// 切换授权信息时联动 DNS 提供商
|
||||
const access = accesses.find((access) => access.id === value);
|
||||
const provider = Array.from(acmeDns01ProvidersMap.values()).find((provider) => provider.provider === access?.provider);
|
||||
@ -230,8 +228,6 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
||||
};
|
||||
|
||||
const handleCAProviderSelect = (value?: string | undefined) => {
|
||||
if (fieldCAProvider === value) return;
|
||||
|
||||
// 切换 CA 提供商时联动授权信息
|
||||
if (value === "") {
|
||||
setTimeout(() => {
|
||||
@ -368,6 +364,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
||||
const provider = accessProvidersMap.get(record.provider);
|
||||
if (provider?.usages?.includes(ACCESS_USAGES.DNS)) {
|
||||
formInst.setFieldValue("providerAccessId", record.id);
|
||||
handleProviderAccessSelect(record.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -322,8 +322,6 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
||||
};
|
||||
|
||||
const handleProviderSelect = (value?: string | undefined) => {
|
||||
if (fieldProvider === value) return;
|
||||
|
||||
// 切换部署目标时重置表单,避免其他部署目标的配置字段影响当前部署目标
|
||||
if (initialValues?.provider === value) {
|
||||
formInst.resetFields();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { forwardRef, memo, useEffect, useImperativeHandle, useState } from "react";
|
||||
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router";
|
||||
import { PlusOutlined as PlusOutlinedIcon, RightOutlined as RightOutlinedIcon } from "@ant-design/icons";
|
||||
@ -9,13 +9,17 @@ import { z } from "zod";
|
||||
import AccessEditModal from "@/components/access/AccessEditModal";
|
||||
import AccessSelect from "@/components/access/AccessSelect";
|
||||
import NotificationProviderSelect from "@/components/provider/NotificationProviderSelect";
|
||||
import { ACCESS_USAGES, accessProvidersMap, notificationProvidersMap } from "@/domain/provider";
|
||||
import { ACCESS_USAGES, NOTIFICATION_PROVIDERS, accessProvidersMap, notificationProvidersMap } from "@/domain/provider";
|
||||
import { notifyChannelsMap } from "@/domain/settings";
|
||||
import { type WorkflowNodeConfigForNotify } from "@/domain/workflow";
|
||||
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
|
||||
import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks";
|
||||
import { useAccessesStore } from "@/stores/access";
|
||||
import { useNotifyChannelsStore } from "@/stores/notify";
|
||||
|
||||
import NotifyNodeConfigFormEmailConfig from "./NotifyNodeConfigFormEmailConfig";
|
||||
import NotifyNodeConfigFormMattermostConfig from "./NotifyNodeConfigFormMattermostConfig";
|
||||
import NotifyNodeConfigFormTelegramConfig from "./NotifyNodeConfigFormTelegramConfig";
|
||||
|
||||
type NotifyNodeConfigFormFieldValues = Partial<WorkflowNodeConfigForNotify>;
|
||||
|
||||
export type NotifyNodeConfigFormProps = {
|
||||
@ -65,6 +69,7 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
|
||||
providerAccessId: z
|
||||
.string({ message: t("workflow_node.notify.form.provider_access.placeholder") })
|
||||
.nonempty(t("workflow_node.notify.form.provider_access.placeholder")),
|
||||
providerConfig: z.any().nullish(),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const { form: formInst, formProps } = useAntdForm({
|
||||
@ -88,9 +93,31 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
|
||||
}
|
||||
}, [accesses, fieldProviderAccessId]);
|
||||
|
||||
const handleProviderSelect = (value: string) => {
|
||||
if (fieldProvider === value) return;
|
||||
const [nestedFormInst] = Form.useForm();
|
||||
const nestedFormName = useAntdFormName({ form: nestedFormInst, name: "workflowNodeNotifyConfigFormProviderConfigForm" });
|
||||
const nestedFormEl = useMemo(() => {
|
||||
const nestedFormProps = {
|
||||
form: nestedFormInst,
|
||||
formName: nestedFormName,
|
||||
disabled: disabled,
|
||||
initialValues: initialValues?.providerConfig,
|
||||
};
|
||||
|
||||
/*
|
||||
注意:如果追加新的子组件,请保持以 ASCII 排序。
|
||||
NOTICE: If you add new child component, please keep ASCII order.
|
||||
*/
|
||||
switch (fieldProvider) {
|
||||
case NOTIFICATION_PROVIDERS.EMAIL:
|
||||
return <NotifyNodeConfigFormEmailConfig {...nestedFormProps} />;
|
||||
case NOTIFICATION_PROVIDERS.MATTERMOST:
|
||||
return <NotifyNodeConfigFormMattermostConfig {...nestedFormProps} />;
|
||||
case NOTIFICATION_PROVIDERS.TELEGRAM:
|
||||
return <NotifyNodeConfigFormTelegramConfig {...nestedFormProps} />;
|
||||
}
|
||||
}, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]);
|
||||
|
||||
const handleProviderSelect = (value: string) => {
|
||||
// 切换消息通知提供商时联动授权信息
|
||||
if (initialValues?.provider === value) {
|
||||
formInst.setFieldValue("providerAccessId", initialValues?.providerAccessId);
|
||||
@ -104,8 +131,6 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
|
||||
};
|
||||
|
||||
const handleProviderAccessSelect = (value: string) => {
|
||||
if (fieldProviderAccessId === value) return;
|
||||
|
||||
// 切换授权信息时联动消息通知提供商
|
||||
const access = accesses.find((access) => access.id === value);
|
||||
const provider = Array.from(notificationProvidersMap.values()).find((provider) => provider.provider === access?.provider);
|
||||
@ -122,13 +147,21 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
getFieldsValue: () => {
|
||||
return formInst.getFieldsValue(true);
|
||||
const values = formInst.getFieldsValue(true);
|
||||
values.providerConfig = nestedFormInst.getFieldsValue();
|
||||
return values;
|
||||
},
|
||||
resetFields: (fields) => {
|
||||
return formInst.resetFields(fields as (keyof NotifyNodeConfigFormFieldValues)[]);
|
||||
formInst.resetFields(fields);
|
||||
|
||||
if (!!fields && fields.includes("providerConfig")) {
|
||||
nestedFormInst.resetFields(fields);
|
||||
}
|
||||
},
|
||||
validateFields: (nameList, config) => {
|
||||
return formInst.validateFields(nameList, config);
|
||||
const t1 = formInst.validateFields(nameList, config);
|
||||
const t2 = nestedFormInst.validateFields(undefined, config);
|
||||
return Promise.all([t1, t2]).then(() => t1);
|
||||
},
|
||||
} as NotifyNodeConfigFormInstance;
|
||||
});
|
||||
@ -207,6 +240,7 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
|
||||
const provider = accessProvidersMap.get(record.provider);
|
||||
if (provider?.usages?.includes(ACCESS_USAGES.NOTIFICATION)) {
|
||||
formInst.setFieldValue("providerAccessId", record.id);
|
||||
handleProviderAccessSelect(record.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@ -224,6 +258,8 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
|
||||
{nestedFormEl}
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,80 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Form, type FormInstance, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { validEmailAddress } from "@/utils/validators";
|
||||
|
||||
type NotifyNodeConfigFormEmailConfigFieldValues = Nullish<{
|
||||
senderAddress?: string;
|
||||
receiverAddress?: string;
|
||||
}>;
|
||||
|
||||
export type NotifyNodeConfigFormEmailConfigProps = {
|
||||
form: FormInstance;
|
||||
formName: string;
|
||||
disabled?: boolean;
|
||||
initialValues?: NotifyNodeConfigFormEmailConfigFieldValues;
|
||||
onValuesChange?: (values: NotifyNodeConfigFormEmailConfigFieldValues) => void;
|
||||
};
|
||||
|
||||
const initFormModel = (): NotifyNodeConfigFormEmailConfigFieldValues => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const NotifyNodeConfigFormEmailConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: NotifyNodeConfigFormEmailConfigProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
senderAddress: z
|
||||
.string()
|
||||
.nullish()
|
||||
.refine((v) => {
|
||||
if (!v) return true;
|
||||
return validEmailAddress(v);
|
||||
}, t("common.errmsg.email_invalid")),
|
||||
receiverAddress: z
|
||||
.string()
|
||||
.nullish()
|
||||
.refine((v) => {
|
||||
if (!v) return true;
|
||||
return validEmailAddress(v);
|
||||
}, t("common.errmsg.email_invalid")),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||
onValuesChange?.(values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={formInst}
|
||||
disabled={disabled}
|
||||
initialValues={initialValues ?? initFormModel()}
|
||||
layout="vertical"
|
||||
name={formName}
|
||||
onValuesChange={handleFormChange}
|
||||
>
|
||||
<Form.Item
|
||||
name="senderAddress"
|
||||
label={t("workflow_node.notify.form.email_sender_address.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.email_sender_address.tooltip") }}></span>}
|
||||
>
|
||||
<Input type="email" placeholder={t("workflow_node.notify.form.email_sender_address.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="receiverAddress"
|
||||
label={t("workflow_node.notify.form.email_receiver_address.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.email_receiver_address.tooltip") }}></span>}
|
||||
>
|
||||
<Input type="email" placeholder={t("workflow_node.notify.form.email_receiver_address.placeholder")} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotifyNodeConfigFormEmailConfig;
|
@ -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";
|
||||
|
||||
type NotifyNodeConfigFormMattermostConfigFieldValues = Nullish<{
|
||||
channelId?: string;
|
||||
}>;
|
||||
|
||||
export type NotifyNodeConfigFormMattermostConfigProps = {
|
||||
form: FormInstance;
|
||||
formName: string;
|
||||
disabled?: boolean;
|
||||
initialValues?: NotifyNodeConfigFormMattermostConfigFieldValues;
|
||||
onValuesChange?: (values: NotifyNodeConfigFormMattermostConfigFieldValues) => void;
|
||||
};
|
||||
|
||||
const initFormModel = (): NotifyNodeConfigFormMattermostConfigFieldValues => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const NotifyNodeConfigFormMattermostConfig = ({
|
||||
form: formInst,
|
||||
formName,
|
||||
disabled,
|
||||
initialValues,
|
||||
onValuesChange,
|
||||
}: NotifyNodeConfigFormMattermostConfigProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
channelId: z.string().nullish(),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||
onValuesChange?.(values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={formInst}
|
||||
disabled={disabled}
|
||||
initialValues={initialValues ?? initFormModel()}
|
||||
layout="vertical"
|
||||
name={formName}
|
||||
onValuesChange={handleFormChange}
|
||||
>
|
||||
<Form.Item
|
||||
name="channelId"
|
||||
label={t("workflow_node.notify.form.mattermost_channel_id.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.mattermost_channel_id.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.notify.form.mattermost_channel_id.placeholder")} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotifyNodeConfigFormMattermostConfig;
|
@ -0,0 +1,66 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Form, type FormInstance, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
type NotifyNodeConfigFormTelegramConfigFieldValues = Nullish<{
|
||||
chatId?: string | number;
|
||||
}>;
|
||||
|
||||
export type NotifyNodeConfigFormTelegramConfigProps = {
|
||||
form: FormInstance;
|
||||
formName: string;
|
||||
disabled?: boolean;
|
||||
initialValues?: NotifyNodeConfigFormTelegramConfigFieldValues;
|
||||
onValuesChange?: (values: NotifyNodeConfigFormTelegramConfigFieldValues) => void;
|
||||
};
|
||||
|
||||
const initFormModel = (): NotifyNodeConfigFormTelegramConfigFieldValues => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const NotifyNodeConfigFormTelegramConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: NotifyNodeConfigFormTelegramConfigProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
chatId: z
|
||||
.preprocess(
|
||||
(v) => (v == null || v === "" ? undefined : Number(v)),
|
||||
z
|
||||
.number()
|
||||
.nullish()
|
||||
.refine((v) => {
|
||||
if (v == null || v + "" === "") return true;
|
||||
return /^\d+$/.test(v + "") && +v! > 0;
|
||||
}, t("workflow_node.notify.form.telegram_chat_id.placeholder"))
|
||||
)
|
||||
.nullish(),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||
onValuesChange?.(values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={formInst}
|
||||
disabled={disabled}
|
||||
initialValues={initialValues ?? initFormModel()}
|
||||
layout="vertical"
|
||||
name={formName}
|
||||
onValuesChange={handleFormChange}
|
||||
>
|
||||
<Form.Item
|
||||
name="chatId"
|
||||
label={t("workflow_node.notify.form.telegram_chat_id.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.telegram_chat_id.tooltip") }}></span>}
|
||||
>
|
||||
<Input type="number" placeholder={t("workflow_node.notify.form.telegram_chat_id.placeholder")} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotifyNodeConfigFormTelegramConfig;
|
@ -222,7 +222,7 @@
|
||||
"access.form.mattermost_username.placeholder": "Please enter Mattermost username",
|
||||
"access.form.mattermost_password.label": "Mattermost password",
|
||||
"access.form.mattermost_password.placeholder": "Please enter Mattermost password",
|
||||
"access.form.mattermost_default_channel_id.label": "Default Mattermost channel ID",
|
||||
"access.form.mattermost_default_channel_id.label": "Default Mattermost channel ID (Optional)",
|
||||
"access.form.mattermost_default_channel_id.placeholder": "Please enter default Mattermost channel ID",
|
||||
"access.form.mattermost_default_channel_id.tooltip": "How to get the channel ID? Select the target channel from the left sidebar, click on the channel name at the top, and choose ”Channel Details.” You can directly see the channel ID on the pop-up page.",
|
||||
"access.form.namecheap_username.label": "Namecheap username",
|
||||
|
@ -720,6 +720,18 @@
|
||||
"workflow_node.notify.form.provider_access.label": "Notification provider authorization",
|
||||
"workflow_node.notify.form.provider_access.placeholder": "Please select an authorization of notification provider",
|
||||
"workflow_node.notify.form.provider_access.button": "Create",
|
||||
"workflow_node.notify.form.email_sender_address.label": "Sender email address (Optional)",
|
||||
"workflow_node.notify.form.email_sender_address.placeholder": "Please enter sender email address",
|
||||
"workflow_node.notify.form.email_sender_address.tooltip": "Leave it blank to use the default sender email address provided by the authorization.",
|
||||
"workflow_node.notify.form.email_receiver_address.label": "Receiver email address (Optional)",
|
||||
"workflow_node.notify.form.email_receiver_address.placeholder": "Please enter receiver email address",
|
||||
"workflow_node.notify.form.email_receiver_address.tooltip": "Leave it blank to use the default receiver email address provided by the selected authorization.",
|
||||
"workflow_node.notify.form.mattermost_channel_id.label": "Mattermost channel ID (Optional)",
|
||||
"workflow_node.notify.form.mattermost_channel_id.placeholder": "Please enter Mattermost channel ID",
|
||||
"workflow_node.notify.form.mattermost_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.",
|
||||
"workflow_node.notify.form.telegram_chat_id.label": "Telegram chat ID (Optional)",
|
||||
"workflow_node.notify.form.telegram_chat_id.placeholder": "Please enter Telegram chat ID",
|
||||
"workflow_node.notify.form.telegram_chat_id.tooltip": "Leave it blank to use the default chat ID provided by the selected authorization.",
|
||||
|
||||
"workflow_node.end.label": "End",
|
||||
|
||||
|
@ -719,6 +719,18 @@
|
||||
"workflow_node.notify.form.provider_access.label": "通知渠道授权",
|
||||
"workflow_node.notify.form.provider_access.placeholder": "请选择通知渠道授权",
|
||||
"workflow_node.notify.form.provider_access.button": "新建",
|
||||
"workflow_node.notify.form.email_sender_address.label": "发送邮箱地址(可选)",
|
||||
"workflow_node.notify.form.email_sender_address.placeholder": "请输入发送邮箱地址",
|
||||
"workflow_node.notify.form.email_sender_address.tooltip": "不填写时,将使用所选通知渠道授权的默认发送邮箱地址。",
|
||||
"workflow_node.notify.form.email_receiver_address.label": "接收邮箱地址(可选)",
|
||||
"workflow_node.notify.form.email_receiver_address.placeholder": "请输入接收邮箱地址",
|
||||
"workflow_node.notify.form.email_receiver_address.tooltip": "不填写时,将使用所选通知渠道授权的默认接收邮箱地址。",
|
||||
"workflow_node.notify.form.mattermost_channel_id.label": "Mattermost 频道 ID(可选)",
|
||||
"workflow_node.notify.form.mattermost_channel_id.placeholder": "请输入 Mattermost 频道 ID",
|
||||
"workflow_node.notify.form.mattermost_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。",
|
||||
"workflow_node.notify.form.telegram_chat_id.label": "Telegram 会话 ID(可选)",
|
||||
"workflow_node.notify.form.telegram_chat_id.placeholder": "请输入 Telegram 会话 ID",
|
||||
"workflow_node.notify.form.telegram_chat_id.tooltip": "不填写时,将使用所选通知渠道授权的默认会话 ID。",
|
||||
|
||||
"workflow_node.end.label": "结束",
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user