Merge pull request #891 from fudiwei/dev

This commit is contained in:
RHQYZ
2025-08-11 19:52:19 +08:00
committed by GitHub
231 changed files with 18173 additions and 1470 deletions
+3 -5
View File
@@ -4,7 +4,6 @@ import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"fmt"
"strings"
"time"
@@ -49,12 +48,11 @@ func (s *CertificateService) InitSchedule(ctx context.Context) error {
return
}
var settingsContent *domain.SettingsContentAsPersistence
json.Unmarshal([]byte(settings.Content), &settingsContent)
if settingsContent != nil && settingsContent.ExpiredCertificatesMaxDaysRetention != 0 {
persistenceSettings, _ := settings.UnmarshalContentAsPersistence()
if persistenceSettings != nil && persistenceSettings.ExpiredCertificatesMaxDaysRetention != 0 {
ret, err := s.certificateRepo.DeleteWhere(
context.Background(),
dbx.NewExp(fmt.Sprintf("validityNotAfter<DATETIME('now', '-%d days')", settingsContent.ExpiredCertificatesMaxDaysRetention)),
dbx.NewExp(fmt.Sprintf("validityNotAfter<DATETIME('now', '-%d days')", persistenceSettings.ExpiredCertificatesMaxDaysRetention)),
)
if err != nil {
app.GetLogger().Error("failed to delete expired certificates", "err", err)
+10
View File
@@ -1,5 +1,7 @@
package domain
import "encoding/json"
const CollectionNameSettings = "settings"
type Settings struct {
@@ -12,3 +14,11 @@ type SettingsContentAsPersistence struct {
WorkflowRunsMaxDaysRetention int `json:"workflowRunsMaxDaysRetention"`
ExpiredCertificatesMaxDaysRetention int `json:"expiredCertificatesMaxDaysRetention"`
}
func (s *Settings) UnmarshalContentAsPersistence() (*SettingsContentAsPersistence, error) {
var content *SettingsContentAsPersistence
if err := json.Unmarshal([]byte(s.Content), &content); err != nil {
return nil, err
}
return content, nil
}
+3 -5
View File
@@ -2,7 +2,6 @@ package workflow
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
@@ -59,14 +58,13 @@ func (s *WorkflowService) InitSchedule(ctx context.Context) error {
return
}
var settingsContent *domain.SettingsContentAsPersistence
json.Unmarshal([]byte(settings.Content), &settingsContent)
if settingsContent != nil && settingsContent.WorkflowRunsMaxDaysRetention != 0 {
persistenceSettings, _ := settings.UnmarshalContentAsPersistence()
if persistenceSettings != nil && persistenceSettings.WorkflowRunsMaxDaysRetention != 0 {
ret, err := s.workflowRunRepo.DeleteWhere(
context.Background(),
dbx.NewExp(fmt.Sprintf("status!='%s'", string(domain.WorkflowRunStatusTypePending))),
dbx.NewExp(fmt.Sprintf("status!='%s'", string(domain.WorkflowRunStatusTypeRunning))),
dbx.NewExp(fmt.Sprintf("endedAt<DATETIME('now', '-%d days')", settingsContent.WorkflowRunsMaxDaysRetention)),
dbx.NewExp(fmt.Sprintf("endedAt<DATETIME('now', '-%d days')", persistenceSettings.WorkflowRunsMaxDaysRetention)),
)
if err != nil {
app.GetLogger().Error("failed to delete workflow history runs", "err", err)
View File
+1577 -228
View File
File diff suppressed because it is too large Load Diff
+19 -16
View File
@@ -1,7 +1,6 @@
{
"name": "ui",
"name": "@certimate/webui",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --host",
@@ -10,17 +9,21 @@
"preview": "vite preview"
},
"dependencies": {
"@ant-design/icons": "^6.0.0",
"@ant-design/pro-components": "^2.8.10",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/language": "^6.11.2",
"@codemirror/legacy-modes": "^6.5.1",
"@flowgram.ai/document": "^0.3.4",
"@flowgram.ai/fixed-layout-editor": "^0.3.4",
"@flowgram.ai/minimap-plugin": "^0.3.4",
"@tabler/icons-react": "^3.34.1",
"@uiw/codemirror-extensions-basic-setup": "^4.24.1",
"@uiw/codemirror-theme-vscode": "^4.24.1",
"@uiw/react-codemirror": "^4.24.1",
"@uiw/codemirror-extensions-basic-setup": "^4.24.2",
"@uiw/codemirror-theme-vscode": "^4.24.2",
"@uiw/react-codemirror": "^4.24.2",
"ahooks": "^3.9.0",
"antd": "^5.26.6",
"antd": "^5.26.7",
"antd-zod": "^7.0.0",
"clsx": "^2.1.1",
"cron-parser": "^5.3.0",
@@ -29,19 +32,19 @@
"i18next-browser-languagedetector": "^8.2.0",
"immer": "^10.1.1",
"nanoid": "^5.1.5",
"pocketbase": "^0.26.1",
"pocketbase": "^0.26.2",
"radash": "^12.1.1",
"react": "^18.3.1",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.3.1",
"react-i18next": "^15.6.1",
"react-router-dom": "^7.7.0",
"react-router-dom": "^7.8.0",
"tailwind-merge": "^3.3.1",
"zod": "^4.0.5",
"zustand": "^5.0.6"
"zod": "^4.0.16",
"zustand": "^5.0.7"
},
"devDependencies": {
"@eslint/js": "^9.31.0",
"@eslint/js": "^9.33.0",
"@tailwindcss/postcss": "^4.1.11",
"@tailwindcss/vite": "^4.1.11",
"@types/file-saver": "^2.0.7",
@@ -52,19 +55,19 @@
"@types/react-dom": "^18.3.7",
"@vitejs/plugin-legacy": "^6.1.1",
"@vitejs/plugin-react": "^4.7.0",
"eslint": "^9.31.0",
"eslint": "^9.33.0",
"eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-better-tailwindcss": "^3.7.1",
"eslint-plugin-better-tailwindcss": "^3.7.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-prettier": "^5.5.3",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"fs-extra": "^11.3.0",
"fs-extra": "^11.3.1",
"prettier": "^3.6.2",
"tailwindcss": "^4.1.11",
"typescript": "^5.8.3",
"typescript-eslint": "^8.38.0",
"typescript-eslint": "^8.39.0",
"vite": "^6.3.5"
}
}
+4
View File
@@ -100,6 +100,10 @@ const RootApp = () => {
...antdTheme?.components?.Dropdown,
paddingBlock: 9,
},
Form: {
...antdTheme?.components?.Form,
itemMarginBottom: 28,
},
},
}}
>
+4 -2
View File
@@ -31,8 +31,10 @@ const AppVersionBadge = ({ className, style, children }: AppVersionBadgeProps) =
<Badge
className={className}
style={style}
styles={{ indicator: { transform: "scale(0.75) translate(50%, -50%)" } }}
count={hasNewVersion ? "NEW" : undefined}
styles={{
indicator: { transform: "scale(0.75) translate(50%, -50%)" },
}}
count={hasNewVersion ? "NEW" : void 0}
>
{children}
</Badge>
+1 -1
View File
@@ -72,7 +72,7 @@ const CodeInput = ({ className, style, disabled, language, ...props }: CodeInput
border: `1px solid ${isFocusWithin ? (themeToken.Input?.activeBorderColor ?? themeToken.colorPrimaryBorder) : themeToken.colorBorder}`,
borderRadius: `${themeToken.borderRadius}px`,
backgroundColor: disabled ? themeToken.colorBgContainerDisabled : themeToken.colorBgContainer,
boxShadow: isFocusWithin ? themeToken.Input?.activeShadow : undefined,
boxShadow: isFocusWithin ? themeToken.Input?.activeShadow : void 0,
overflow: "hidden",
}}
>
+19 -3
View File
@@ -1,4 +1,5 @@
import { useTranslation } from "react-i18next";
import { IconX } from "@tabler/icons-react";
import { useControllableValue } from "ahooks";
import { Button, Drawer, type DrawerProps, Flex, Form, type FormProps, type ModalProps } from "antd";
@@ -66,13 +67,14 @@ const DrawerForm = <T extends NonNullable<unknown> = any>({
});
const mergedFormProps: FormProps = {
clearOnDestroy: drawerProps?.destroyOnHidden ? true : undefined,
clearOnDestroy: drawerProps?.destroyOnHidden ? true : void 0,
...formProps,
...props,
};
const mergedDrawerProps: DrawerProps = {
...drawerProps,
closeIcon: false,
afterOpenChange: (open) => {
if (!open && !mergedFormProps.preserve) {
formInst.resetFields();
@@ -109,7 +111,7 @@ const DrawerForm = <T extends NonNullable<unknown> = any>({
<Drawer
{...mergedDrawerProps}
footer={
<Flex justify="end" gap="small">
<Flex className="px-2" justify="end" gap="small">
<Button {...cancelButtonProps} onClick={handleCancelClick}>
{cancelText ?? t("common.button.cancel")}
</Button>
@@ -120,7 +122,21 @@ const DrawerForm = <T extends NonNullable<unknown> = any>({
}
forceRender
open={open}
title={title}
title={
<Flex align="center" justify="space-between" gap="small">
<div className="flex-1 truncate">{title}</div>
{mergedDrawerProps.closeIcon !== false && (
<Button
className="ant-drawer-close"
style={{ marginInline: 0 }}
icon={mergedDrawerProps.closeIcon ?? <IconX size="1.25em" />}
size="small"
type="text"
onClick={handleCancelClick}
/>
)}
</Flex>
}
width={width}
>
<Form className={className} style={style} {...mergedFormProps} form={formInst}>
+1 -1
View File
@@ -75,7 +75,7 @@ const ModalForm = <T extends NonNullable<unknown> = any>({
});
const mergedFormProps: FormProps = {
clearOnDestroy: modalProps?.destroyOnHidden ? true : undefined,
clearOnDestroy: modalProps?.destroyOnHidden ? true : void 0,
...formProps,
...props,
};
+4 -4
View File
@@ -142,7 +142,7 @@ const MultipleInput = ({
allowRemove={allowRemove}
allowUp={allowUp}
disabled={disabled}
defaultValue={undefined}
defaultValue={void 0}
showSortButton={showSortButton}
value={element}
onBlur={() => handleInputBlur(index)}
@@ -245,10 +245,10 @@ const MultipleInputItem = forwardRef<MultipleInputItemInstance, MultipleInputIte
<Input
{...props}
ref={inputRef}
className={undefined}
style={undefined}
className={void 0}
style={void 0}
allowClear={allowClear}
defaultValue={undefined}
defaultValue={void 0}
value={value}
onChange={handleInputChange}
/>
@@ -2,7 +2,7 @@
import { IconList } from "@tabler/icons-react";
import { useControllableValue } from "ahooks";
import { Button, Form, Input, type InputProps, Space } from "antd";
import { nanoid } from "nanoid";
import { nanoid } from "nanoid/non-secure";
import { useAntdForm } from "@/hooks";
import ModalForm from "./ModalForm";
+2 -2
View File
@@ -4,7 +4,7 @@ import { IconFileImport } from "@tabler/icons-react";
import { Button, type ButtonProps, Input, type UploadProps } from "antd";
import { type TextAreaProps } from "antd/es/input/TextArea";
import { readFileContent } from "@/utils/file";
import { readFileAsText } from "@/utils/file";
export interface TextFileInputProps extends Omit<TextAreaProps, "onChange"> {
accept?: UploadProps["accept"];
@@ -27,7 +27,7 @@ const TextFileInput = ({ className, style, accept, disabled, readOnly, uploadTex
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
const { files } = e.target as HTMLInputElement;
if (files?.length) {
const value = await readFileContent(files[0]);
const value = await readFileAsText(files[0]);
onChange?.(value);
}
};
+30
View File
@@ -0,0 +1,30 @@
import { IconBulb } from "@tabler/icons-react";
import { Alert, Flex, Typography, theme } from "antd";
export interface TipsProps {
className?: string;
style?: React.CSSProperties;
message: React.ReactNode;
}
const Tips = ({ className, style, message }: TipsProps) => {
const { token: themeToken } = theme.useToken();
return (
<Alert
className={className}
style={style}
message={
<Flex gap="small">
<div>
<IconBulb size="1.5em" color={themeToken.colorInfo} />
</div>
<Typography.Text>{message}</Typography.Text>
</Flex>
}
type="info"
/>
);
};
export default Tips;
+21 -6
View File
@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { IconX } from "@tabler/icons-react";
import { useControllableValue } from "ahooks";
import { App, Button, Drawer, Flex } from "antd";
@@ -99,11 +100,11 @@ const AccessEditDrawer = ({ mode, data, loading, trigger, usage, afterSubmit, ..
<Drawer
afterOpenChange={setOpen}
closable={!formPending}
closeIcon={false}
destroyOnHidden
footer={
footerShow ? (
<Flex justify="end" gap="small">
<Flex className="px-2" justify="end" gap="small">
<Button onClick={handleCancelClick}>{t("common.button.cancel")}</Button>
<Button loading={formPending} type="primary" onClick={handleOkClick}>
{mode === "edit" ? t("common.button.save") : t("common.button.submit")}
@@ -116,9 +117,23 @@ const AccessEditDrawer = ({ mode, data, loading, trigger, usage, afterSubmit, ..
loading={loading}
maskClosable={!formPending}
open={open}
title={t(`access.action.${mode}.modal.title`)}
width={720}
onClose={() => setOpen(false)}
size="large"
title={
<Flex align="center" justify="space-between" gap="small">
<div className="flex-1 truncate">
{mode === "edit" && !!data ? t("access.action.edit.modal.title") + ` #${data.id}` : t(`access.action.${mode}.modal.title`)}
</div>
<Button
className="ant-drawer-close"
style={{ marginInline: 0 }}
icon={<IconX size="1.25em" />}
size="small"
type="text"
onClick={handleCancelClick}
/>
</Flex>
}
onClose={handleCancelClick}
>
<AccessForm ref={formRef} disabled={formPending} initialValues={data} mode={mode} usage={usage} onValuesChange={handleFormValuesChange} />
</Drawer>
@@ -134,7 +149,7 @@ const useProps = () => {
setOpen(open);
if (!open) {
setData(undefined);
setData(void 0);
}
};
+2 -6
View File
@@ -138,16 +138,12 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
default:
console.warn(`[certimate] unsupported provider usage: '${usage}'`);
}
return undefined;
}, [usage]);
const providerTooltip = useMemo(() => {
switch (usage) {
case "dns-hosting":
return <span dangerouslySetInnerHTML={{ __html: t("access.form.provider.tooltip") }}></span>;
}
return undefined;
}, [usage]);
const fieldProvider = Form.useWatch<z.infer<typeof formSchema>["provider"]>("provider", formInst);
@@ -344,7 +340,7 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
getFieldsValue: () => {
const values = formInst.getFieldsValue(true);
values.config = nestedFormInst.getFieldsValue();
values.reserve = usage === "ca" ? "ca" : usage === "notification" ? "notification" : undefined;
values.reserve = usage === "ca" ? "ca" : usage === "notification" ? "notification" : void 0;
return values;
},
resetFields: (fields) => {
@@ -356,7 +352,7 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
},
validateFields: (nameList, config) => {
const t1 = formInst.validateFields(nameList, config);
const t2 = nestedFormInst.validateFields(undefined, config);
const t2 = nestedFormInst.validateFields(void 0, config);
return Promise.all([t1, t2]).then(() => t1);
},
} as AccessFormInstance;
@@ -26,7 +26,7 @@ const AccessFormDiscordBotConfig = ({ form: formInst, formName, disabled, initia
const formSchema = z.object({
botToken: z
.string(t("access.form.discordbot_token.placeholder"))
.string()
.min(1, t("access.form.discordbot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 })),
channelId: z.string().nullish(),
@@ -26,7 +26,7 @@ const AccessFormSlackBotConfig = ({ form: formInst, formName, disabled, initialV
const formSchema = z.object({
botToken: z
.string(t("access.form.slackbot_token.placeholder"))
.string()
.min(1, t("access.form.slackbot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 })),
channelId: z.string().nullish(),
@@ -26,12 +26,12 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi
const formSchema = z.object({
botToken: z
.string(t("access.form.telegrambot_token.placeholder"))
.string()
.min(1, t("access.form.telegrambot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 })),
chatId: z
.preprocess(
(v) => (v == null || v === "" ? undefined : Number(v)),
(v) => (v == null || v === "" ? void 0 : Number(v)),
z.number().refine((v) => {
return !Number.isNaN(+v!) && +v! !== 0;
}, t("access.form.telegrambot_chat_id.placeholder"))
@@ -1,11 +1,12 @@
import { useTranslation } from "react-i18next";
import { IconChevronDown } from "@tabler/icons-react";
import { Alert, Button, Dropdown, Form, type FormInstance, Input, Select, Switch } from "antd";
import { Button, Dropdown, Form, type FormInstance, Input, Select, Switch } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import CodeInput from "@/components/CodeInput";
import Show from "@/components/Show";
import Tips from "@/components/Tips";
import { type AccessConfigForWebhook } from "@/domain/access";
type AccessFormWebhookConfigFieldValues = Nullish<AccessConfigForWebhook>;
@@ -330,7 +331,7 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
</Form.Item>
<Form.Item>
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_data_for_deployment.guide") }}></span>} />
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_data_for_deployment.guide") }}></span>} />
</Form.Item>
</Show>
@@ -373,7 +374,7 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
</Form.Item>
<Form.Item>
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_data_for_notification.guide") }}></span>} />
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_data_for_notification.guide") }}></span>} />
</Form.Item>
</Show>
+1 -1
View File
@@ -71,7 +71,7 @@ const AccessSelect = ({ onFilter, ...props }: AccessTypeSelectProps) => {
loading={!loadedAtOnce}
options={options}
optionFilterProp="label"
optionLabelProp={undefined}
optionLabelProp={void 0}
optionRender={(option) => renderOption(option.data.value)}
/>
);
@@ -1,6 +1,7 @@
import { useState } from "react";
import { IconX } from "@tabler/icons-react";
import { useControllableValue } from "ahooks";
import { Drawer } from "antd";
import { Button, Drawer, Flex } from "antd";
import Show from "@/components/Show";
import { type CertificateModel } from "@/domain/certificate";
@@ -30,12 +31,25 @@ const CertificateDetailDrawer = ({ data, loading, trigger, ...props }: Certifica
<Drawer
afterOpenChange={setOpen}
closeIcon={false}
destroyOnHidden
open={open}
loading={loading}
placement="right"
title={`Certificate #${data?.id}`}
width={720}
size="large"
title={
<Flex align="center" justify="space-between" gap="small">
<div className="flex-1 truncate">{data ? `Certificate #${data.id}` : "Certificate"}</div>
<Button
className="ant-drawer-close"
style={{ marginInline: 0 }}
icon={<IconX size="1.25em" />}
size="small"
type="text"
onClick={() => setOpen(false)}
/>
</Flex>
}
onClose={() => setOpen(false)}
>
<Show when={!!data}>
@@ -54,7 +68,7 @@ const useProps = () => {
setOpen(open);
if (!open) {
setData(undefined);
setData(void 0);
}
};
@@ -58,8 +58,8 @@ const ACMEDns01ProviderSelect = ({ onFilter, ...props }: ACMEDns01ProviderSelect
return <span style={{ color: themeToken.colorTextPlaceholder }}>{props.placeholder}</span>;
}}
options={options}
optionFilterProp={undefined}
optionLabelProp={undefined}
optionFilterProp={void 0}
optionLabelProp={void 0}
optionRender={(option) => renderOption(option.data.value)}
/>
);
@@ -114,7 +114,7 @@ const AccessProviderPicker = ({ className, style, autoFocus, placeholder, showOp
<Avatar className="bg-stone-100" icon={<img src={provider.icon} />} shape="square" size={28} />
<div className="flex-1 overflow-hidden">
<div className={mergeCls("max-w-full", showOptionTagAnyhow ? "mb-1 truncate" : "line-clamp-2")}>
<Typography.Text type={provider.builtin ? "secondary" : undefined}>{t(provider.name) || "\u00A0"}</Typography.Text>
<Typography.Text type={provider.builtin ? "secondary" : void 0}>{t(provider.name) || "\u00A0"}</Typography.Text>
</div>
<Show when={showOptionTagAnyhow}>
<div className="origin-left scale-80 whitespace-nowrap">
@@ -53,7 +53,7 @@ const AccessProviderSelect = ({ showOptionTags, onFilter, ...props }: AccessProv
<div className="flex max-w-full items-center justify-between gap-4 overflow-hidden">
<div className="flex items-center gap-2 truncate overflow-hidden">
<Avatar shape="square" src={provider.icon} size="small" />
<Typography.Text className="flex-1 truncate overflow-hidden" type={provider.builtin ? "secondary" : undefined} ellipsis>
<Typography.Text className="flex-1 truncate overflow-hidden" type={provider.builtin ? "secondary" : void 0} ellipsis>
{t(provider.name)}
</Typography.Text>
</div>
@@ -95,8 +95,8 @@ const AccessProviderSelect = ({ showOptionTags, onFilter, ...props }: AccessProv
return <span style={{ color: themeToken.colorTextPlaceholder }}>{props.placeholder}</span>;
}}
options={options}
optionFilterProp={undefined}
optionLabelProp={undefined}
optionFilterProp={void 0}
optionLabelProp={void 0}
optionRender={(option) => renderOption(option.data.value)}
/>
);
@@ -77,8 +77,8 @@ const CAProviderSelect = ({ onFilter, ...props }: CAProviderSelectProps) => {
return <span style={{ color: themeToken.colorTextPlaceholder }}>{props.placeholder}</span>;
}}
options={options}
optionFilterProp={undefined}
optionLabelProp={undefined}
optionFilterProp={void 0}
optionLabelProp={void 0}
optionRender={(option) => renderOption(option.data.value)}
/>
);
@@ -58,8 +58,8 @@ const DeploymentProviderSelect = ({ onFilter, ...props }: DeploymentProviderSele
return <span style={{ color: themeToken.colorTextPlaceholder }}>{props.placeholder}</span>;
}}
options={options}
optionFilterProp={undefined}
optionLabelProp={undefined}
optionFilterProp={void 0}
optionLabelProp={void 0}
optionRender={(option) => renderOption(option.data.value)}
/>
);
@@ -58,8 +58,8 @@ const NotificationProviderSelect = ({ onFilter, ...props }: NotificationProvider
return <span style={{ color: themeToken.colorTextPlaceholder }}>{props.placeholder}</span>;
}}
options={options}
optionFilterProp={undefined}
optionLabelProp={undefined}
optionFilterProp={void 0}
optionLabelProp={void 0}
optionRender={(option) => renderOption(option.data.value)}
/>
);
@@ -22,6 +22,10 @@ export interface WorkflowElementProps {
branchIndex?: number;
}
/**
*
* @deprecated
*/
const WorkflowElement = ({ node, disabled, branchId, branchIndex }: WorkflowElementProps) => {
const nodeEl = useMemo(() => {
switch (node.type) {
@@ -11,6 +11,10 @@ export interface WorkflowElementsProps {
disabled?: boolean;
}
/**
*
* @deprecated
*/
const WorkflowElements = ({ className, style, disabled }: WorkflowElementsProps) => {
const { workflow } = useWorkflowStore(useZustandShallowSelector(["workflow"]));
@@ -11,6 +11,10 @@ export interface WorkflowElementsProps {
disabled?: boolean;
}
/**
*
* @deprecated
*/
const WorkflowElementsContainer = ({ className, style, disabled }: WorkflowElementsProps) => {
const [scale, setScale] = useState(1);
@@ -30,15 +34,19 @@ const WorkflowElementsContainer = ({ className, style, disabled }: WorkflowEleme
</div>
</div>
<div className="absolute right-6 bottom-4 z-2">
<Card className="rounded-lg p-2 shadow-lg" styles={{ body: { padding: 0 } }}>
<div className="flex items-center gap-2">
<Button icon={<IconMinus size="1.25em" />} disabled={scale <= MIN_SCALE} onClick={() => setScale((s) => Math.max(MIN_SCALE, s - STEP_SCALE))} />
<Typography.Text className="min-w-12 text-center">{Math.round(scale * 100)}%</Typography.Text>
<Button icon={<IconPlus size="1.25em" />} disabled={scale >= MAX_SCALE} onClick={() => setScale((s) => Math.min(MAX_SCALE, s + STEP_SCALE))} />
<Button icon={<IconMaximize size="1.25em" />} onClick={() => setScale(1)} />
<div className="absolute bottom-4 z-2 w-full px-4">
<div className="container">
<div className="flex justify-end">
<Card className="rounded-lg p-2 shadow-lg" styles={{ body: { padding: 0 } }}>
<div className="flex items-center gap-2">
<Button icon={<IconMinus size="1.25em" />} disabled={scale <= MIN_SCALE} onClick={() => setScale((s) => Math.max(MIN_SCALE, s - STEP_SCALE))} />
<Typography.Text className="min-w-12 text-center">{Math.round(scale * 100)}%</Typography.Text>
<Button icon={<IconPlus size="1.25em" />} disabled={scale >= MAX_SCALE} onClick={() => setScale((s) => Math.min(MAX_SCALE, s + STEP_SCALE))} />
<Button icon={<IconMaximize size="1.25em" />} onClick={() => setScale(1)} />
</div>
</Card>
</div>
</Card>
</div>
</div>
</div>
);
@@ -1,6 +1,7 @@
import { useState } from "react";
import { IconX } from "@tabler/icons-react";
import { useControllableValue } from "ahooks";
import { Drawer } from "antd";
import { Button, Drawer, Flex } from "antd";
import Show from "@/components/Show";
import { type WorkflowRunModel } from "@/domain/workflowRun";
@@ -31,12 +32,25 @@ const WorkflowRunDetailDrawer = ({ data, loading, trigger, ...props }: WorkflowR
<Drawer
afterOpenChange={setOpen}
closeIcon={false}
destroyOnHidden
open={open}
loading={loading}
placement="right"
title={`WorkflowRun #${data?.id}`}
width={720}
size="large"
title={
<Flex align="center" justify="space-between" gap="small">
<div className="flex-1 truncate">{data ? `Workflow Run #${data.id}` : "Workflow Run"}</div>
<Button
className="ant-drawer-close"
style={{ marginInline: 0 }}
icon={<IconX size="1.25em" />}
size="small"
type="text"
onClick={() => setOpen(false)}
/>
</Flex>
}
onClose={() => setOpen(false)}
>
<Show when={!!data}>
@@ -43,7 +43,7 @@ const WorkflowStatusTag = ({ className, style, status }: WorkflowStatusTagProps)
);
case WORKFLOW_RUN_STATUSES.CANCELED:
return (
<Tag className={className} style={style} color="var(--color-danger)" icon={Icon}>
<Tag className={className} style={style} color="var(--color-warning)" icon={Icon}>
{t("workflow_run.props.status.canceled")}
</Tag>
);
@@ -0,0 +1,183 @@
import { forwardRef, useImperativeHandle, useMemo, useRef } from "react";
import {
ConstantKeys,
EditorRenderer,
FixedLayoutEditorProvider,
type FixedLayoutPluginContext,
type FixedLayoutProps,
type FlowDocumentJSON,
type FlowNodeEntity,
FlowTextKey,
getNodeForm,
} from "@flowgram.ai/fixed-layout-editor";
import { createMinimapPlugin } from "@flowgram.ai/minimap-plugin";
import "@flowgram.ai/fixed-layout-editor/index.css";
import { theme } from "antd";
import { getFlowComponents } from "./components";
import { EditorContextProvider } from "./EditorContext";
import NodeRender from "./NodeRender";
import { getFlowNodeRegistries } from "./nodes";
import { BranchNode } from "./nodes/_shared";
import "./flowgram.css";
export interface EditorProps {
className?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
initialData?: FlowDocumentJSON;
readonly?: boolean;
onNodeClick?: (ctx: FixedLayoutPluginContext, node: FlowNodeEntity) => void;
}
export interface EditorInstance extends FixedLayoutPluginContext {
validateNode(node: string | FlowNodeEntity): Promise<boolean>;
validateAllNodes(): Promise<boolean>;
}
const Editor = forwardRef<EditorInstance, EditorProps>(({ className, style, children, initialData, readonly, onNodeClick }, ref) => {
const { token: themeToken } = theme.useToken();
const flowgramEditorRef = useRef<FixedLayoutPluginContext>(null);
const flowgramEditorProps = useMemo<FixedLayoutProps>(
() => ({
initialData: initialData,
constants: {
[ConstantKeys.BASE_COLOR]: themeToken.colorBorder,
[ConstantKeys.BASE_ACTIVATED_COLOR]: themeToken.colorPrimary,
[ConstantKeys.NODE_SPACING]: 48,
[ConstantKeys.BRANCH_SPACING]: 48,
},
background: {
backgroundColor: themeToken.colorBgContainer,
dotSize: 0,
},
playground: {
autoFocus: true,
autoResize: true,
preventGlobalGesture: true,
},
selectBox: {
enable: false,
},
scroll: {
enableScrollLimit: true,
},
readonly: readonly,
nodeEngine: {
enable: true,
},
variableEngine: {
enable: true,
},
materials: {
components: getFlowComponents(),
renderTexts: {
[FlowTextKey.TRY_START_TEXT]: "Try",
[FlowTextKey.TRY_END_TEXT]: "Finally",
[FlowTextKey.CATCH_TEXT]: "Catch",
},
renderDefaultNode: NodeRender,
},
nodeRegistries: getFlowNodeRegistries(),
getNodeDefaultRegistry(type) {
return {
type,
meta: {
defaultExpanded: true,
},
formMeta: {
render: () => <BranchNode description={type} />,
},
};
},
plugins: () => [
createMinimapPlugin({
disableLayer: true,
enableDisplayAllNodes: true,
canvasStyle: {
canvasWidth: 160,
canvasHeight: 160,
},
}),
],
onAllLayersRendered: (ctx) => {
// 画布初始化后向下滚动一点,露出可能被 Alert 遮挡的部分
setTimeout(() => {
ctx.playground.config.scroll({ scrollY: -80 });
}, 1);
},
}),
[themeToken, initialData, readonly]
);
useImperativeHandle(ref, () => {
return {
get container() {
return flowgramEditorRef.current!.container;
},
get document() {
return flowgramEditorRef.current!.document;
},
get playground() {
return flowgramEditorRef.current!.playground;
},
get operation() {
return flowgramEditorRef.current!.operation;
},
get clipboard() {
return flowgramEditorRef.current!.clipboard;
},
get selection() {
return flowgramEditorRef.current!.selection;
},
get history() {
return flowgramEditorRef.current!.history;
},
get(identifier) {
return flowgramEditorRef.current!.get(identifier);
},
getAll(identifier) {
return flowgramEditorRef.current!.getAll(identifier);
},
validateNode(node) {
if (typeof node === "string") {
node = flowgramEditorRef.current!.document.getNode(node)!;
}
const form = getNodeForm(node);
return form ? form.validate().then((res) => res && !form.state.invalid) : Promise.resolve(true);
},
validateAllNodes() {
const nodes = flowgramEditorRef.current!.document.getAllNodes();
const forms = nodes.map((node) => getNodeForm(node)).filter((form) => form != null);
return Promise.allSettled(forms.map((form) => form.validate())).then((res) => forms.every((form, index) => res[index] && !form.state.invalid));
},
};
});
return (
<FixedLayoutEditorProvider ref={flowgramEditorRef} {...flowgramEditorProps}>
<EditorContextProvider value={{ onNodeClick: (node) => onNodeClick?.(flowgramEditorRef.current!, node) }}>
<EditorRenderer className={className} style={style} />
{children}
</EditorContextProvider>
</FixedLayoutEditorProvider>
);
});
export default Editor;
@@ -0,0 +1,20 @@
import { createContext, useContext } from "react";
import { type FlowNodeEntity } from "@flowgram.ai/fixed-layout-editor";
export type NodeRenderContextType = {
onNodeClick: (node: FlowNodeEntity) => void;
};
export const EditorContext = createContext<NodeRenderContextType>({
onNodeClick: () => {},
});
export const EditorContextProvider = EditorContext.Provider;
export const useEditorContext = () => {
const context = useContext(EditorContext);
if (!context) {
throw new Error("`EditorContext` must be used within a `EditorContextProvider`");
}
return context;
};
@@ -0,0 +1,36 @@
import { useService } from "@flowgram.ai/fixed-layout-editor";
import { FlowMinimapService, MinimapRender } from "@flowgram.ai/minimap-plugin";
export interface MinimapProps {
className?: string;
style?: React.CSSProperties;
}
const Minimap = ({ className, style }: MinimapProps) => {
const minimapService = useService(FlowMinimapService);
return (
<div className={className} style={style}>
<MinimapRender
service={minimapService}
panelStyles={{}}
containerStyles={{
pointerEvents: "auto",
position: "relative",
top: "unset",
right: "unset",
bottom: "unset",
left: "unset",
}}
inactiveStyle={{
opacity: 1,
scale: 0.5,
translateX: 0,
translateY: 0,
}}
/>
</div>
);
};
export default Minimap;
@@ -0,0 +1,100 @@
import { startTransition, useMemo, useState } from "react";
import { type FlowNodeEntity } from "@flowgram.ai/fixed-layout-editor";
import { useControllableValue } from "ahooks";
import { useTriggerElement } from "@/hooks";
import BizApplyNodeConfigDrawer from "./forms/BizApplyNodeConfigDrawer";
import BizDeployNodeConfigDrawer from "./forms/BizDeployNodeConfigDrawer";
import BizMonitorNodeConfigDrawer from "./forms/BizMonitorNodeConfigDrawer";
import BizNotifyNodeConfigDrawer from "./forms/BizNotifyNodeConfigDrawer";
import BizUploadNodeConfigDrawer from "./forms/BizUploadNodeConfigDrawer";
import BranchBlockNodeConfigDrawer from "./forms/BranchBlockNodeConfigDrawer";
import StartNodeConfigDrawer from "./forms/StartNodeConfigDrawer";
import { NodeType } from "./nodes/typings";
export interface NodeDrawerProps {
children?: React.ReactNode;
loading?: boolean;
node?: FlowNodeEntity;
open?: boolean;
trigger?: React.ReactNode;
onOpenChange?: (open: boolean) => void;
}
const NodeDrawer = (_: NodeDrawerProps) => {
const { node, trigger, ...props } = _;
const [open, setOpen] = useControllableValue<boolean>(props, {
valuePropName: "open",
defaultValuePropName: "defaultOpen",
trigger: "onOpenChange",
});
const triggerEl = useTriggerElement(trigger, { onClick: () => setOpen(true) });
const drawerProps = useMemo(
() => ({
...props,
node: node!,
open: open,
onOpenChange: (open: boolean) => {
setOpen(open);
},
}),
[props, node, open]
);
return (
<>
{triggerEl}
{node?.flowNodeType === NodeType.Start ? (
<StartNodeConfigDrawer {...drawerProps} />
) : node?.flowNodeType === NodeType.BizApply ? (
<BizApplyNodeConfigDrawer {...drawerProps} />
) : node?.flowNodeType === NodeType.BizUpload ? (
<BizUploadNodeConfigDrawer {...drawerProps} />
) : node?.flowNodeType === NodeType.BizMonitor ? (
<BizMonitorNodeConfigDrawer {...drawerProps} />
) : node?.flowNodeType === NodeType.BizDeploy ? (
<BizDeployNodeConfigDrawer {...drawerProps} />
) : node?.flowNodeType === NodeType.BizNotify ? (
<BizNotifyNodeConfigDrawer {...drawerProps} />
) : node?.flowNodeType === NodeType.BranchBlock ? (
<BranchBlockNodeConfigDrawer {...drawerProps} />
) : (
<></>
)}
</>
);
};
const useProps = () => {
const [node, setNode] = useState<NodeDrawerProps["node"]>();
const [open, setOpen] = useState<boolean>(false);
const onOpenChange = (open: boolean) => {
setOpen(open);
startTransition(() => {
if (!open) {
setNode(void 0);
}
});
};
return {
node,
open,
setNode,
setOpen,
onOpenChange,
};
};
const _default = Object.assign(NodeDrawer, {
useProps,
});
export default _default;
@@ -0,0 +1,58 @@
import { useEffect } from "react";
import { type NodeRenderProps, useClientContext, useNodeRender, useRefresh } from "@flowgram.ai/fixed-layout-editor";
import { useEditorContext } from "./EditorContext";
import { NodeRenderContextProvider } from "./NodeRenderContext";
import { type NodeRegistry } from "./nodes/typings";
export interface NodeProps extends NodeRenderProps {}
const Node = (_: NodeProps) => {
const ctx = useClientContext();
const refresh = useRefresh();
const nodeRender = useNodeRender();
useEffect(() => {
const d = ctx.document.originTree.onTreeChange(() => refresh());
return () => d.dispose();
}, []);
useEffect(() => {
const d1 = nodeRender.form?.onFormValuesChange?.(() => refresh());
const d2 = nodeRender.form?.onValidate?.(() => refresh());
return () => {
d1?.dispose();
d2?.dispose();
};
}, [nodeRender.form]);
const { onNodeClick } = useEditorContext();
const handleNodeClick = () => {
const node = nodeRender.node;
if (node.getNodeRegistry<NodeRegistry>().meta?.clickable) {
onNodeClick?.(node);
}
};
return (
<div
style={{
opacity: nodeRender.dragging ? 0.3 : 1,
...nodeRender.node.getNodeRegistry<NodeRegistry>().meta?.style,
}}
onMouseEnter={nodeRender.onMouseEnter}
onMouseLeave={nodeRender.onMouseLeave}
onMouseDown={(e) => {
nodeRender.startDrag(e);
e.stopPropagation();
}}
onClick={handleNodeClick}
>
<NodeRenderContextProvider value={nodeRender}>{nodeRender.form?.render()}</NodeRenderContextProvider>
</div>
);
};
export default Node;
@@ -0,0 +1,16 @@
import { createContext, useContext } from "react";
import { type NodeRenderReturnType } from "@flowgram.ai/fixed-layout-editor";
export type NodeRenderContextType = NodeRenderReturnType;
export const NodeRenderContext = createContext<NodeRenderContextType>({} as NodeRenderContextType);
export const NodeRenderContextProvider = NodeRenderContext.Provider;
export const useNodeRenderContext = () => {
const context = useContext(NodeRenderContext);
if (!context) {
throw new Error("`NodeRenderContext` must be used within a `NodeRenderContextProvider`");
}
return context;
};
@@ -0,0 +1,114 @@
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { EditorState, FlowLayoutDefault, useClientContext, usePlaygroundTools, useRefresh } from "@flowgram.ai/fixed-layout-editor";
import { IconHandStop, IconLayoutCards, IconMatrix, IconMaximize, IconMinus, IconPlus } from "@tabler/icons-react";
import { Button, Dropdown, Tooltip } from "antd";
import { mergeCls } from "@/utils/css";
import Minimap from "./Minimap";
export interface ToolbarProps {
className?: string;
style?: React.CSSProperties;
}
const Toolbar = ({ className, style }: ToolbarProps) => {
const { t } = useTranslation();
const ctx = useClientContext();
const { playground } = ctx;
const tools = usePlaygroundTools({ minZoom: 0.1, maxZoom: 3, padding: 48 });
const refresh = useRefresh();
useEffect(() => {
const d = playground.config.onReadonlyOrDisabledChange(() => refresh());
return () => d.dispose();
}, [playground]);
const [isMinimapVisible, setIsMinimapVisible] = useState(window.screen.availWidth >= 1024);
const [isMouseFriendly, setIsMouseFriendly] = useState(playground.editorState.is(EditorState.STATE_MOUSE_FRIENDLY_SELECT.id));
const handleToggleLayout = useCallback(() => {
if (tools.isVertical) {
tools.changeLayout(FlowLayoutDefault.HORIZONTAL_FIXED_LAYOUT);
} else {
tools.changeLayout(FlowLayoutDefault.VERTICAL_FIXED_LAYOUT);
}
}, [tools.isVertical]);
const handleToggleMinimap = useCallback(() => {
setIsMinimapVisible((prev) => !prev);
}, [isMinimapVisible]);
const handleToggleMouseFriendly = useCallback(() => {
if (isMouseFriendly) {
playground.editorState.changeState(EditorState.STATE_SELECT.id);
setIsMouseFriendly(false);
} else {
playground.editorState.changeState(EditorState.STATE_MOUSE_FRIENDLY_SELECT.id);
setIsMouseFriendly(true);
}
}, [isMouseFriendly]);
return (
<div className={className} style={style}>
<div className="relative flex items-center gap-2">
<Tooltip title={isMouseFriendly ? t("workflow.detail.design.toolbar.hand_mode") : t("workflow.detail.design.toolbar.pointer_mode")}>
<Button
ghost={isMouseFriendly}
icon={<IconHandStop size="1.25em" />}
type={isMouseFriendly ? "primary" : "default"}
onClick={handleToggleMouseFriendly}
/>
</Tooltip>
<Tooltip title={t("workflow.detail.design.toolbar.zoomout")}>
<Button icon={<IconMinus size="1.25em" />} onClick={() => tools.zoomout()} />
</Tooltip>
<Dropdown
menu={{
items: [
...[200, 100, 75, 50, 25].map((zoom) => ({
key: `${zoom}%`,
label: `${zoom}%`,
onClick: () => tools.updateZoom(zoom / 100),
})),
{
type: "divider",
},
{
key: "auto",
label: t("workflow.detail.design.toolbar.auto_fit"),
onClick: () => tools.fitView(),
},
],
}}
trigger={["click"]}
>
<Button className="w-16 text-center">{Math.round(tools.zoom * 100)}%</Button>
</Dropdown>
<Tooltip title={t("workflow.detail.design.toolbar.zoomin")}>
<Button icon={<IconPlus size="1.25em" />} onClick={() => tools.zoomin()} />
</Tooltip>
<Tooltip title={t("workflow.detail.design.toolbar.auto_fit")}>
<Button icon={<IconMaximize size="1.25em" />} onClick={() => tools.fitView()} />
</Tooltip>
<Tooltip title={tools.isVertical ? t("workflow.detail.design.toolbar.vertical_layout") : t("workflow.detail.design.toolbar.horizontal_layout")}>
<Button icon={<IconLayoutCards className={mergeCls({ ["rotate-90"]: tools.isVertical })} size="1.25em" />} onClick={handleToggleLayout} />
</Tooltip>
<Tooltip title={t("workflow.detail.design.toolbar.minimap")}>
<Button icon={<IconMatrix size="1.25em" />} ghost={isMinimapVisible} type={isMinimapVisible ? "primary" : "default"} onClick={handleToggleMinimap} />
</Tooltip>
{isMinimapVisible && <Minimap className="absolute right-0 bottom-[42px]" />}
</div>
</div>
);
};
export default Toolbar;
@@ -0,0 +1,60 @@
import { type AdderProps as FlowgramAdderProps, useClientContext } from "@flowgram.ai/fixed-layout-editor";
import { IconPlus } from "@tabler/icons-react";
import { Button, Dropdown } from "antd";
import { getFlowNodeRegistries } from "../nodes";
export interface AdderProps extends FlowgramAdderProps {}
const Adder = ({ from, hoverActivated }: AdderProps) => {
const ctx = useClientContext();
const { operation, playground } = ctx;
const menuItems = getFlowNodeRegistries()
.filter((registry) => {
if (registry.meta?.addDisable != null) {
return !registry.meta.addDisable;
}
return true;
})
.filter((registry) => {
if (registry.canAdd != null) {
return registry.canAdd(ctx, from);
}
return true;
})
.map((registry) => {
const Icon = registry.meta?.icon;
return {
key: registry.type,
label: registry.meta?.labelText ?? registry.type,
icon: <span className="anticon scale-125">{Icon && <Icon size="1em" />}</span>,
onClick: () => {
const block = operation.addFromNode(from, registry.onAdd!(ctx, from));
setTimeout(() => {
playground.scrollToView({
bounds: block.bounds,
scrollToCenter: true,
});
}, 1);
},
};
});
return playground.config.readonlyOrDisabled ? null : (
<div className="relative">
<Dropdown menu={{ items: menuItems }} placement="bottomRight" trigger={["click"]}>
{hoverActivated ? (
<Button icon={<IconPlus size="1em" stroke={3} />} shape="circle" size="small" type="primary" />
) : (
<div className="size-2 rounded-full bg-primary opacity-75"></div>
)}
</Dropdown>
</div>
);
};
export default Adder;
@@ -0,0 +1,54 @@
import { useTranslation } from "react-i18next";
import { type FlowNodeEntity, type AdderProps as FlowgramAdderProps, useClientContext } from "@flowgram.ai/fixed-layout-editor";
import { Button } from "antd";
import { BranchBlockNodeRegistry } from "../nodes/ConditionNode";
import { CatchBlockNodeRegistry } from "../nodes/TryCatchNode";
import { NodeType } from "../nodes/typings";
export interface BranchAdderProps extends FlowgramAdderProps {}
const BranchAdder = ({ node }: BranchAdderProps) => {
const { t } = useTranslation();
const ctx = useClientContext();
const { operation, playground } = ctx;
const handleAddBranch = () => {
let block: FlowNodeEntity;
switch (node.flowNodeType) {
case NodeType.Condition:
block = operation.addBlock(node, BranchBlockNodeRegistry.onAdd!(ctx, node));
break;
case NodeType.TryCatch:
block = operation.addBlock(node, CatchBlockNodeRegistry.onAdd!(ctx, node));
break;
default:
console.warn(`[certimate] unsupported node type for adding branch: '${node.flowNodeType}'`);
break;
}
setTimeout(() => {
playground.scrollToView({
bounds: block.bounds,
scrollToCenter: true,
});
}, 1);
};
return playground.config.readonlyOrDisabled ? null : (
<div
className="relative"
onMouseEnter={() => node.firstChild?.renderData?.toggleMouseEnter()}
onMouseLeave={() => node.firstChild?.renderData?.toggleMouseLeave()}
>
<Button shape="round" size="small" onClick={handleAddBranch}>
<span className="text-xs">{t("workflow.detail.design.editor.add_branch")}</span>
</Button>
</div>
);
};
export default BranchAdder;
@@ -0,0 +1,9 @@
import { type CollapseProps as FlowgramCollapseProps } from "@flowgram.ai/fixed-layout-editor";
export interface CollapseProps extends FlowgramCollapseProps {}
const Collapse = (_: CollapseProps) => {
return null;
};
export default Collapse;
@@ -0,0 +1,14 @@
import { type DragNodeProps as FlowgramDragNodeProps } from "@flowgram.ai/fixed-layout-editor";
import { IconGradienter } from "@tabler/icons-react";
const DragHighlightAdder = (_: FlowgramDragNodeProps) => {
return (
<div className="size-4 animate-ping rounded-full bg-primary text-white shadow">
<div className="flex size-full items-center justify-center">
<IconGradienter size="1em" />
</div>
</div>
);
};
export default DragHighlightAdder;
@@ -0,0 +1,26 @@
import { type FlowNodeEntity, type DragNodeProps as FlowgramDragNodeProps, getNodeForm } from "@flowgram.ai/fixed-layout-editor";
import { Badge, Card } from "antd";
export interface DragNodeProps extends FlowgramDragNodeProps {
dragStart: FlowNodeEntity;
dragNodes: FlowNodeEntity[];
}
const DragNode = ({ dragStart, dragNodes }: DragNodeProps) => {
const count = (dragNodes || [])
.map((n) => (n.allCollapsedChildren.length ? n.allCollapsedChildren.filter((_n) => !_n.hidden).length : 1))
.reduce((acc, cur) => acc + cur, 0);
return (
<Badge count={count > 1 ? count : 0} size="small">
<div className="relative w-[160px]">
<Card className="bg-transparent shadow" styles={{ body: { padding: 0 } }}>
<div className="overflow-hidden px-4 py-2 text-primary">
<div className="truncate">{dragStart ? getNodeForm(dragStart)?.getValueIn("name") || `#${dragStart?.id}` : "\u00A0"}</div>
</div>
</Card>
</div>
</Badge>
);
};
export default DragNode;
@@ -0,0 +1,31 @@
import { FlowDragLayer, type AdderProps as FlowgramAdderProps, usePlayground } from "@flowgram.ai/fixed-layout-editor";
import { IconChevronsDown } from "@tabler/icons-react";
export interface DraggingAdderProps extends FlowgramAdderProps {}
const DraggingAdder = ({ from }: DraggingAdderProps) => {
const playground = usePlayground();
const layer = playground.getLayer(FlowDragLayer);
if (!layer) return <></>;
if (
layer.options.canDrop &&
!layer.options.canDrop({
dragNodes: layer.dragEntities ?? [],
dropNode: from,
isBranch: false,
})
) {
return <></>;
}
return (
<div className="size-4 animate-bounce rounded-full bg-primary text-white shadow">
<div className="flex size-full items-center justify-center">
<IconChevronsDown size="1em" />
</div>
</div>
);
};
export default DraggingAdder;
@@ -0,0 +1,11 @@
import { type FlowNodeEntity } from "@flowgram.ai/fixed-layout-editor";
export interface NullProps {
node: FlowNodeEntity;
}
const Null = (_: NullProps) => {
return null;
};
export default Null;
@@ -0,0 +1,62 @@
import {
type CustomLabelProps,
FlowNodeRenderData,
FlowNodeTransformData,
FlowRendererRegistry,
FlowTextKey,
useBaseColor,
} from "@flowgram.ai/fixed-layout-editor";
export interface TryCatchCollapseProps extends CustomLabelProps {}
const TryCatchCollapse = ({ node, ...props }: TryCatchCollapseProps) => {
const { baseColor, baseActivatedColor } = useBaseColor();
const nodeRenderData = node.getData(FlowNodeRenderData)!;
const nodeTransformData = node.getData(FlowNodeTransformData)!;
const handleMouseEnter = () => {
nodeRenderData.activated = true;
};
const handleMouseLeave = () => {
nodeRenderData.activated = false;
};
if (!nodeTransformData || !nodeTransformData.parent) {
return <></>;
}
const width = nodeTransformData.inputPoint.x - nodeTransformData.parent.inputPoint.x;
const height = 40;
return (
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{
width,
height,
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 6,
}}
>
<div
data-label-id={props.labelId}
style={{
fontSize: 12,
color: nodeRenderData.activated || nodeRenderData.lineActivated ? baseActivatedColor : baseColor,
textAlign: "center",
lineHeight: "20px",
whiteSpace: "nowrap",
backgroundColor: "var(--g-editor-background)",
}}
>
{node.getService(FlowRendererRegistry).getText(FlowTextKey.CATCH_TEXT)}
</div>
</div>
);
};
export default TryCatchCollapse;
@@ -0,0 +1,30 @@
import { FlowRendererKey } from "@flowgram.ai/fixed-layout-editor";
import Adder from "./Adder";
import BranchAdder from "./BranchAdder";
import Collapse from "./Collapse";
import DraggingAdder from "./DraggingAdder";
import DragHighlightAdder from "./DragHighlightAdder";
import DragNode from "./DragNode";
import Null from "./Null";
import TryCatchCollapse from "./TryCatchCollapse";
export const getFlowComponents = () => {
return {
[FlowRendererKey.ADDER]: Adder,
[FlowRendererKey.BRANCH_ADDER]: BranchAdder,
[FlowRendererKey.SLOT_ADDER]: Null,
[FlowRendererKey.COLLAPSE]: Collapse,
[FlowRendererKey.TRY_CATCH_COLLAPSE]: TryCatchCollapse,
[FlowRendererKey.SLOT_COLLPASE]: Null,
[FlowRendererKey.DRAG_NODE]: DragNode,
[FlowRendererKey.DRAG_HIGHLIGHT_ADDER]: DragHighlightAdder,
[FlowRendererKey.DRAG_BRANCH_HIGHLIGHT_ADDER]: DragHighlightAdder,
[FlowRendererKey.DRAGGABLE_ADDER]: DraggingAdder,
[FlowRendererKey.CONTEXT_MENU_POPOVER]: Null,
[FlowRendererKey.SELECTOR_BOX_POPOVER]: Null,
};
};
@@ -0,0 +1,26 @@
/* 隐藏 flowgram 的标签背景 */
/* flowgram 暂未提供相关配置项,这里需与 antd.colorBgContainer 保持一致 */
:root {
--g-editor-background: #fff;
}
.dark {
--g-editor-background: #262c2d;
}
/* 隐藏 flowgram 的折叠按钮 */
.flow-canvas-collapse-adder > .flow-canvas-collapse {
display: hidden !important;
position: absolute !important;
top: -99999px !important;
left: -99999px !important;
width: 0 !important;
height: 0 !important;
visibility: hidden !important;
transform: scale(0) !important;
}
/* 控制 flowgram 线条样式 */
/* flowgram 暂未提供相关配置项 */
.flow-lines-container {
stroke-width: 1.5px;
}
@@ -0,0 +1,40 @@
import { useTranslation } from "react-i18next";
import { type FlowNodeEntity } from "@flowgram.ai/fixed-layout-editor";
import { Form } from "antd";
import { NodeConfigDrawer } from "./_shared";
import BizApplyNodeConfigForm from "./BizApplyNodeConfigForm";
import { NodeType } from "../nodes/typings";
export interface BizApplyNodeConfigDrawerProps {
loading?: boolean;
node: FlowNodeEntity;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}
const BizApplyNodeConfigDrawer = (_: BizApplyNodeConfigDrawerProps) => {
const { node, ...props } = _;
if (node.flowNodeType !== NodeType.BizApply) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.BizApply}`);
}
const { i18n } = useTranslation();
const [formInst] = Form.useForm();
return (
<NodeConfigDrawer
anchor={{
items: BizApplyNodeConfigForm.getAnchorItems({ i18n }),
}}
form={formInst}
node={node}
{...props}
>
<BizApplyNodeConfigForm form={formInst} node={node} />
</NodeConfigDrawer>
);
};
export default BizApplyNodeConfigDrawer;
@@ -0,0 +1,652 @@
import { memo, useEffect, useMemo, useState } from "react";
import { getI18n, useTranslation } from "react-i18next";
import { Link } from "react-router";
import { QuestionCircleOutlined as IconQuestionCircleOutlined } from "@ant-design/icons";
import { type FlowNodeEntity, getNodeForm } from "@flowgram.ai/fixed-layout-editor";
import { IconChevronRight, IconCircleMinus, IconPlus } from "@tabler/icons-react";
import { useControllableValue } from "ahooks";
import { type AnchorProps, AutoComplete, Button, Divider, Flex, Form, type FormInstance, Input, InputNumber, Select, Switch, Tooltip, Typography } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import AccessEditDrawer from "@/components/access/AccessEditDrawer";
import AccessSelect from "@/components/access/AccessSelect";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
import ACMEDns01ProviderSelect from "@/components/provider/ACMEDns01ProviderSelect";
import CAProviderSelect from "@/components/provider/CAProviderSelect";
import Show from "@/components/Show";
import { ACCESS_USAGES, ACME_DNS01_PROVIDERS, accessProvidersMap, acmeDns01ProvidersMap, caProvidersMap } from "@/domain/provider";
import { type WorkflowNodeConfigForApply, defaultNodeConfigForApply } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { useAccessesStore } from "@/stores/access";
import { useContactEmailsStore } from "@/stores/contact";
import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/validators";
import { FormNestedFieldsContextProvider, NodeFormContextProvider } from "./_context";
import BizApplyNodeConfigFormProviderAliyunESA from "./BizApplyNodeConfigFormProviderAliyunESA";
import BizApplyNodeConfigFormProviderAWSRoute53 from "./BizApplyNodeConfigFormProviderAWSRoute53";
import BizApplyNodeConfigFormProviderHuaweiCloudDNS from "./BizApplyNodeConfigFormProviderHuaweiCloudDNS";
import BizApplyNodeConfigFormProviderJDCloudDNS from "./BizApplyNodeConfigFormProviderJDCloudDNS";
import BizApplyNodeConfigFormProviderTencentCloudEO from "./BizApplyNodeConfigFormProviderTencentCloudEO";
import { NodeType } from "../nodes/typings";
const MULTIPLE_INPUT_SEPARATOR = ";";
export interface BizApplyNodeConfigFormProps {
form: FormInstance;
node: FlowNodeEntity;
}
const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps) => {
if (node.flowNodeType !== NodeType.BizApply) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.BizApply}`);
}
const { i18n, t } = useTranslation();
const { accesses } = useAccessesStore(useZustandShallowSelector("accesses"));
const initialValues = useMemo(() => {
return getNodeForm(node)?.getValueIn("config") as WorkflowNodeConfigForApply | undefined;
}, [node]);
const formSchema = getSchema({ i18n });
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
form: props.form,
name: "workflowNodeBizApplyConfigForm",
initialValues: initialValues ?? getInitialValues(),
});
const fieldProvider = Form.useWatch<string>("provider", { form: formInst, preserve: true });
const fieldProviderAccessId = Form.useWatch<string>("providerAccessId", { form: formInst, preserve: true });
const fieldCAProvider = Form.useWatch<string>("caProvider", { form: formInst, preserve: true });
const NestedProviderConfigFields = useMemo(() => {
/*
注意:如果追加新的子组件,请保持以 ASCII 排序。
NOTICE: If you add new child component, please keep ASCII order.
*/
switch (fieldProvider) {
case ACME_DNS01_PROVIDERS.ALIYUN_ESA:
return BizApplyNodeConfigFormProviderAliyunESA;
case ACME_DNS01_PROVIDERS.AWS:
case ACME_DNS01_PROVIDERS.AWS_ROUTE53:
return BizApplyNodeConfigFormProviderAWSRoute53;
case ACME_DNS01_PROVIDERS.HUAWEICLOUD:
case ACME_DNS01_PROVIDERS.HUAWEICLOUD_DNS:
return BizApplyNodeConfigFormProviderHuaweiCloudDNS;
case ACME_DNS01_PROVIDERS.JDCLOUD:
case ACME_DNS01_PROVIDERS.JDCLOUD_DNS:
return BizApplyNodeConfigFormProviderJDCloudDNS;
case ACME_DNS01_PROVIDERS.TENCENTCLOUD_EO:
return BizApplyNodeConfigFormProviderTencentCloudEO;
}
}, [fieldProvider]);
const [showProvider, setShowProvider] = useState(false);
useEffect(() => {
// 通常情况下每个授权信息只对应一个 DNS 提供商,此时无需显示 DNS 提供商字段;
// 如果对应多个(如 AWS 的 Route53、Lightsail,阿里云的 DNS、ESA,腾讯云的 DNS、EdgeOne 等),则显示。
if (fieldProviderAccessId) {
const access = accesses.find((e) => e.id === fieldProviderAccessId);
const providers = Array.from(acmeDns01ProvidersMap.values()).filter((e) => e.provider === access?.provider);
setShowProvider(providers.length > 1);
} else {
setShowProvider(false);
}
}, [accesses, fieldProviderAccessId]);
const [showCAProviderAccess, setShowCAProviderAccess] = useState(false);
useEffect(() => {
// 内置的 CA 提供商(如 Let's Encrypt)无需显示授权信息字段
if (fieldCAProvider) {
const provider = caProvidersMap.get(fieldCAProvider);
setShowCAProviderAccess(!provider?.builtin);
} else {
setShowCAProviderAccess(false);
}
}, [fieldCAProvider]);
const handleProviderSelect = (value: string) => {
if (fieldProvider === value) return;
// 切换 DNS 提供商时联动授权信息
if (initialValues?.provider === value) {
formInst.setFieldValue("providerAccessId", initialValues?.providerAccessId);
} else {
if (acmeDns01ProvidersMap.get(fieldProvider)?.provider !== acmeDns01ProvidersMap.get(value)?.provider) {
formInst.setFieldValue("providerAccessId", void 0);
}
}
};
const handleProviderAccessSelect = (value: string) => {
// 切换授权信息时联动 DNS 提供商
const access = accesses.find((access) => access.id === value);
const provider = Array.from(acmeDns01ProvidersMap.values()).find((provider) => provider.provider === access?.provider);
if (fieldProvider !== provider?.type) {
formInst.setFieldValue("provider", provider?.type);
}
};
const handleCAProviderSelect = (value?: string | undefined) => {
// 切换 CA 提供商时联动授权信息
if (value === "") {
setTimeout(() => {
formInst.setFieldValue("caProvider", void 0);
formInst.setFieldValue("caProviderAccessId", void 0);
}, 1);
} else if (initialValues?.caProvider === value) {
formInst.setFieldValue("caProviderAccessId", initialValues?.caProviderAccessId);
} else {
if (caProvidersMap.get(fieldCAProvider)?.provider !== caProvidersMap.get(value!)?.provider) {
formInst.setFieldValue("caProviderAccessId", void 0);
}
}
};
return (
<NodeFormContextProvider value={{ node }}>
<Form {...formProps} clearOnDestroy={true} form={formInst} layout="vertical" preserve={false} scrollToFirstError>
<div id="parameters" data-anchor="parameters">
<Form.Item
name="domains"
label={t("workflow_node.apply.form.domains.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.domains.tooltip") }}></span>}
>
<MultipleSplitValueInput
modalTitle={t("workflow_node.apply.form.domains.multiple_input_modal.title")}
placeholder={t("workflow_node.apply.form.domains.placeholder")}
placeholderInModal={t("workflow_node.apply.form.domains.multiple_input_modal.placeholder")}
separator={MULTIPLE_INPUT_SEPARATOR}
splitOptions={{ removeEmpty: true, trimSpace: true }}
/>
</Form.Item>
<Form.Item
name="contactEmail"
label={t("workflow_node.apply.form.contact_email.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.contact_email.tooltip") }}></span>}
>
<EmailInput placeholder={t("workflow_node.apply.form.contact_email.placeholder")} />
</Form.Item>
<Form.Item name="challengeType" label={t("workflow_node.apply.form.challenge_type.label")} rules={[formRule]} hidden>
<Select
options={["DNS-01"].map((e) => ({
label: e,
value: e.toLowerCase(),
}))}
placeholder={t("workflow_node.apply.form.challenge_type.placeholder")}
/>
</Form.Item>
<Form.Item name="provider" label={t("workflow_node.apply.form.provider.label")} hidden={!showProvider} rules={[formRule]}>
<ACMEDns01ProviderSelect
disabled={!showProvider}
placeholder={t("workflow_node.apply.form.provider.placeholder")}
showSearch
onFilter={(_, option) => {
if (fieldProviderAccessId) {
return accesses.find((e) => e.id === fieldProviderAccessId)?.provider === option.provider;
}
return true;
}}
onSelect={handleProviderSelect}
/>
</Form.Item>
<Form.Item noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.apply.form.provider_access.label")}</span>
<Tooltip title={t("workflow_node.apply.form.provider_access.tooltip")}>
<Typography.Text className="ms-1" type="secondary">
<IconQuestionCircleOutlined />
</Typography.Text>
</Tooltip>
</div>
<div className="text-right">
<AccessEditDrawer
mode="create"
trigger={
<Button size="small" type="link">
{t("workflow_node.apply.form.provider_access.button")}
<IconPlus size="1.25em" />
</Button>
}
usage="dns"
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.DNS)) {
formInst.setFieldValue("providerAccessId", record.id);
handleProviderAccessSelect(record.id);
}
}}
/>
</div>
</div>
</label>
<Form.Item name="providerAccessId" rules={[formRule]}>
<AccessSelect
placeholder={t("workflow_node.apply.form.provider_access.placeholder")}
showSearch
onChange={handleProviderAccessSelect}
onFilter={(_, option) => {
if (option.reserve) return false;
const provider = accessProvidersMap.get(option.provider);
return !!provider?.usages?.includes(ACCESS_USAGES.DNS);
}}
/>
</Form.Item>
</Form.Item>
<FormNestedFieldsContextProvider value={{ parentNamePath: "providerConfig" }}>
{NestedProviderConfigFields && <NestedProviderConfigFields />}
</FormNestedFieldsContextProvider>
</div>
<div id="certificate" data-anchor="certificate">
<Divider size="small">
<Typography.Text className="text-xs font-normal" type="secondary">
{t("workflow_node.apply.form_anchor.certificate.title")}
</Typography.Text>
</Divider>
<Form.Item noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.apply.form.ca_provider.label")}</span>
</div>
<div className="text-right">
<Show when={!fieldCAProvider}>
<Link className="ant-typography" to="/settings/ssl-provider" target="_blank">
<Button size="small" type="link">
{t("workflow_node.apply.form.ca_provider.button")}
<IconChevronRight size="1.25em" />
</Button>
</Link>
</Show>
</div>
</div>
</label>
<Form.Item name="caProvider" rules={[formRule]}>
<CAProviderSelect
allowClear
placeholder={t("workflow_node.apply.form.ca_provider.placeholder")}
showSearch
onSelect={handleCAProviderSelect}
onClear={handleCAProviderSelect}
/>
</Form.Item>
</Form.Item>
<Form.Item hidden={!showCAProviderAccess} noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.apply.form.ca_provider_access.label")}</span>
</div>
<div className="text-right">
<AccessEditDrawer
data={{ provider: caProvidersMap.get(fieldCAProvider!)?.provider }}
mode="create"
trigger={
<Button size="small" type="link">
{t("workflow_node.apply.form.ca_provider_access.button")}
<IconChevronRight size="1.25em" />
</Button>
}
usage="ca"
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.CA)) {
formInst.setFieldValue("caProviderAccessId", record.id);
}
}}
/>
</div>
</div>
</label>
<Form.Item name="caProviderAccessId" rules={[formRule]}>
<AccessSelect
placeholder={t("workflow_node.apply.form.ca_provider_access.placeholder")}
showSearch
onFilter={(_, option) => {
if (option.reserve !== "ca") return false;
if (fieldCAProvider) return caProvidersMap.get(fieldCAProvider)?.provider === option.provider;
const provider = accessProvidersMap.get(option.provider);
return !!provider?.usages?.includes(ACCESS_USAGES.CA);
}}
/>
</Form.Item>
</Form.Item>
<Form.Item name="keyAlgorithm" label={t("workflow_node.apply.form.key_algorithm.label")} rules={[formRule]}>
<Select
options={["RSA2048", "RSA3072", "RSA4096", "RSA8192", "EC256", "EC384"].map((e) => ({
label: e,
value: e,
}))}
placeholder={t("workflow_node.apply.form.key_algorithm.placeholder")}
/>
</Form.Item>
<Form.Item
name="acmeProfile"
label={t("workflow_node.apply.form.acme_profile.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.acme_profile.tooltip") }}></span>}
>
<AutoComplete
allowClear
options={["classic", "tlsserver", "shortlived"].map((value) => ({ value }))}
placeholder={t("workflow_node.apply.form.acme_profile.placeholder")}
filterOption={(inputValue, option) => option!.value.toLowerCase().includes(inputValue.toLowerCase())}
/>
</Form.Item>
</div>
<div id="advanced" data-anchor="advanced">
<Divider size="small">
<Typography.Text className="text-xs font-normal" type="secondary">
{t("workflow_node.apply.form_anchor.advanced.title")}
</Typography.Text>
</Divider>
<Form.Item
name="nameservers"
label={t("workflow_node.apply.form.nameservers.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.nameservers.tooltip") }}></span>}
>
<MultipleSplitValueInput
modalTitle={t("workflow_node.apply.form.nameservers.multiple_input_modal.title")}
placeholder={t("workflow_node.apply.form.nameservers.placeholder")}
placeholderInModal={t("workflow_node.apply.form.nameservers.multiple_input_modal.placeholder")}
separator={MULTIPLE_INPUT_SEPARATOR}
splitOptions={{ removeEmpty: true, trimSpace: true }}
/>
</Form.Item>
<Form.Item
name="dnsPropagationWait"
label={t("workflow_node.apply.form.dns_propagation_wait.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.dns_propagation_wait.tooltip") }}></span>}
>
<Input
type="number"
allowClear
min={0}
max={3600}
placeholder={t("workflow_node.apply.form.dns_propagation_wait.placeholder")}
addonAfter={t("workflow_node.apply.form.dns_propagation_wait.unit")}
/>
</Form.Item>
<Form.Item
name="dnsPropagationTimeout"
label={t("workflow_node.apply.form.dns_propagation_timeout.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.dns_propagation_timeout.tooltip") }}></span>}
>
<Input
type="number"
allowClear
min={0}
max={3600}
placeholder={t("workflow_node.apply.form.dns_propagation_timeout.placeholder")}
addonAfter={t("workflow_node.apply.form.dns_propagation_timeout.unit")}
/>
</Form.Item>
<Form.Item
name="dnsTTL"
label={t("workflow_node.apply.form.dns_ttl.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.dns_ttl.tooltip") }}></span>}
>
<Input
type="number"
allowClear
min={0}
max={86400}
placeholder={t("workflow_node.apply.form.dns_ttl.placeholder")}
addonAfter={t("workflow_node.apply.form.dns_ttl.unit")}
/>
</Form.Item>
<Form.Item
name="disableFollowCNAME"
label={t("workflow_node.apply.form.disable_follow_cname.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.disable_follow_cname.tooltip") }}></span>}
>
<Switch />
</Form.Item>
<Form.Item
name="disableARI"
label={t("workflow_node.apply.form.disable_ari.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.disable_ari.tooltip") }}></span>}
>
<Switch />
</Form.Item>
</div>
<div id="strategy" data-anchor="strategy">
<Divider size="small">
<Typography.Text className="text-xs font-normal" type="secondary">
{t("workflow_node.apply.form_anchor.strategy.title")}
</Typography.Text>
</Divider>
<Form.Item
label={t("workflow_node.apply.form.skip_before_expiry_days.label")}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.skip_before_expiry_days.tooltip") }}></span>}
>
<Flex align="center" gap={8} wrap="wrap">
<div>{t("workflow_node.apply.form.skip_before_expiry_days.prefix")}</div>
<Form.Item name="skipBeforeExpiryDays" noStyle rules={[formRule]}>
<InputNumber
className="w-24"
min={1}
max={365}
placeholder={t("workflow_node.apply.form.skip_before_expiry_days.placeholder")}
addonAfter={t("workflow_node.apply.form.skip_before_expiry_days.unit")}
/>
</Form.Item>
<div>{t("workflow_node.apply.form.skip_before_expiry_days.suffix")}</div>
</Flex>
</Form.Item>
</div>
</Form>
</NodeFormContextProvider>
);
};
const EmailInput = memo(
({ disabled, placeholder, ...props }: { disabled?: boolean; placeholder?: string; value?: string; onChange?: (value: string) => void }) => {
const { emails, fetchEmails, removeEmail } = useContactEmailsStore();
useEffect(() => {
fetchEmails();
}, []);
const [value, setValue] = useControllableValue<string>(props, {
valuePropName: "value",
defaultValuePropName: "defaultValue",
trigger: "onChange",
});
const [inputValue, setInputValue] = useState<string>();
const renderOptionLabel = (email: string, removable: boolean = false) => (
<div className="flex items-center gap-2 overflow-hidden">
<span className="flex-1 truncate overflow-hidden">{email}</span>
{removable && (
<Button
color="default"
disabled={disabled}
icon={<IconCircleMinus size="1.25em" />}
size="small"
type="text"
onClick={(e) => {
removeEmail(email);
e.stopPropagation();
}}
/>
)}
</div>
);
const options = useMemo(() => {
const temp = emails.map((email) => ({
label: renderOptionLabel(email, true),
value: email,
}));
if (!!inputValue && temp.every((option) => option.value !== inputValue)) {
temp.unshift({
label: renderOptionLabel(inputValue),
value: inputValue,
});
}
return temp;
}, [emails, inputValue]);
const handleChange = (value: string) => {
setValue(value);
};
const handleSearch = (value: string) => {
setInputValue(value?.trim());
};
return (
<AutoComplete
backfill
defaultValue={value}
disabled={disabled}
filterOption
options={options}
placeholder={placeholder}
showSearch
value={value}
onChange={handleChange}
onSearch={handleSearch}
/>
);
}
);
const getAnchorItems = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }): Required<AnchorProps>["items"] => {
const { t } = i18n;
return ["parameters", "certificate", "advanced", "strategy"].map((key) => ({
key: key,
title: t(`workflow_node.apply.form_anchor.${key}.tab`),
href: "#" + key,
}));
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domains: "",
provider: "",
providerAccessId: "",
...defaultNodeConfigForApply(),
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
domains: z.string().refine((v) => {
if (!v) return false;
return String(v)
.split(MULTIPLE_INPUT_SEPARATOR)
.every((e) => validDomainName(e, { allowWildcard: true }));
}, t("common.errmsg.domain_invalid")),
contactEmail: z.email(t("common.errmsg.email_invalid")),
challengeType: z.string().nullish(),
provider: z.string().nonempty(t("workflow_node.apply.form.provider.placeholder")),
providerAccessId: z.string().nonempty(t("workflow_node.apply.form.provider_access.placeholder")),
providerConfig: z.any().nullish(),
caProvider: z.string().nullish(),
caProviderAccessId: z.string().nullish(),
caProviderConfig: z.any().nullish(),
keyAlgorithm: z.string().nonempty(t("workflow_node.apply.form.key_algorithm.placeholder")),
nameservers: z
.string()
.nullish()
.refine((v) => {
if (!v) return true;
return String(v)
.split(MULTIPLE_INPUT_SEPARATOR)
.every((e) => validIPv4Address(e) || validIPv6Address(e) || validDomainName(e));
}, t("common.errmsg.host_invalid")),
dnsPropagationWait: z.preprocess(
(v) => (v == null || v === "" ? void 0 : Number(v)),
z
.number()
.int(t("workflow_node.apply.form.dns_propagation_wait.placeholder"))
.gte(0, t("workflow_node.apply.form.dns_propagation_wait.placeholder"))
.nullish()
),
dnsPropagationTimeout: z.preprocess(
(v) => (v == null || v === "" ? void 0 : Number(v)),
z
.number()
.int(t("workflow_node.apply.form.dns_propagation_timeout.placeholder"))
.gte(1, t("workflow_node.apply.form.dns_propagation_timeout.placeholder"))
.nullish()
),
dnsTTL: z.preprocess(
(v) => (v == null || v === "" ? void 0 : Number(v)),
z.number().int(t("workflow_node.apply.form.dns_ttl.placeholder")).gte(1, t("workflow_node.apply.form.dns_ttl.placeholder")).nullish()
),
acmeProfile: z.string().nullish(),
disableFollowCNAME: z.boolean().nullish(),
disableARI: z.boolean().nullish(),
skipBeforeExpiryDays: z.preprocess(
(v) => Number(v),
z
.number()
.int(t("workflow_node.apply.form.skip_before_expiry_days.placeholder"))
.gte(1, t("workflow_node.apply.form.skip_before_expiry_days.placeholder"))
),
})
.superRefine((values, ctx) => {
if (values.caProvider) {
const provider = caProvidersMap.get(values.caProvider);
if (!provider?.builtin && !values.caProviderAccessId) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.apply.form.ca_provider_access.placeholder"),
path: ["caProviderAccessId"],
});
}
}
});
};
const _default = Object.assign(BizApplyNodeConfigForm, {
getAnchorItems,
getSchema,
});
export default _default;
@@ -0,0 +1,64 @@
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 BizApplyNodeConfigFormProviderAWSRoute53 = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.apply.form.aws_route53_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.aws_route53_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.apply.form.aws_route53_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "hostedZoneId"]}
initialValue={initialValues.hostedZoneId}
label={t("workflow_node.apply.form.aws_route53_hosted_zone_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.aws_route53_hosted_zone_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.apply.form.aws_route53_hosted_zone_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "us-east-1",
hostedZoneId: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.apply.form.aws_route53_region.placeholder")),
hostedZoneId: z.string().nonempty(t("workflow_node.apply.form.aws_route53_hosted_zone_id.placeholder")),
});
};
const _default = Object.assign(BizApplyNodeConfigFormProviderAWSRoute53, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,52 @@
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 BizApplyNodeConfigFormProviderAliyunESA = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.apply.form.aliyun_esa_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.aliyun_esa_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.apply.form.aliyun_esa_region.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "cn-hangzhou",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.apply.form.aliyun_esa_region.placeholder")),
});
};
const _default = Object.assign(BizApplyNodeConfigFormProviderAliyunESA, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,52 @@
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 BizApplyNodeConfigFormProviderHuaweiCloudDNS = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.apply.form.huaweicloud_dns_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.huaweicloud_dns_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.apply.form.huaweicloud_dns_region.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "cn-north-1",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.apply.form.huaweicloud_dns_region.placeholder")),
});
};
const _default = Object.assign(BizApplyNodeConfigFormProviderHuaweiCloudDNS, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,52 @@
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 BizApplyNodeConfigFormProviderJDCloudDNS = () => {
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, "regionId"]}
initialValue={initialValues.regionId}
label={t("workflow_node.apply.form.jdcloud_dns_region_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.jdcloud_dns_region_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.apply.form.jdcloud_dns_region_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
regionId: "cn-north-1",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
regionId: z.string().nonempty(t("workflow_node.apply.form.jdcloud_dns_region_id.placeholder")),
});
};
const _default = Object.assign(BizApplyNodeConfigFormProviderJDCloudDNS, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,52 @@
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 BizApplyNodeConfigFormProviderTencentCloudEO = () => {
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, "zoneId"]}
initialValue={initialValues.zoneId}
label={t("workflow_node.apply.form.tencentcloud_eo_zone_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.tencentcloud_eo_zone_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.apply.form.tencentcloud_eo_zone_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
zoneId: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
zoneId: z.string().nonempty(t("workflow_node.apply.form.tencentcloud_eo_zone_id.placeholder")),
});
};
const _default = Object.assign(BizApplyNodeConfigFormProviderTencentCloudEO, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,41 @@
import { useTranslation } from "react-i18next";
import { type FlowNodeEntity } from "@flowgram.ai/fixed-layout-editor";
import { Form } from "antd";
import { NodeConfigDrawer } from "./_shared";
import BizDeployNodeConfigForm from "./BizDeployNodeConfigForm";
import { NodeType } from "../nodes/typings";
export interface BizDeployNodeConfigDrawerProps {
loading?: boolean;
node: FlowNodeEntity;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}
const BizDeployNodeConfigDrawer = (_: BizDeployNodeConfigDrawerProps) => {
const { node, ...props } = _;
if (node.flowNodeType !== NodeType.BizDeploy) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.BizDeploy}`);
}
const { i18n } = useTranslation();
const [formInst] = Form.useForm();
const fieldProvider = Form.useWatch<string>("provider", { form: formInst, preserve: true });
return (
<NodeConfigDrawer
anchor={fieldProvider ? { items: BizDeployNodeConfigForm.getAnchorItems({ i18n }) } : false}
footer={fieldProvider ? true : false}
form={formInst}
node={node}
{...props}
>
<BizDeployNodeConfigForm form={formInst} node={node} />
</NodeConfigDrawer>
);
};
export default BizDeployNodeConfigDrawer;
@@ -0,0 +1,576 @@
import { useEffect, useMemo, useState } from "react";
import { getI18n, useTranslation } from "react-i18next";
import { QuestionCircleOutlined as IconQuestionCircleOutlined } from "@ant-design/icons";
import { type FlowNodeEntity, getNodeForm } from "@flowgram.ai/fixed-layout-editor";
import { IconPlus } from "@tabler/icons-react";
import { type AnchorProps, Button, Divider, Flex, Form, type FormInstance, Select, Switch, Tooltip, Typography, theme } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import AccessEditDrawer from "@/components/access/AccessEditDrawer";
import AccessSelect from "@/components/access/AccessSelect";
import DeploymentProviderPicker from "@/components/provider/DeploymentProviderPicker";
import DeploymentProviderSelect from "@/components/provider/DeploymentProviderSelect";
import Show from "@/components/Show";
import { ACCESS_USAGES, DEPLOYMENT_PROVIDERS, accessProvidersMap, deploymentProvidersMap } from "@/domain/provider";
import { type WorkflowNodeConfigForDeploy, WorkflowNodeType, defaultNodeConfigForDeploy } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
import { FormNestedFieldsContextProvider, NodeFormContextProvider } from "./_context";
import BizDeployNodeConfigFormProvider1PanelConsole from "./BizDeployNodeConfigFormProvider1PanelConsole";
import BizDeployNodeConfigFormProvider1PanelSite from "./BizDeployNodeConfigFormProvider1PanelSite";
import BizDeployNodeConfigFormProviderAliyunALB from "./BizDeployNodeConfigFormProviderAliyunALB";
import BizDeployNodeConfigFormProviderAliyunAPIGW from "./BizDeployNodeConfigFormProviderAliyunAPIGW";
import BizDeployNodeConfigFormProviderAliyunCAS from "./BizDeployNodeConfigFormProviderAliyunCAS";
import BizDeployNodeConfigFormProviderAliyunCASDeploy from "./BizDeployNodeConfigFormProviderAliyunCASDeploy";
import BizDeployNodeConfigFormProviderAliyunCDN from "./BizDeployNodeConfigFormProviderAliyunCDN";
import BizDeployNodeConfigFormProviderAliyunCLB from "./BizDeployNodeConfigFormProviderAliyunCLB";
import BizDeployNodeConfigFormProviderAliyunDCDN from "./BizDeployNodeConfigFormProviderAliyunDCDN";
import BizDeployNodeConfigFormProviderAliyunDDoS from "./BizDeployNodeConfigFormProviderAliyunDDoS";
import BizDeployNodeConfigFormProviderAliyunESA from "./BizDeployNodeConfigFormProviderAliyunESA";
import BizDeployNodeConfigFormProviderAliyunFC from "./BizDeployNodeConfigFormProviderAliyunFC";
import BizDeployNodeConfigFormProviderAliyunGA from "./BizDeployNodeConfigFormProviderAliyunGA";
import BizDeployNodeConfigFormProviderAliyunLive from "./BizDeployNodeConfigFormProviderAliyunLive";
import BizDeployNodeConfigFormProviderAliyunNLB from "./BizDeployNodeConfigFormProviderAliyunNLB";
import BizDeployNodeConfigFormProviderAliyunOSS from "./BizDeployNodeConfigFormProviderAliyunOSS";
import BizDeployNodeConfigFormProviderAliyunVOD from "./BizDeployNodeConfigFormProviderAliyunVOD";
import BizDeployNodeConfigFormProviderAliyunWAF from "./BizDeployNodeConfigFormProviderAliyunWAF";
import BizDeployNodeConfigFormProviderAPISIX from "./BizDeployNodeConfigFormProviderAPISIX";
import BizDeployNodeConfigFormProviderAWSACM from "./BizDeployNodeConfigFormProviderAWSACM";
import BizDeployNodeConfigFormProviderAWSCloudFront from "./BizDeployNodeConfigFormProviderAWSCloudFront";
import BizDeployNodeConfigFormProviderAWSIAM from "./BizDeployNodeConfigFormProviderAWSIAM";
import BizDeployNodeConfigFormProviderAzureKeyVault from "./BizDeployNodeConfigFormProviderAzureKeyVault";
import BizDeployNodeConfigFormProviderBaiduCloudAppBLB from "./BizDeployNodeConfigFormProviderBaiduCloudAppBLB";
import BizDeployNodeConfigFormProviderBaiduCloudBLB from "./BizDeployNodeConfigFormProviderBaiduCloudBLB";
import BizDeployNodeConfigFormProviderBaiduCloudCDN from "./BizDeployNodeConfigFormProviderBaiduCloudCDN";
import BizDeployNodeConfigFormProviderBaishanCDN from "./BizDeployNodeConfigFormProviderBaishanCDN";
import BizDeployNodeConfigFormProviderBaotaPanelConsole from "./BizDeployNodeConfigFormProviderBaotaPanelConsole";
import BizDeployNodeConfigFormProviderBaotaPanelSite from "./BizDeployNodeConfigFormProviderBaotaPanelSite";
import BizDeployNodeConfigFormProviderBaotaWAFSite from "./BizDeployNodeConfigFormProviderBaotaWAFSite";
import BizDeployNodeConfigFormProviderBunnyCDN from "./BizDeployNodeConfigFormProviderBunnyCDN";
import BizDeployNodeConfigFormProviderBytePlusCDN from "./BizDeployNodeConfigFormProviderBytePlusCDN";
import BizDeployNodeConfigFormProviderCdnfly from "./BizDeployNodeConfigFormProviderCdnfly";
import BizDeployNodeConfigFormProviderCTCCCloudAO from "./BizDeployNodeConfigFormProviderCTCCCloudAO";
import BizDeployNodeConfigFormProviderCTCCCloudCDN from "./BizDeployNodeConfigFormProviderCTCCCloudCDN";
import BizDeployNodeConfigFormProviderCTCCCloudELB from "./BizDeployNodeConfigFormProviderCTCCCloudELB";
import BizDeployNodeConfigFormProviderCTCCCloudICDN from "./BizDeployNodeConfigFormProviderCTCCCloudICDN";
import BizDeployNodeConfigFormProviderCTCCCloudLVDN from "./BizDeployNodeConfigFormProviderCTCCCloudLVDN";
import BizDeployNodeConfigFormProviderDogeCloudCDN from "./BizDeployNodeConfigFormProviderDogeCloudCDN";
import BizDeployNodeConfigFormProviderEdgioApplications from "./BizDeployNodeConfigFormProviderEdgioApplications";
import BizDeployNodeConfigFormProviderFlexCDN from "./BizDeployNodeConfigFormProviderFlexCDN";
import BizDeployNodeConfigFormProviderGcoreCDN from "./BizDeployNodeConfigFormProviderGcoreCDN";
import BizDeployNodeConfigFormProviderGoEdge from "./BizDeployNodeConfigFormProviderGoEdge";
import BizDeployNodeConfigFormProviderHuaweiCloudCDN from "./BizDeployNodeConfigFormProviderHuaweiCloudCDN";
import BizDeployNodeConfigFormProviderHuaweiCloudELB from "./BizDeployNodeConfigFormProviderHuaweiCloudELB";
import BizDeployNodeConfigFormProviderHuaweiCloudWAF from "./BizDeployNodeConfigFormProviderHuaweiCloudWAF";
import BizDeployNodeConfigFormProviderJDCloudALB from "./BizDeployNodeConfigFormProviderJDCloudALB";
import BizDeployNodeConfigFormProviderJDCloudCDN from "./BizDeployNodeConfigFormProviderJDCloudCDN";
import BizDeployNodeConfigFormProviderJDCloudLive from "./BizDeployNodeConfigFormProviderJDCloudLive";
import BizDeployNodeConfigFormProviderJDCloudVOD from "./BizDeployNodeConfigFormProviderJDCloudVOD";
import BizDeployNodeConfigFormProviderKong from "./BizDeployNodeConfigFormProviderKong";
import BizDeployNodeConfigFormProviderKubernetesSecret from "./BizDeployNodeConfigFormProviderKubernetesSecret";
import BizDeployNodeConfigFormProviderLeCDN from "./BizDeployNodeConfigFormProviderLeCDN";
import BizDeployNodeConfigFormProviderLocal from "./BizDeployNodeConfigFormProviderLocal";
import BizDeployNodeConfigFormProviderNetlifySite from "./BizDeployNodeConfigFormProviderNetlifySite";
import BizDeployNodeConfigFormProviderProxmoxVE from "./BizDeployNodeConfigFormProviderProxmoxVE";
import BizDeployNodeConfigFormProviderQiniuCDN from "./BizDeployNodeConfigFormProviderQiniuCDN";
import BizDeployNodeConfigFormProviderQiniuKodo from "./BizDeployNodeConfigFormProviderQiniuKodo";
import BizDeployNodeConfigFormProviderQiniuPili from "./BizDeployNodeConfigFormProviderQiniuPili";
import BizDeployNodeConfigFormProviderRainYunRCDN from "./BizDeployNodeConfigFormProviderRainYunRCDN";
import BizDeployNodeConfigFormProviderRatPanelSite from "./BizDeployNodeConfigFormProviderRatPanelSite";
import BizDeployNodeConfigFormProviderSafeLine from "./BizDeployNodeConfigFormProviderSafeLine";
import BizDeployNodeConfigFormProviderSSH from "./BizDeployNodeConfigFormProviderSSH";
import BizDeployNodeConfigFormProviderTencentCloudCDN from "./BizDeployNodeConfigFormProviderTencentCloudCDN";
import BizDeployNodeConfigFormProviderTencentCloudCLB from "./BizDeployNodeConfigFormProviderTencentCloudCLB";
import BizDeployNodeConfigFormProviderTencentCloudCOS from "./BizDeployNodeConfigFormProviderTencentCloudCOS";
import BizDeployNodeConfigFormProviderTencentCloudCSS from "./BizDeployNodeConfigFormProviderTencentCloudCSS";
import BizDeployNodeConfigFormProviderTencentCloudECDN from "./BizDeployNodeConfigFormProviderTencentCloudECDN";
import BizDeployNodeConfigFormProviderTencentCloudEO from "./BizDeployNodeConfigFormProviderTencentCloudEO";
import BizDeployNodeConfigFormProviderTencentCloudGAAP from "./BizDeployNodeConfigFormProviderTencentCloudGAAP";
import BizDeployNodeConfigFormProviderTencentCloudSCF from "./BizDeployNodeConfigFormProviderTencentCloudSCF";
import BizDeployNodeConfigFormProviderTencentCloudSSL from "./BizDeployNodeConfigFormProviderTencentCloudSSL";
import BizDeployNodeConfigFormProviderTencentCloudSSLDeploy from "./BizDeployNodeConfigFormProviderTencentCloudSSLDeploy";
import BizDeployNodeConfigFormProviderTencentCloudSSLUpdate from "./BizDeployNodeConfigFormProviderTencentCloudSSLUpdate";
import BizDeployNodeConfigFormProviderTencentCloudVOD from "./BizDeployNodeConfigFormProviderTencentCloudVOD";
import BizDeployNodeConfigFormProviderTencentCloudWAF from "./BizDeployNodeConfigFormProviderTencentCloudWAF";
import BizDeployNodeConfigFormProviderUCloudUCDN from "./BizDeployNodeConfigFormProviderUCloudUCDN";
import BizDeployNodeConfigFormProviderUCloudUS3 from "./BizDeployNodeConfigFormProviderUCloudUS3";
import BizDeployNodeConfigFormProviderUniCloudWebHost from "./BizDeployNodeConfigFormProviderUniCloudWebHost";
import BizDeployNodeConfigFormProviderUpyunCDN from "./BizDeployNodeConfigFormProviderUpyunCDN";
import BizDeployNodeConfigFormProviderUpyunFile from "./BizDeployNodeConfigFormProviderUpyunFile";
import BizDeployNodeConfigFormProviderVolcEngineALB from "./BizDeployNodeConfigFormProviderVolcEngineALB";
import BizDeployNodeConfigFormProviderVolcEngineCDN from "./BizDeployNodeConfigFormProviderVolcEngineCDN";
import BizDeployNodeConfigFormProviderVolcEngineCertCenter from "./BizDeployNodeConfigFormProviderVolcEngineCertCenter";
import BizDeployNodeConfigFormProviderVolcEngineCLB from "./BizDeployNodeConfigFormProviderVolcEngineCLB";
import BizDeployNodeConfigFormProviderVolcEngineDCDN from "./BizDeployNodeConfigFormProviderVolcEngineDCDN";
import BizDeployNodeConfigFormProviderVolcEngineImageX from "./BizDeployNodeConfigFormProviderVolcEngineImageX";
import BizDeployNodeConfigFormProviderVolcEngineLive from "./BizDeployNodeConfigFormProviderVolcEngineLive";
import BizDeployNodeConfigFormProviderVolcEngineTOS from "./BizDeployNodeConfigFormProviderVolcEngineTOS";
import BizDeployNodeConfigFormProviderWangsuCDN from "./BizDeployNodeConfigFormProviderWangsuCDN";
import BizDeployNodeConfigFormProviderWangsuCDNPro from "./BizDeployNodeConfigFormProviderWangsuCDNPro";
import BizDeployNodeConfigFormProviderWangsuCertificate from "./BizDeployNodeConfigFormProviderWangsuCertificate";
import BizDeployNodeConfigFormProviderWebhook from "./BizDeployNodeConfigFormProviderWebhook";
import { NodeType } from "../nodes/typings";
export interface BizDeployNodeConfigFormProps {
form: FormInstance;
node: FlowNodeEntity;
}
const BizDeployNodeConfigForm = ({ node, ...props }: BizDeployNodeConfigFormProps) => {
if (node.flowNodeType !== NodeType.BizDeploy) {
console.warn(`[certimate] current workflow node type is not: ${NodeType.BizDeploy}`);
}
const { i18n, t } = useTranslation();
const { token: themeToken } = theme.useToken();
const { getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"]));
const initialValues = useMemo(() => {
return getNodeForm(node)?.getValueIn("config") as WorkflowNodeConfigForDeploy | undefined;
}, [node]);
const formSchema = getSchema({ i18n });
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
form: props.form,
name: "workflowNodeBizDeployConfigForm",
initialValues: initialValues ?? getInitialValues(),
});
const fieldProvider = Form.useWatch<string>("provider", { form: formInst, preserve: true });
const [showProviderAccess, setShowProviderAccess] = useState(false);
useEffect(() => {
// 内置的部署提供商(如本地部署)无需显示授权信息字段
if (fieldProvider) {
const provider = deploymentProvidersMap.get(fieldProvider);
setShowProviderAccess(!provider?.builtin);
} else {
setShowProviderAccess(false);
}
}, [fieldProvider]);
// TODO: 适配 flowgram
const certificateCandidates = useMemo(() => {
const previousNodes = getWorkflowOuptutBeforeId(node.id, "certificate");
return previousNodes
.filter((node) => node.type === WorkflowNodeType.Apply || node.type === WorkflowNodeType.Upload)
.map((item) => {
return {
label: item.name,
options: (item.outputs ?? [])?.map((output) => {
return {
label: output.label,
value: `${item.id}#${output.name}`,
};
}),
};
})
.filter((group) => group.options.length > 0);
}, [node.id]);
const NestedProviderConfigFields = useMemo(() => {
/*
注意:如果追加新的子组件,请保持以 ASCII 排序。
NOTICE: If you add new child component, please keep ASCII order.
*/
switch (fieldProvider) {
case DEPLOYMENT_PROVIDERS["1PANEL_CONSOLE"]:
return BizDeployNodeConfigFormProvider1PanelConsole;
case DEPLOYMENT_PROVIDERS["1PANEL_SITE"]:
return BizDeployNodeConfigFormProvider1PanelSite;
case DEPLOYMENT_PROVIDERS.ALIYUN_ALB:
return BizDeployNodeConfigFormProviderAliyunALB;
case DEPLOYMENT_PROVIDERS.ALIYUN_APIGW:
return BizDeployNodeConfigFormProviderAliyunAPIGW;
case DEPLOYMENT_PROVIDERS.ALIYUN_CAS:
return BizDeployNodeConfigFormProviderAliyunCAS;
case DEPLOYMENT_PROVIDERS.ALIYUN_CAS_DEPLOY:
return BizDeployNodeConfigFormProviderAliyunCASDeploy;
case DEPLOYMENT_PROVIDERS.ALIYUN_CLB:
return BizDeployNodeConfigFormProviderAliyunCLB;
case DEPLOYMENT_PROVIDERS.ALIYUN_CDN:
return BizDeployNodeConfigFormProviderAliyunCDN;
case DEPLOYMENT_PROVIDERS.ALIYUN_DCDN:
return BizDeployNodeConfigFormProviderAliyunDCDN;
case DEPLOYMENT_PROVIDERS.ALIYUN_DDOS:
return BizDeployNodeConfigFormProviderAliyunDDoS;
case DEPLOYMENT_PROVIDERS.ALIYUN_ESA:
return BizDeployNodeConfigFormProviderAliyunESA;
case DEPLOYMENT_PROVIDERS.ALIYUN_FC:
return BizDeployNodeConfigFormProviderAliyunFC;
case DEPLOYMENT_PROVIDERS.ALIYUN_GA:
return BizDeployNodeConfigFormProviderAliyunGA;
case DEPLOYMENT_PROVIDERS.ALIYUN_LIVE:
return BizDeployNodeConfigFormProviderAliyunLive;
case DEPLOYMENT_PROVIDERS.ALIYUN_NLB:
return BizDeployNodeConfigFormProviderAliyunNLB;
case DEPLOYMENT_PROVIDERS.ALIYUN_OSS:
return BizDeployNodeConfigFormProviderAliyunOSS;
case DEPLOYMENT_PROVIDERS.ALIYUN_VOD:
return BizDeployNodeConfigFormProviderAliyunVOD;
case DEPLOYMENT_PROVIDERS.ALIYUN_WAF:
return BizDeployNodeConfigFormProviderAliyunWAF;
case DEPLOYMENT_PROVIDERS.APISIX:
return BizDeployNodeConfigFormProviderAPISIX;
case DEPLOYMENT_PROVIDERS.AWS_ACM:
return BizDeployNodeConfigFormProviderAWSACM;
case DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT:
return BizDeployNodeConfigFormProviderAWSCloudFront;
case DEPLOYMENT_PROVIDERS.AWS_IAM:
return BizDeployNodeConfigFormProviderAWSIAM;
case DEPLOYMENT_PROVIDERS.AZURE_KEYVAULT:
return BizDeployNodeConfigFormProviderAzureKeyVault;
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_APPBLB:
return BizDeployNodeConfigFormProviderBaiduCloudAppBLB;
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_BLB:
return BizDeployNodeConfigFormProviderBaiduCloudBLB;
case DEPLOYMENT_PROVIDERS.BAIDUCLOUD_CDN:
return BizDeployNodeConfigFormProviderBaiduCloudCDN;
case DEPLOYMENT_PROVIDERS.BAISHAN_CDN:
return BizDeployNodeConfigFormProviderBaishanCDN;
case DEPLOYMENT_PROVIDERS.BAOTAPANEL_CONSOLE:
return BizDeployNodeConfigFormProviderBaotaPanelConsole;
case DEPLOYMENT_PROVIDERS.BAOTAPANEL_SITE:
return BizDeployNodeConfigFormProviderBaotaPanelSite;
case DEPLOYMENT_PROVIDERS.BAOTAWAF_SITE:
return BizDeployNodeConfigFormProviderBaotaWAFSite;
case DEPLOYMENT_PROVIDERS.BUNNY_CDN:
return BizDeployNodeConfigFormProviderBunnyCDN;
case DEPLOYMENT_PROVIDERS.BYTEPLUS_CDN:
return BizDeployNodeConfigFormProviderBytePlusCDN;
case DEPLOYMENT_PROVIDERS.CDNFLY:
return BizDeployNodeConfigFormProviderCdnfly;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_AO:
return BizDeployNodeConfigFormProviderCTCCCloudAO;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN:
return BizDeployNodeConfigFormProviderCTCCCloudCDN;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_ELB:
return BizDeployNodeConfigFormProviderCTCCCloudELB;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_ICDN:
return BizDeployNodeConfigFormProviderCTCCCloudICDN;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_LVDN:
return BizDeployNodeConfigFormProviderCTCCCloudLVDN;
case DEPLOYMENT_PROVIDERS.DOGECLOUD_CDN:
return BizDeployNodeConfigFormProviderDogeCloudCDN;
case DEPLOYMENT_PROVIDERS.EDGIO_APPLICATIONS:
return BizDeployNodeConfigFormProviderEdgioApplications;
case DEPLOYMENT_PROVIDERS.FLEXCDN:
return BizDeployNodeConfigFormProviderFlexCDN;
case DEPLOYMENT_PROVIDERS.GCORE_CDN:
return BizDeployNodeConfigFormProviderGcoreCDN;
case DEPLOYMENT_PROVIDERS.GOEDGE:
return BizDeployNodeConfigFormProviderGoEdge;
case DEPLOYMENT_PROVIDERS.HUAWEICLOUD_CDN:
return BizDeployNodeConfigFormProviderHuaweiCloudCDN;
case DEPLOYMENT_PROVIDERS.HUAWEICLOUD_ELB:
return BizDeployNodeConfigFormProviderHuaweiCloudELB;
case DEPLOYMENT_PROVIDERS.HUAWEICLOUD_WAF:
return BizDeployNodeConfigFormProviderHuaweiCloudWAF;
case DEPLOYMENT_PROVIDERS.JDCLOUD_ALB:
return BizDeployNodeConfigFormProviderJDCloudALB;
case DEPLOYMENT_PROVIDERS.JDCLOUD_CDN:
return BizDeployNodeConfigFormProviderJDCloudCDN;
case DEPLOYMENT_PROVIDERS.JDCLOUD_LIVE:
return BizDeployNodeConfigFormProviderJDCloudLive;
case DEPLOYMENT_PROVIDERS.JDCLOUD_VOD:
return BizDeployNodeConfigFormProviderJDCloudVOD;
case DEPLOYMENT_PROVIDERS.KONG:
return BizDeployNodeConfigFormProviderKong;
case DEPLOYMENT_PROVIDERS.KUBERNETES_SECRET:
return BizDeployNodeConfigFormProviderKubernetesSecret;
case DEPLOYMENT_PROVIDERS.LECDN:
return BizDeployNodeConfigFormProviderLeCDN;
case DEPLOYMENT_PROVIDERS.LOCAL:
return BizDeployNodeConfigFormProviderLocal;
case DEPLOYMENT_PROVIDERS.NETLIFY_SITE:
return BizDeployNodeConfigFormProviderNetlifySite;
case DEPLOYMENT_PROVIDERS.PROXMOXVE:
return BizDeployNodeConfigFormProviderProxmoxVE;
case DEPLOYMENT_PROVIDERS.QINIU_CDN:
return BizDeployNodeConfigFormProviderQiniuCDN;
case DEPLOYMENT_PROVIDERS.QINIU_KODO:
return BizDeployNodeConfigFormProviderQiniuKodo;
case DEPLOYMENT_PROVIDERS.QINIU_PILI:
return BizDeployNodeConfigFormProviderQiniuPili;
case DEPLOYMENT_PROVIDERS.RAINYUN_RCDN:
return BizDeployNodeConfigFormProviderRainYunRCDN;
case DEPLOYMENT_PROVIDERS.RATPANEL_SITE:
return BizDeployNodeConfigFormProviderRatPanelSite;
case DEPLOYMENT_PROVIDERS.SAFELINE:
return BizDeployNodeConfigFormProviderSafeLine;
case DEPLOYMENT_PROVIDERS.SSH:
return BizDeployNodeConfigFormProviderSSH;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_CDN:
return BizDeployNodeConfigFormProviderTencentCloudCDN;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_CLB:
return BizDeployNodeConfigFormProviderTencentCloudCLB;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_COS:
return BizDeployNodeConfigFormProviderTencentCloudCOS;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_CSS:
return BizDeployNodeConfigFormProviderTencentCloudCSS;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_ECDN:
return BizDeployNodeConfigFormProviderTencentCloudECDN;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_EO:
return BizDeployNodeConfigFormProviderTencentCloudEO;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_GAAP:
return BizDeployNodeConfigFormProviderTencentCloudGAAP;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_SCF:
return BizDeployNodeConfigFormProviderTencentCloudSCF;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_SSL:
return BizDeployNodeConfigFormProviderTencentCloudSSL;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_SSL_DEPLOY:
return BizDeployNodeConfigFormProviderTencentCloudSSLDeploy;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_SSL_UPDATE:
return BizDeployNodeConfigFormProviderTencentCloudSSLUpdate;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_VOD:
return BizDeployNodeConfigFormProviderTencentCloudVOD;
case DEPLOYMENT_PROVIDERS.TENCENTCLOUD_WAF:
return BizDeployNodeConfigFormProviderTencentCloudWAF;
case DEPLOYMENT_PROVIDERS.UCLOUD_UCDN:
return BizDeployNodeConfigFormProviderUCloudUCDN;
case DEPLOYMENT_PROVIDERS.UCLOUD_US3:
return BizDeployNodeConfigFormProviderUCloudUS3;
case DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST:
return BizDeployNodeConfigFormProviderUniCloudWebHost;
case DEPLOYMENT_PROVIDERS.UPYUN_CDN:
return BizDeployNodeConfigFormProviderUpyunCDN;
case DEPLOYMENT_PROVIDERS.UPYUN_FILE:
return BizDeployNodeConfigFormProviderUpyunFile;
case DEPLOYMENT_PROVIDERS.VOLCENGINE_ALB:
return BizDeployNodeConfigFormProviderVolcEngineALB;
case DEPLOYMENT_PROVIDERS.VOLCENGINE_CDN:
return BizDeployNodeConfigFormProviderVolcEngineCDN;
case DEPLOYMENT_PROVIDERS.VOLCENGINE_CERTCENTER:
return BizDeployNodeConfigFormProviderVolcEngineCertCenter;
case DEPLOYMENT_PROVIDERS.VOLCENGINE_CLB:
return BizDeployNodeConfigFormProviderVolcEngineCLB;
case DEPLOYMENT_PROVIDERS.VOLCENGINE_DCDN:
return BizDeployNodeConfigFormProviderVolcEngineDCDN;
case DEPLOYMENT_PROVIDERS.VOLCENGINE_IMAGEX:
return BizDeployNodeConfigFormProviderVolcEngineImageX;
case DEPLOYMENT_PROVIDERS.VOLCENGINE_LIVE:
return BizDeployNodeConfigFormProviderVolcEngineLive;
case DEPLOYMENT_PROVIDERS.VOLCENGINE_TOS:
return BizDeployNodeConfigFormProviderVolcEngineTOS;
case DEPLOYMENT_PROVIDERS.WANGSU_CDN:
return BizDeployNodeConfigFormProviderWangsuCDN;
case DEPLOYMENT_PROVIDERS.WANGSU_CDNPRO:
return BizDeployNodeConfigFormProviderWangsuCDNPro;
case DEPLOYMENT_PROVIDERS.WANGSU_CERTIFICATE:
return BizDeployNodeConfigFormProviderWangsuCertificate;
case DEPLOYMENT_PROVIDERS.WEBHOOK:
return BizDeployNodeConfigFormProviderWebhook;
}
}, [fieldProvider]);
const handleProviderPick = (value: string) => {
formInst.setFieldValue("provider", value);
console.log();
};
const handleProviderSelect = (value?: string | undefined) => {
// 切换部署目标时重置表单,避免其他部署目标的配置字段影响当前部署目标
if (initialValues?.provider === value) {
formInst.resetFields();
} else {
const oldValues = formInst.getFieldsValue();
const newValues: Record<string, unknown> = {};
for (const key in oldValues) {
if (key === "provider" || key === "providerAccessId" || key === "certificate" || key === "skipOnLastSucceeded") {
newValues[key] = oldValues[key];
} else {
delete newValues[key];
}
}
formInst.setFieldsValue(newValues);
if (deploymentProvidersMap.get(fieldProvider)?.provider !== deploymentProvidersMap.get(value!)?.provider) {
formInst.setFieldValue("providerAccessId", void 0);
}
}
};
return (
<NodeFormContextProvider value={{ node }}>
<Form {...formProps} clearOnDestroy={true} form={formInst} layout="vertical" preserve={false} scrollToFirstError>
<Show when={!fieldProvider}>
<DeploymentProviderPicker autoFocus placeholder={t("workflow_node.deploy.form.provider.search.placeholder")} onSelect={handleProviderPick} />
</Show>
<div style={{ display: fieldProvider ? "block" : "none" }}>
<div id="parameters" data-anchor="parameters">
<Form.Item name="provider" label={t("workflow_node.deploy.form.provider.label")} rules={[formRule]}>
<DeploymentProviderSelect
allowClear
disabled={!!initialValues?.provider}
placeholder={t("workflow_node.deploy.form.provider.placeholder")}
showSearch
onSelect={handleProviderSelect}
onClear={handleProviderSelect}
/>
</Form.Item>
<Form.Item hidden={!showProviderAccess} noStyle>
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.deploy.form.provider_access.label")}</span>
<Tooltip title={t("workflow_node.deploy.form.provider_access.tooltip")}>
<Typography.Text className="ms-1" type="secondary">
<IconQuestionCircleOutlined />
</Typography.Text>
</Tooltip>
</div>
<div className="text-right">
<AccessEditDrawer
data={{ provider: deploymentProvidersMap.get(fieldProvider!)?.provider }}
mode="create"
trigger={
<Button size="small" type="link">
{t("workflow_node.deploy.form.provider_access.button")}
<IconPlus size="1.25em" />
</Button>
}
usage="hosting"
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (provider?.usages?.includes(ACCESS_USAGES.HOSTING)) {
formInst.setFieldValue("providerAccessId", record.id);
}
}}
/>
</div>
</div>
</label>
<Form.Item name="providerAccessId" rules={[formRule]}>
<AccessSelect
placeholder={t("workflow_node.deploy.form.provider_access.placeholder")}
showSearch
onFilter={(_, option) => {
if (option.reserve) return false;
if (fieldProvider) return deploymentProvidersMap.get(fieldProvider)?.provider === option.provider;
const provider = accessProvidersMap.get(option.provider);
return !!provider?.usages?.includes(ACCESS_USAGES.HOSTING);
}}
/>
</Form.Item>
</Form.Item>
<Form.Item
name="certificate"
label={t("workflow_node.deploy.form.certificate.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.certificate.tooltip") }}></span>}
>
<Select
labelRender={({ label, value }) => {
if (value != null) {
const group = certificateCandidates.find((group) => group.options.some((option) => option.value === value));
return `${group?.label} - ${label}`;
}
return <span style={{ color: themeToken.colorTextPlaceholder }}>{t("workflow_node.deploy.form.certificate.placeholder")}</span>;
}}
options={certificateCandidates}
placeholder={t("workflow_node.deploy.form.certificate.placeholder")}
/>
</Form.Item>
</div>
<div id="deployment" data-anchor="deployment">
<FormNestedFieldsContextProvider value={{ parentNamePath: "providerConfig" }}>
{NestedProviderConfigFields && (
<>
<Divider size="small">
<Typography.Text className="text-xs font-normal" type="secondary">
{t("workflow_node.deploy.form_anchor.deployment.title")}
</Typography.Text>
</Divider>
<NestedProviderConfigFields />
</>
)}
</FormNestedFieldsContextProvider>
</div>
<div id="strategy" data-anchor="strategy">
<Divider size="small">
<Typography.Text className="text-xs font-normal" type="secondary">
{t("workflow_node.deploy.form_anchor.strategy.title")}
</Typography.Text>
</Divider>
<Form.Item label={t("workflow_node.deploy.form.skip_on_last_succeeded.label")}>
<Flex align="center" gap={8} wrap="wrap">
<div>{t("workflow_node.deploy.form.skip_on_last_succeeded.prefix")}</div>
<Form.Item name="skipOnLastSucceeded" noStyle rules={[formRule]}>
<Switch
checkedChildren={t("workflow_node.deploy.form.skip_on_last_succeeded.switch.on")}
unCheckedChildren={t("workflow_node.deploy.form.skip_on_last_succeeded.switch.off")}
/>
</Form.Item>
<div>{t("workflow_node.deploy.form.skip_on_last_succeeded.suffix")}</div>
</Flex>
</Form.Item>
</div>
</div>
</Form>
</NodeFormContextProvider>
);
};
const getAnchorItems = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }): Required<AnchorProps>["items"] => {
const { t } = i18n;
return ["parameters", "deployment", "strategy"].map((key) => ({
key: key,
title: t(`workflow_node.deploy.form_anchor.${key}.tab`),
href: "#" + key,
}));
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
certificate: "",
provider: "",
...defaultNodeConfigForDeploy(),
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
certificate: z.string().nonempty(t("workflow_node.deploy.form.certificate.placeholder")),
provider: z.string().nonempty(t("workflow_node.deploy.form.provider.placeholder")),
providerAccessId: z.string().nullish(),
providerConfig: z.any().nullish(),
skipOnLastSucceeded: z.boolean().nullish(),
})
.superRefine((values, ctx) => {
if (values.provider) {
const provider = deploymentProvidersMap.get(values.provider);
if (!provider?.builtin && !values.providerAccessId) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.provider_access.placeholder"),
path: ["providerAccessId"],
});
}
}
});
};
const _default = Object.assign(BizDeployNodeConfigForm, {
getAnchorItems,
getSchema,
});
export default _default;
@@ -0,0 +1,51 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Switch } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProvider1PanelConsole = () => {
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, "autoRestart"]}
initialValue={initialValues.autoRestart}
label={t("workflow_node.deploy.form.1panel_console_auto_restart.label")}
rules={[formRule]}
>
<Switch />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
autoRestart: true,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({
autoRestart: z.boolean().nullish(),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProvider1PanelConsole, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,133 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_WEBSITE = "website" as const;
const RESOURCE_TYPE_CERTIFICATE = "certificate" as const;
const BizDeployNodeConfigFormProvider1PanelSite = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "nodeName"]}
initialValue={initialValues.nodeName}
label={t("workflow_node.deploy.form.1panel_site_node_name.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.1panel_site_node_name.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.1panel_site_node_name.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.1panel_site_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.1panel_site_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_WEBSITE} value={RESOURCE_TYPE_WEBSITE}>
{t("workflow_node.deploy.form.1panel_site_resource_type.option.website.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_CERTIFICATE} value={RESOURCE_TYPE_CERTIFICATE}>
{t("workflow_node.deploy.form.1panel_site_resource_type.option.certificate.label")}
</Select.Option>
</Select>
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_WEBSITE}>
<Form.Item
name={[parentNamePath, "websiteId"]}
initialValue={initialValues.websiteId}
label={t("workflow_node.deploy.form.1panel_site_website_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.1panel_site_website_id.tooltip") }}></span>}
>
<Input type="number" placeholder={t("workflow_node.deploy.form.1panel_site_website_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_CERTIFICATE}>
<Form.Item
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.1panel_site_certificate_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.1panel_site_certificate_id.tooltip") }}></span>}
>
<Input type="number" placeholder={t("workflow_node.deploy.form.1panel_site_certificate_id.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
resourceType: RESOURCE_TYPE_WEBSITE,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
nodeName: z.string().nullish(),
resourceType: z.literal([RESOURCE_TYPE_WEBSITE, RESOURCE_TYPE_CERTIFICATE], t("workflow_node.deploy.form.1panel_site_resource_type.placeholder")),
websiteId: z.union([z.string(), z.number()]).nullish(),
certificateId: z.union([z.string(), z.number()]).nullish(),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_WEBSITE:
{
const res = z.preprocess((v) => Number(v), z.number().int().positive()).safeParse(values.websiteId);
if (!res.success) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.1panel_site_website_id.placeholder"),
path: ["websiteId"],
});
}
}
break;
case RESOURCE_TYPE_CERTIFICATE:
{
const res = z.preprocess((v) => Number(v), z.number().int().positive()).safeParse(values.certificateId);
if (!res.success) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.1panel_site_certificate_id.placeholder"),
path: ["websiteId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProvider1PanelSite, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,91 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_CERTIFICATE = "certificate" as const;
const BizDeployNodeConfigFormProviderAPISIX = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.apisix_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.apisix_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_CERTIFICATE} value={RESOURCE_TYPE_CERTIFICATE}>
{t("workflow_node.deploy.form.apisix_resource_type.option.certificate.label")}
</Select.Option>
</Select>
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_CERTIFICATE}>
<Form.Item
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.apisix_certificate_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.apisix_certificate_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.apisix_certificate_id.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
resourceType: RESOURCE_TYPE_CERTIFICATE,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
resourceType: z.literal(RESOURCE_TYPE_CERTIFICATE, t("workflow_node.deploy.form.apisix_resource_type.placeholder")),
certificateId: z.string().nullish(),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_CERTIFICATE:
{
if (!values.certificateId?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.apisix_certificate_id.placeholder"),
path: ["certificateId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAPISIX, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,63 @@
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 BizDeployNodeConfigFormProviderAWSACM = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aws_acm_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_acm_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aws_acm_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "certificateArn"]}
initialValue={initialValues.certificateArn}
label={t("workflow_node.deploy.form.aws_acm_certificate_arn.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_acm_certificate_arn.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.aws_acm_certificate_arn.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aws_acm_region.placeholder")),
certificateArn: z.string().nullish(),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAWSACM, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,80 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderAWSCloudFront = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aws_cloudfront_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_cloudfront_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aws_cloudfront_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "distributionId"]}
initialValue={initialValues.distributionId}
label={t("workflow_node.deploy.form.aws_cloudfront_distribution_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_cloudfront_distribution_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "certificateSource"]}
initialValue={initialValues.certificateSource}
label={t("workflow_node.deploy.form.aws_cloudfront_certificate_source.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder")}>
<Select.Option key="ACM" value="ACM">
ACM
</Select.Option>
<Select.Option key="IAM" value="IAM">
IAM
</Select.Option>
</Select>
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
certificateSource: "ACM",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aws_cloudfront_region.placeholder")),
distributionId: z.string().nonempty(t("workflow_node.deploy.form.aws_cloudfront_distribution_id.placeholder")),
certificateSource: z.string().nonempty(t("workflow_node.deploy.form.aws_cloudfront_certificate_source.placeholder")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAWSCloudFront, {
getInitialValues,
getSchema,
});
export default _default;
@@ -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 BizDeployNodeConfigFormProviderAWSIAM = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aws_iam_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_iam_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aws_iam_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "certificatePath"]}
initialValue={initialValues.certificatePath}
label={t("workflow_node.deploy.form.aws_iam_certificate_path.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aws_iam_certificate_path.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.aws_iam_certificate_path.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
certificatePath: "/",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aws_iam_region.placeholder")),
certificatePath: z
.string()
.nullish()
.refine((v) => {
if (!v) return true;
return v.startsWith("/") && v.endsWith("/");
}, t("workflow_node.deploy.form.aws_iam_certificate_path.errmsg.invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAWSIAM, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,153 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
const RESOURCE_TYPE_LISTENER = "listener" as const;
const BizDeployNodeConfigFormProviderAliyunALB = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_alb_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_alb_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_alb_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.aliyun_alb_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.aliyun_alb_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_LOADBALANCER} value={RESOURCE_TYPE_LOADBALANCER}>
{t("workflow_node.deploy.form.aliyun_alb_resource_type.option.loadbalancer.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_LISTENER} value={RESOURCE_TYPE_LISTENER}>
{t("workflow_node.deploy.form.aliyun_alb_resource_type.option.listener.label")}
</Select.Option>
</Select>
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER}>
<Form.Item
name={[parentNamePath, "loadbalancerId"]}
initialValue={initialValues.loadbalancerId}
label={t("workflow_node.deploy.form.aliyun_alb_loadbalancer_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_alb_loadbalancer_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_alb_loadbalancer_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "listenerId"]}
initialValue={initialValues.listenerId}
label={t("workflow_node.deploy.form.aliyun_alb_listener_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_alb_listener_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_alb_listener_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER || fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_alb_snidomain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_alb_snidomain.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.aliyun_alb_snidomain.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
resourceType: RESOURCE_TYPE_LISTENER,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_alb_region.placeholder")),
resourceType: z.literal([RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER], t("workflow_node.deploy.form.aliyun_alb_resource_type.placeholder")),
loadbalancerId: z.string().nullish(),
listenerId: z.string().nullish(),
domain: z
.string()
.nullish()
.refine((v) => {
return !v || validDomainName(v!, { allowWildcard: true });
}, t("common.errmsg.domain_invalid")),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_LOADBALANCER:
{
const res = z.string().nonempty().safeParse(values.loadbalancerId);
if (!res.success) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.aliyun_alb_loadbalancer_id.placeholder"),
path: ["loadbalancerId"],
});
}
}
break;
case RESOURCE_TYPE_LISTENER:
{
const res = z.string().nonempty().safeParse(values.listenerId);
if (!res.success) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.aliyun_alb_listener_id.placeholder"),
path: ["listenerId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunALB, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,146 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const SERVICE_TYPE_CLOUDNATIVE = "cloudnative" as const;
const SERVICE_TYPE_TRADITIONAL = "traditional" as const;
const BizDeployNodeConfigFormProviderAliyunAPIGW = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldServiceType = Form.useWatch([parentNamePath, "serviceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "serviceType"]}
initialValue={initialValues.serviceType}
label={t("workflow_node.deploy.form.aliyun_apigw_service_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.aliyun_apigw_service_type.placeholder")}>
<Select.Option key={SERVICE_TYPE_CLOUDNATIVE} value={SERVICE_TYPE_CLOUDNATIVE}>
{t("workflow_node.deploy.form.aliyun_apigw_service_type.option.cloudnative.label")}
</Select.Option>
<Select.Option key={SERVICE_TYPE_TRADITIONAL} value={SERVICE_TYPE_TRADITIONAL}>
{t("workflow_node.deploy.form.aliyun_apigw_service_type.option.traditional.label")}
</Select.Option>
</Select>
</Form.Item>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_apigw_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_apigw_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_apigw_region.placeholder")} />
</Form.Item>
<Show when={fieldServiceType === SERVICE_TYPE_CLOUDNATIVE}>
<Form.Item
name={[parentNamePath, "gatewayId"]}
initialValue={initialValues.gatewayId}
label={t("workflow_node.deploy.form.aliyun_apigw_gateway_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_apigw_gateway_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_apigw_gateway_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldServiceType === SERVICE_TYPE_TRADITIONAL}>
<Form.Item
name={[parentNamePath, "groupId"]}
initialValue={initialValues.groupId}
label={t("workflow_node.deploy.form.aliyun_apigw_group_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_apigw_group_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_apigw_group_id.placeholder")} />
</Form.Item>
</Show>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_apigw_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_apigw_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_apigw_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
serviceType: z.literal([SERVICE_TYPE_CLOUDNATIVE, SERVICE_TYPE_TRADITIONAL], t("workflow_node.deploy.form.aliyun_apigw_service_type.placeholder")),
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_apigw_region.placeholder")),
gatewayId: z.string().nullish(),
groupId: z.string().nullish(),
domain: z
.string()
.nonempty(t("workflow_node.deploy.form.aliyun_apigw_domain.placeholder"))
.refine((v) => validDomainName(v!, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
})
.superRefine((values, ctx) => {
switch (values.serviceType) {
case SERVICE_TYPE_CLOUDNATIVE:
{
if (!values.gatewayId?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.aliyun_apigw_gateway_id.placeholder"),
path: ["gatewayId"],
});
}
}
break;
case SERVICE_TYPE_TRADITIONAL:
{
if (!values.groupId?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.aliyun_apigw_group_id.placeholder"),
path: ["groupId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunAPIGW, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,52 @@
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 BizDeployNodeConfigFormProviderAliyunCAS = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_cas_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_cas_region.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_cas_region.placeholder")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunCAS, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,108 @@
import { getI18n, useTranslation } from "react-i18next";
import { Alert, Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
import { useFormNestedFieldsContext } from "./_context";
const MULTIPLE_INPUT_SEPARATOR = ";";
const BizDeployNodeConfigFormProviderAliyunCASDeploy = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const initialValues = getInitialValues();
return (
<>
<Form.Item>
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy.guide") }}></span>} />
</Form.Item>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_cas_deploy_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceIds"]}
initialValue={initialValues.resourceIds}
label={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.tooltip") }}></span>}
>
<MultipleSplitValueInput
modalTitle={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.multiple_input_modal.placeholder")}
separator={MULTIPLE_INPUT_SEPARATOR}
splitOptions={{ removeEmpty: true, trimSpace: true }}
/>
</Form.Item>
<Form.Item
name={[parentNamePath, "contactIds"]}
initialValue={initialValues.contactIds}
label={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.tooltip") }}></span>}
>
<MultipleSplitValueInput
modalTitle={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.placeholder")}
separator={MULTIPLE_INPUT_SEPARATOR}
splitOptions={{ removeEmpty: true, trimSpace: true }}
/>
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
resourceIds: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_cas_deploy_region.placeholder")),
resourceIds: z.string().refine((v) => {
if (!v) return false;
return String(v)
.split(MULTIPLE_INPUT_SEPARATOR)
.every((e) => /^[1-9]\d*$/.test(e));
}, t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.errmsg.invalid")),
contactIds: z
.string()
.nullish()
.refine((v) => {
if (!v) return true;
return String(v)
.split(MULTIPLE_INPUT_SEPARATOR)
.every((e) => /^[1-9]\d*$/.test(e));
}, t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.errmsg.invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunCASDeploy, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,54 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderAliyunCDN = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_cdn_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cdn_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_cdn_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,139 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { validDomainName, validPortNumber } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
const RESOURCE_TYPE_LISTENER = "listener" as const;
const BizDeployNodeConfigFormProviderAliyunCLB = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_clb_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_clb_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_clb_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.aliyun_clb_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.aliyun_clb_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_LOADBALANCER} value={RESOURCE_TYPE_LOADBALANCER}>
{t("workflow_node.deploy.form.aliyun_clb_resource_type.option.loadbalancer.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_LISTENER} value={RESOURCE_TYPE_LISTENER}>
{t("workflow_node.deploy.form.aliyun_clb_resource_type.option.listener.label")}
</Select.Option>
</Select>
</Form.Item>
<Form.Item
name={[parentNamePath, "loadbalancerId"]}
initialValue={initialValues.loadbalancerId}
label={t("workflow_node.deploy.form.aliyun_clb_loadbalancer_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_clb_loadbalancer_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_clb_loadbalancer_id.placeholder")} />
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "listenerPort"]}
initialValue={initialValues.listenerPort}
label={t("workflow_node.deploy.form.aliyun_clb_listener_port.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_clb_listener_port.tooltip") }}></span>}
>
<Input type="number" min={1} max={65535} placeholder={t("workflow_node.deploy.form.aliyun_clb_listener_port.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER || fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_clb_snidomain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_clb_snidomain.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.aliyun_clb_snidomain.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
resourceType: RESOURCE_TYPE_LISTENER,
loadbalancerId: "",
listenerPort: 443,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_clb_region.placeholder")),
resourceType: z.literal([RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER], t("workflow_node.deploy.form.aliyun_clb_resource_type.placeholder")),
loadbalancerId: z.string().nonempty(t("workflow_node.deploy.form.aliyun_clb_loadbalancer_id.placeholder")),
listenerPort: z.preprocess((v) => (v == null || v === "" ? undefined : Number(v)), z.number().nullish()),
domain: z
.string()
.nullish()
.refine((v) => {
return !v || validDomainName(v!, { allowWildcard: true });
}, t("common.errmsg.domain_invalid")),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_LISTENER:
{
if (!validPortNumber(values.listenerPort!)) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.aliyun_clb_listener_port.placeholder"),
path: ["listenerPort"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunCLB, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,54 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderAliyunDCDN = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_dcdn_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_dcdn_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_dcdn_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunDCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,66 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderAliyunDDoS = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_ddos_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_ddos_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_ddos_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_ddos_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_ddos_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_ddos_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_ddos_region.placeholder")),
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunDDoS, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,66 @@
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 BizDeployNodeConfigFormProviderAliyunESA = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_esa_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_esa_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_esa_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "siteId"]}
initialValue={initialValues.siteId}
label={t("workflow_node.deploy.form.aliyun_esa_site_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_esa_site_id.tooltip") }}></span>}
>
<Input type="number" placeholder={t("workflow_node.deploy.form.aliyun_esa_site_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
siteId: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_esa_region.placeholder")),
siteId: z.union([z.string(), z.number().int()]).refine((v) => {
return /^\d+$/.test(v + "") && +v > 0;
}, t("workflow_node.deploy.form.aliyun_esa_site_id.placeholder")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunESA, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,84 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderAliyunFC = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_fc_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_fc_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_fc_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "serviceVersion"]}
initialValue={initialValues.serviceVersion}
label={t("workflow_node.deploy.form.aliyun_fc_service_version.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.aliyun_fc_service_version.placeholder")}>
<Select.Option key="2.0" value="2.0">
2.0
</Select.Option>
<Select.Option key="3.0" value="3.0">
3.0
</Select.Option>
</Select>
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_fc_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_fc_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_fc_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
serviceVersion: "3.0",
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_fc_region.placeholder")),
serviceVersion: z.literal(["2.0", "3.0"], t("workflow_node.deploy.form.aliyun_fc_service_version.placeholder")),
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunFC, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,126 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_ACCELERATOR = "accelerator" as const;
const RESOURCE_TYPE_LISTENER = "listener" as const;
const BizDeployNodeConfigFormProviderAliyunGA = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.aliyun_ga_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.aliyun_ga_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_ACCELERATOR} value={RESOURCE_TYPE_ACCELERATOR}>
{t("workflow_node.deploy.form.aliyun_ga_resource_type.option.accelerator.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_LISTENER} value={RESOURCE_TYPE_LISTENER}>
{t("workflow_node.deploy.form.aliyun_ga_resource_type.option.listener.label")}
</Select.Option>
</Select>
</Form.Item>
<Form.Item
name={[parentNamePath, "acceleratorId"]}
initialValue={initialValues.acceleratorId}
label={t("workflow_node.deploy.form.aliyun_ga_accelerator_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_ga_accelerator_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_ga_accelerator_id.placeholder")} />
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "listenerId"]}
initialValue={initialValues.listenerId}
label={t("workflow_node.deploy.form.aliyun_ga_listener_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_ga_listener_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_ga_listener_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_ACCELERATOR || fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_ga_snidomain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_ga_snidomain.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.aliyun_ga_snidomain.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
resourceType: RESOURCE_TYPE_LISTENER,
acceleratorId: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
resourceType: z.literal([RESOURCE_TYPE_ACCELERATOR, RESOURCE_TYPE_LISTENER], t("workflow_node.deploy.form.aliyun_ga_resource_type.placeholder")),
acceleratorId: z.string().nonempty(t("workflow_node.deploy.form.aliyun_ga_accelerator_id.placeholder")),
listenerId: z.string().nullish(),
domain: z
.string()
.nullish()
.refine((v) => {
return !v || validDomainName(v!);
}, t("common.errmsg.domain_invalid")),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_LISTENER:
{
if (!values.listenerId?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.aliyun_ga_listener_id.placeholder"),
path: ["listenerId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunGA, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,66 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderAliyunLive = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_live_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_live_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_live_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_live_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_live_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_live_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_live_region.placeholder")),
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunLive, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,132 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
const RESOURCE_TYPE_LISTENER = "listener" as const;
const BizDeployNodeConfigFormProviderAliyunNLB = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_nlb_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_nlb_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_nlb_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.aliyun_nlb_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.aliyun_nlb_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_LOADBALANCER} value={RESOURCE_TYPE_LOADBALANCER}>
{t("workflow_node.deploy.form.aliyun_nlb_resource_type.option.loadbalancer.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_LISTENER} value={RESOURCE_TYPE_LISTENER}>
{t("workflow_node.deploy.form.aliyun_nlb_resource_type.option.listener.label")}
</Select.Option>
</Select>
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER}>
<Form.Item
name={[parentNamePath, "loadbalancerId"]}
initialValue={initialValues.loadbalancerId}
label={t("workflow_node.deploy.form.aliyun_nlb_loadbalancer_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_nlb_loadbalancer_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_nlb_loadbalancer_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "listenerId"]}
initialValue={initialValues.listenerId}
label={t("workflow_node.deploy.form.aliyun_nlb_listener_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_nlb_listener_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_nlb_listener_id.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
resourceType: RESOURCE_TYPE_LISTENER,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_nlb_region.placeholder")),
resourceType: z.literal([RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER], t("workflow_node.deploy.form.aliyun_nlb_resource_type.placeholder")),
loadbalancerId: z.string().nullish(),
listenerId: z.string().nullish(),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_LOADBALANCER:
{
if (!values.loadbalancerId?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.aliyun_nlb_loadbalancer_id.placeholder"),
path: ["loadbalancerId"],
});
}
}
break;
case RESOURCE_TYPE_LISTENER:
{
if (!values.listenerId?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.aliyun_nlb_listener_id.placeholder"),
path: ["listenerId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunNLB, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,78 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderAliyunOSS = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_oss_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_oss_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_oss_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "bucket"]}
initialValue={initialValues.bucket}
label={t("workflow_node.deploy.form.aliyun_oss_bucket.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_oss_bucket.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_oss_bucket.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_oss_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_oss_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_oss_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
bucket: "",
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_oss_region.placeholder")),
bucket: z.string().nonempty(t("workflow_node.deploy.form.aliyun_oss_bucket.placeholder")),
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunOSS, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,66 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderAliyunVOD = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_vod_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_vod_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_vod_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_vod_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_vod_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_vod_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_vod_region.placeholder")),
domain: z.string().refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunVOD, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,97 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderAliyunWAF = () => {
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, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.aliyun_waf_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_waf_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_waf_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "serviceVersion"]}
initialValue={initialValues.serviceVersion}
label={t("workflow_node.deploy.form.aliyun_waf_service_version.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.aliyun_waf_service_version.placeholder")}>
<Select.Option key="3.0" value="3.0">
3.0
</Select.Option>
</Select>
</Form.Item>
<Form.Item
name={[parentNamePath, "instanceId"]}
initialValue={initialValues.instanceId}
label={t("workflow_node.deploy.form.aliyun_waf_instance_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_waf_instance_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_waf_instance_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_waf_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_waf_domain.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.aliyun_waf_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
serviceVersion: "3.0",
instanceId: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_waf_region.placeholder")),
serviceVersion: z.literal("3.0", t("workflow_node.deploy.form.aliyun_waf_service_version.placeholder")),
instanceId: z.string().nonempty(t("workflow_node.deploy.form.aliyun_waf_instance_id.placeholder")),
domain: z
.string()
.nullish()
.refine((v) => {
return !v || validDomainName(v!, { allowWildcard: true });
}, t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAliyunWAF, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,69 @@
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 BizDeployNodeConfigFormProviderAzureKeyVault = () => {
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, "keyvaultName"]}
initialValue={initialValues.keyvaultName}
label={t("workflow_node.deploy.form.azure_keyvault_name.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.azure_keyvault_name.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.azure_keyvault_name.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "certificateName"]}
initialValue={initialValues.certificateName}
label={t("workflow_node.deploy.form.azure_keyvault_certificate_name.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.azure_keyvault_certificate_name.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
keyvaultName: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
keyvaultName: z.string().nonempty(t("workflow_node.deploy.form.azure_keyvault_name.placeholder")),
certificateName: z
.string()
.nullish()
.refine((v) => {
if (!v) return true;
return /^[a-zA-Z0-9-]{1,127}$/.test(v);
}, t("workflow_node.deploy.form.azure_keyvault_certificate_name.errmsg.invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderAzureKeyVault, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,139 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { validDomainName, validPortNumber } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
const RESOURCE_TYPE_LISTENER = "listener" as const;
const BizDeployNodeConfigFormProviderBaiduCloudAppBLB = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.baiducloud_appblb_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_appblb_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.baiducloud_appblb_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.baiducloud_appblb_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.baiducloud_appblb_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_LOADBALANCER} value={RESOURCE_TYPE_LOADBALANCER}>
{t("workflow_node.deploy.form.baiducloud_appblb_resource_type.option.loadbalancer.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_LISTENER} value={RESOURCE_TYPE_LISTENER}>
{t("workflow_node.deploy.form.baiducloud_appblb_resource_type.option.listener.label")}
</Select.Option>
</Select>
</Form.Item>
<Form.Item
name={[parentNamePath, "loadbalancerId"]}
initialValue={initialValues.loadbalancerId}
label={t("workflow_node.deploy.form.baiducloud_appblb_loadbalancer_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_appblb_loadbalancer_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.baiducloud_appblb_loadbalancer_id.placeholder")} />
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "listenerPort"]}
initialValue={initialValues.listenerPort}
label={t("workflow_node.deploy.form.baiducloud_appblb_listener_port.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_appblb_listener_port.tooltip") }}></span>}
>
<Input type="number" min={1} max={65535} placeholder={t("workflow_node.deploy.form.baiducloud_appblb_listener_port.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER || fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.baiducloud_appblb_snidomain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_appblb_snidomain.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.baiducloud_appblb_snidomain.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
resourceType: RESOURCE_TYPE_LISTENER,
loadbalancerId: "",
listenerPort: 443,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
region: z.string().nonempty(t("workflow_node.deploy.form.baiducloud_appblb_region.placeholder")),
resourceType: z.literal([RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER], t("workflow_node.deploy.form.baiducloud_appblb_resource_type.placeholder")),
loadbalancerId: z.string().nonempty(t("workflow_node.deploy.form.baiducloud_appblb_loadbalancer_id.placeholder")),
listenerPort: z.preprocess((v) => (v == null || v === "" ? undefined : Number(v)), z.number().nullish()),
domain: z
.string()
.nullish()
.refine((v) => {
return !v || validDomainName(v!, { allowWildcard: true });
}, t("common.errmsg.domain_invalid")),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_LISTENER:
{
if (!validPortNumber(values.listenerPort!)) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.baiducloud_appblb_listener_port.placeholder"),
path: ["listenerPort"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderBaiduCloudAppBLB, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,139 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { validDomainName, validPortNumber } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
const RESOURCE_TYPE_LISTENER = "listener" as const;
const BizDeployNodeConfigFormProviderBaiduCloudBLB = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.baiducloud_blb_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_blb_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.baiducloud_blb_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.baiducloud_blb_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.baiducloud_blb_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_LOADBALANCER} value={RESOURCE_TYPE_LOADBALANCER}>
{t("workflow_node.deploy.form.baiducloud_blb_resource_type.option.loadbalancer.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_LISTENER} value={RESOURCE_TYPE_LISTENER}>
{t("workflow_node.deploy.form.baiducloud_blb_resource_type.option.listener.label")}
</Select.Option>
</Select>
</Form.Item>
<Form.Item
name={[parentNamePath, "loadbalancerId"]}
initialValue={initialValues.loadbalancerId}
label={t("workflow_node.deploy.form.baiducloud_blb_loadbalancer_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_blb_loadbalancer_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.baiducloud_blb_loadbalancer_id.placeholder")} />
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "listenerPort"]}
initialValue={initialValues.listenerPort}
label={t("workflow_node.deploy.form.baiducloud_blb_listener_port.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_blb_listener_port.tooltip") }}></span>}
>
<Input type="number" min={1} max={65535} placeholder={t("workflow_node.deploy.form.baiducloud_blb_listener_port.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER || fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.baiducloud_blb_snidomain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_blb_snidomain.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.baiducloud_blb_snidomain.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
resourceType: RESOURCE_TYPE_LISTENER,
loadbalancerId: "",
listenerPort: 443,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
region: z.string().nonempty(t("workflow_node.deploy.form.baiducloud_blb_region.placeholder")),
resourceType: z.literal([RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER], t("workflow_node.deploy.form.baiducloud_blb_resource_type.placeholder")),
loadbalancerId: z.string().nonempty(t("workflow_node.deploy.form.baiducloud_blb_loadbalancer_id.placeholder")),
listenerPort: z.preprocess((v) => (v == null || v === "" ? undefined : Number(v)), z.number().nullish()),
domain: z
.string()
.nullish()
.refine((v) => {
return !v || validDomainName(v!, { allowWildcard: true });
}, t("common.errmsg.domain_invalid")),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_LISTENER:
{
if (!validPortNumber(values.listenerPort!)) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.baiducloud_blb_listener_port.placeholder"),
path: ["listenerPort"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderBaiduCloudBLB, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,54 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderBaiduCloudCDN = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.baiducloud_cdn_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baiducloud_cdn_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.baiducloud_cdn_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderBaiduCloudCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,71 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderBaishanCDN = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.baishan_cdn_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baishan_cdn_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.baishan_cdn_domain.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.baishan_cdn_certificate_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baishan_cdn_certificate_id.tooltip") }}></span>}
>
<Input allowClear type="number" placeholder={t("workflow_node.deploy.form.baishan_cdn_certificate_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
certificateId: z
.union([z.string(), z.number().int()])
.nullish()
.refine((v) => {
if (!v) return true;
return /^\d+$/.test(v + "") && +v > 0;
}, t("workflow_node.deploy.form.baishan_cdn_certificate_id.placeholder")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderBaishanCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,51 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Switch } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderBaotaPanelConsole = () => {
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, "autoRestart"]}
initialValue={initialValues.autoRestart}
label={t("workflow_node.deploy.form.baotapanel_console_auto_restart.label")}
rules={[formRule]}
>
<Switch />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
autoRestart: true,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({
autoRestart: z.boolean().nullish(),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderBaotaPanelConsole, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,129 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
import Show from "@/components/Show";
import { useFormNestedFieldsContext } from "./_context";
const SITE_TYPE_PHP = "php";
const SITE_TYPE_OTHER = "other";
const MULTIPLE_INPUT_SEPARATOR = ";";
const BizDeployNodeConfigFormProviderBaotaPanelSite = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldSiteType = Form.useWatch([parentNamePath, "siteType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "siteType"]}
initialValue={initialValues.siteType}
label={t("workflow_node.deploy.form.baotapanel_site_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.baotapanel_site_type.placeholder")}>
<Select.Option key={SITE_TYPE_PHP} value={SITE_TYPE_PHP}>
{t("workflow_node.deploy.form.baotapanel_site_type.option.php.label")}
</Select.Option>
<Select.Option key={SITE_TYPE_OTHER} value={SITE_TYPE_OTHER}>
{t("workflow_node.deploy.form.baotapanel_site_type.option.other.label")}
</Select.Option>
</Select>
</Form.Item>
<Show when={fieldSiteType === SITE_TYPE_PHP}>
<Form.Item
name={[parentNamePath, "siteName"]}
initialValue={initialValues.siteName}
label={t("workflow_node.deploy.form.baotapanel_site_name.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baotapanel_site_name.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.baotapanel_site_name.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldSiteType === SITE_TYPE_OTHER}>
<Form.Item
name={[parentNamePath, "siteNames"]}
initialValue={initialValues.siteNames}
label={t("workflow_node.deploy.form.baotapanel_site_names.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baotapanel_site_names.tooltip") }}></span>}
>
<MultipleSplitValueInput
modalTitle={t("workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.baotapanel_site_names.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder")}
separator={MULTIPLE_INPUT_SEPARATOR}
splitOptions={{ removeEmpty: true, trimSpace: true }}
/>
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
siteType: SITE_TYPE_OTHER,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
siteType: z.literal([SITE_TYPE_PHP, SITE_TYPE_OTHER], t("workflow_node.deploy.form.baotapanel_site_type.placeholder")),
siteName: z.string().nullish(),
siteNames: z.string().nullish(),
})
.superRefine((values, ctx) => {
switch (values.siteType) {
case SITE_TYPE_PHP:
{
if (!values.siteName?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.baotapanel_site_name.placeholder"),
path: ["siteName"],
});
}
}
break;
case SITE_TYPE_OTHER:
{
if (!values.siteNames?.trim() || !values.siteNames.split(MULTIPLE_INPUT_SEPARATOR).every((e) => !!e.trim())) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.baotapanel_site_names.placeholder"),
path: ["siteNames"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderBaotaPanelSite, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,71 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, InputNumber } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validPortNumber } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderBaotaWAFSite = () => {
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, "siteName"]}
initialValue={initialValues.siteName}
label={t("workflow_node.deploy.form.baotawaf_site_name.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baotawaf_site_name.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.baotawaf_site_name.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "sitePort"]}
initialValue={initialValues.sitePort}
label={t("workflow_node.deploy.form.baotawaf_site_port.label")}
rules={[formRule]}
>
<InputNumber style={{ width: "100%" }} placeholder={t("access.form.ssh_port.placeholder")} min={1} max={65535} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
siteName: "",
sitePort: 443,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
siteName: z.string().nonempty(t("workflow_node.deploy.form.baotawaf_site_name.placeholder")),
sitePort: z.preprocess(
(v) => Number(v),
z
.number()
.int(t("workflow_node.deploy.form.baotawaf_site_port.placeholder"))
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderBaotaWAFSite, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,72 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderBunnyCDN = () => {
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, "pullZoneId"]}
initialValue={initialValues.pullZoneId}
label={t("workflow_node.deploy.form.bunny_cdn_pull_zone_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.bunny_cdn_pull_zone_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.bunny_cdn_pull_zone_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "hostname"]}
initialValue={initialValues.hostname}
label={t("workflow_node.deploy.form.bunny_cdn_hostname.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.bunny_cdn_hostname.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.bunny_cdn_hostname.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
pullZoneId: "",
hostname: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
pullZoneId: z.union([z.string(), z.number().int()]).refine((v) => {
return /^\d+$/.test(v + "") && +v! > 0;
}, t("workflow_node.deploy.form.bunny_cdn_pull_zone_id.placeholder")),
hostname: z
.string()
.nonempty(t("workflow_node.deploy.form.bunny_cdn_hostname.placeholder"))
.refine((v) => {
return validDomainName(v!, { allowWildcard: true });
}, t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderBunnyCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,54 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderBytePlusCDN = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.byteplus_cdn_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.byteplus_cdn_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.byteplus_cdn_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderBytePlusCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,54 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderCTCCCloudAO = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.ctcccloud_ao_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_ao_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_ao_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderCTCCCloudAO, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,54 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderCTCCCloudCDN = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.ctcccloud_cdn_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_cdn_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_cdn_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderCTCCCloudCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,132 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
const RESOURCE_TYPE_LISTENER = "listener" as const;
const BizDeployNodeConfigFormProviderCTCCCloudELB = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "regionId"]}
initialValue={initialValues.regionId}
label={t("workflow_node.deploy.form.ctcccloud_elb_region_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_elb_region_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.ctcccloud_elb_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.ctcccloud_elb_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_LOADBALANCER} value={RESOURCE_TYPE_LOADBALANCER}>
{t("workflow_node.deploy.form.ctcccloud_elb_resource_type.option.loadbalancer.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_LISTENER} value={RESOURCE_TYPE_LISTENER}>
{t("workflow_node.deploy.form.ctcccloud_elb_resource_type.option.listener.label")}
</Select.Option>
</Select>
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER}>
<Form.Item
name={[parentNamePath, "loadbalancerId"]}
initialValue={initialValues.loadbalancerId}
label={t("workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "listenerId"]}
initialValue={initialValues.listenerId}
label={t("workflow_node.deploy.form.ctcccloud_elb_listener_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_elb_listener_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_elb_listener_id.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
regionId: "",
resourceType: RESOURCE_TYPE_LISTENER,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
regionId: z.string().nonempty(t("workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder")),
resourceType: z.literal([RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER], t("workflow_node.deploy.form.ctcccloud_elb_resource_type.placeholder")),
loadbalancerId: z.string().nullish(),
listenerId: z.string().nullish(),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_LOADBALANCER:
{
if (!values.loadbalancerId?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.placeholder"),
path: ["loadbalancerId"],
});
}
}
break;
case RESOURCE_TYPE_LISTENER:
{
if (!values.listenerId?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.ctcccloud_elb_listener_id.placeholder"),
path: ["listenerId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderCTCCCloudELB, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,54 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderCTCCCloudICDN = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.ctcccloud_icdn_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_icdn_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_icdn_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderCTCCCloudICDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,54 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderCTCCCloudLVDN = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.ctcccloud_lvdn_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_lvdn_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_lvdn_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderCTCCCloudLVDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,122 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_SITE = "site" as const;
const RESOURCE_TYPE_CERTIFICATE = "certificate" as const;
const BizDeployNodeConfigFormProviderCdnfly = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.cdnfly_resource_type.label")}
rules={[formRule]}
>
<Select placeholder={t("workflow_node.deploy.form.cdnfly_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_SITE} value={RESOURCE_TYPE_SITE}>
{t("workflow_node.deploy.form.cdnfly_resource_type.option.site.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_CERTIFICATE} value={RESOURCE_TYPE_CERTIFICATE}>
{t("workflow_node.deploy.form.cdnfly_resource_type.option.certificate.label")}
</Select.Option>
</Select>
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_SITE}>
<Form.Item
name={[parentNamePath, "siteId"]}
initialValue={initialValues.siteId}
label={t("workflow_node.deploy.form.cdnfly_site_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.cdnfly_site_id.tooltip") }}></span>}
>
<Input type="number" placeholder={t("workflow_node.deploy.form.cdnfly_site_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_CERTIFICATE}>
<Form.Item
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.cdnfly_certificate_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.cdnfly_certificate_id.tooltip") }}></span>}
>
<Input type="number" placeholder={t("workflow_node.deploy.form.cdnfly_certificate_id.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
resourceType: RESOURCE_TYPE_SITE,
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
resourceType: z.literal([RESOURCE_TYPE_SITE, RESOURCE_TYPE_CERTIFICATE], t("workflow_node.deploy.form.cdnfly_resource_type.placeholder")),
siteId: z.union([z.string(), z.number().int()]).nullish(),
certificateId: z.union([z.string(), z.number().int()]).nullish(),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_SITE:
{
const res = z.preprocess((v) => Number(v), z.number().int().positive()).safeParse(values.siteId);
if (!res.success) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.cdnfly_site_id.placeholder"),
path: ["siteId"],
});
}
}
break;
case RESOURCE_TYPE_CERTIFICATE:
{
const res = z.preprocess((v) => Number(v), z.number().int().positive()).safeParse(values.certificateId);
if (!res.success) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.cdnfly_certificate_id.placeholder"),
path: ["certificateId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderCdnfly, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,54 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const BizDeployNodeConfigFormProviderDogeCloudCDN = () => {
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, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.dogecloud_cdn_domain.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.dogecloud_cdn_domain.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.dogecloud_cdn_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
domain: z.string().refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderDogeCloudCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -0,0 +1,52 @@
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 BizDeployNodeConfigFormProviderEdgioApplications = () => {
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, "environmentId"]}
initialValue={initialValues.environmentId}
label={t("workflow_node.deploy.form.edgio_applications_environment_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.edgio_applications_environment_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.edgio_applications_environment_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
environmentId: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z.object({
environmentId: z.string().nonempty(t("workflow_node.deploy.form.edgio_applications_environment_id.placeholder")),
});
};
const _default = Object.assign(BizDeployNodeConfigFormProviderEdgioApplications, {
getInitialValues,
getSchema,
});
export default _default;

Some files were not shown because too many files have changed in this diff Show More