@gadmin2n/schematics 0.0.140 → 0.0.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/gen-jwt.ts +8 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +32 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +39 -33
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.spec.ts +34 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +26 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +149 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.ts +20 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.spec.ts +85 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.ts +157 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.spec.ts +59 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +15 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +125 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +109 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/PageHeader.tsx +46 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.test.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +6 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/constants/layout.ts +30 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +20 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +20 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +9 -20
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/list.tsx +141 -138
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +8 -19
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/human-approval/inbox.tsx +12 -19
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +47 -150
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/permissionReadme/index.tsx +13 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +110 -113
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +61 -64
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +187 -200
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/index.tsx +46 -44
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NewNodeDefaults.test.tsx +6 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.tsx +50 -41
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +9 -22
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instances.tsx +4 -8
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/index.tsx +9 -22
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.test.tsx +42 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.tsx +77 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.test.tsx +29 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.tsx +245 -55
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.test.tsx +152 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +417 -161
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/newNodeConfig.ts +5 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/index.css +20 -0
- package/package.json +1 -1
|
@@ -1,20 +1,37 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ArrowLeftOutlined,
|
|
3
|
+
CheckCircleFilled,
|
|
4
|
+
EditOutlined,
|
|
5
|
+
EyeInvisibleOutlined,
|
|
6
|
+
EyeOutlined,
|
|
7
|
+
PlusOutlined,
|
|
8
|
+
ReloadOutlined,
|
|
9
|
+
} from '@ant-design/icons';
|
|
2
10
|
import { useGetIdentity } from '@refinedev/core';
|
|
3
11
|
import {
|
|
4
12
|
Alert,
|
|
5
13
|
Button,
|
|
6
14
|
Card,
|
|
15
|
+
Descriptions,
|
|
16
|
+
Divider,
|
|
17
|
+
Drawer,
|
|
18
|
+
Empty,
|
|
7
19
|
Form,
|
|
8
20
|
Input,
|
|
21
|
+
Result,
|
|
22
|
+
Space,
|
|
9
23
|
Spin,
|
|
24
|
+
Tag,
|
|
10
25
|
Typography,
|
|
11
26
|
message,
|
|
27
|
+
theme,
|
|
12
28
|
} from 'antd';
|
|
13
29
|
import { customRequest } from 'helpers/http';
|
|
14
|
-
import {
|
|
30
|
+
import { HEADER_CARD_PROPS, PAGE_WRAPPER_STYLE } from 'constants/layout';
|
|
31
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
15
32
|
import { useNavigate } from 'react-router-dom';
|
|
16
33
|
|
|
17
|
-
const { Title } = Typography;
|
|
34
|
+
const { Paragraph, Text, Title } = Typography;
|
|
18
35
|
|
|
19
36
|
interface TenantConfig {
|
|
20
37
|
id: number;
|
|
@@ -23,84 +40,169 @@ interface TenantConfig {
|
|
|
23
40
|
namespace: string;
|
|
24
41
|
dbUrl: string;
|
|
25
42
|
displayName: string;
|
|
26
|
-
runtimeBaseUrl
|
|
27
|
-
runtimeToken
|
|
43
|
+
runtimeBaseUrl?: string | null;
|
|
44
|
+
runtimeToken?: string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface TenantConfigListResponse {
|
|
48
|
+
data: TenantConfig[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type TenantConfigFormValues = Omit<TenantConfig, 'id'>;
|
|
52
|
+
|
|
53
|
+
const configSelect = {
|
|
54
|
+
id: true,
|
|
55
|
+
adminUrl: true,
|
|
56
|
+
adminToken: true,
|
|
57
|
+
namespace: true,
|
|
58
|
+
dbUrl: true,
|
|
59
|
+
displayName: true,
|
|
60
|
+
runtimeBaseUrl: true,
|
|
61
|
+
runtimeToken: true,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
function maskSecret(value?: string | null) {
|
|
65
|
+
if (!value) return '未配置';
|
|
66
|
+
if (value.length <= 4) return '••••••••';
|
|
67
|
+
return `••••••••${value.slice(-4)}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function maskDatabaseUrl(value: string) {
|
|
71
|
+
return value.replace(/(\/\/[^:/?#]+:)[^@/?#]+(@)/, '$1••••••$2');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function SensitiveValue({
|
|
75
|
+
label,
|
|
76
|
+
value,
|
|
77
|
+
maskedValue,
|
|
78
|
+
}: {
|
|
79
|
+
label: string;
|
|
80
|
+
value?: string | null;
|
|
81
|
+
maskedValue?: string;
|
|
82
|
+
}) {
|
|
83
|
+
const [visible, setVisible] = useState(false);
|
|
84
|
+
|
|
85
|
+
if (!value) {
|
|
86
|
+
return <Text type="secondary">未配置</Text>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Space size={6} style={{ maxWidth: '100%' }}>
|
|
91
|
+
<Text
|
|
92
|
+
code
|
|
93
|
+
style={{
|
|
94
|
+
overflowWrap: 'anywhere',
|
|
95
|
+
whiteSpace: 'normal',
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
{visible ? value : (maskedValue ?? maskSecret(value))}
|
|
99
|
+
</Text>
|
|
100
|
+
<Button
|
|
101
|
+
type="text"
|
|
102
|
+
size="small"
|
|
103
|
+
aria-label={`${visible ? '隐藏' : '显示'} ${label}`}
|
|
104
|
+
icon={visible ? <EyeInvisibleOutlined /> : <EyeOutlined />}
|
|
105
|
+
onClick={() => setVisible((current) => !current)}
|
|
106
|
+
/>
|
|
107
|
+
</Space>
|
|
108
|
+
);
|
|
28
109
|
}
|
|
29
110
|
|
|
30
111
|
export default function TemporalTenantPage() {
|
|
31
112
|
const navigate = useNavigate();
|
|
32
|
-
const [form] = Form.useForm();
|
|
113
|
+
const [form] = Form.useForm<TenantConfigFormValues>();
|
|
114
|
+
const { token } = theme.useToken();
|
|
33
115
|
const { data: identity } = useGetIdentity<any>();
|
|
34
116
|
const isAdmin = (identity?.configPerms?.roles || []).includes('SYSTEM_ADMIN');
|
|
35
117
|
|
|
36
118
|
const [loading, setLoading] = useState(true);
|
|
37
119
|
const [saving, setSaving] = useState(false);
|
|
120
|
+
const [editorOpen, setEditorOpen] = useState(false);
|
|
38
121
|
const [existing, setExisting] = useState<TenantConfig | null>(null);
|
|
122
|
+
const [loadError, setLoadError] = useState(false);
|
|
39
123
|
|
|
40
|
-
|
|
41
|
-
customRequest(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
namespace: true,
|
|
49
|
-
dbUrl: true,
|
|
50
|
-
displayName: true,
|
|
51
|
-
runtimeBaseUrl: true,
|
|
52
|
-
runtimeToken: true,
|
|
124
|
+
const loadConfig = useCallback(async () => {
|
|
125
|
+
const res = await customRequest<TenantConfigListResponse>(
|
|
126
|
+
'temporalTenantConfig/findMany',
|
|
127
|
+
'POST',
|
|
128
|
+
{
|
|
129
|
+
take: 1,
|
|
130
|
+
skip: 0,
|
|
131
|
+
select: configSelect,
|
|
53
132
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
133
|
+
);
|
|
134
|
+
const item = res.data[0] ?? null;
|
|
135
|
+
setExisting(item);
|
|
136
|
+
setLoadError(false);
|
|
137
|
+
return item;
|
|
138
|
+
}, []);
|
|
139
|
+
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
loadConfig()
|
|
142
|
+
.catch(() => {
|
|
143
|
+
setLoadError(true);
|
|
144
|
+
message.error('加载配置失败');
|
|
59
145
|
})
|
|
60
|
-
.catch(() => message.error('加载配置失败'))
|
|
61
146
|
.finally(() => setLoading(false));
|
|
62
|
-
}, [
|
|
147
|
+
}, [loadConfig]);
|
|
148
|
+
|
|
149
|
+
const retryLoadConfig = async () => {
|
|
150
|
+
setLoading(true);
|
|
151
|
+
try {
|
|
152
|
+
await loadConfig();
|
|
153
|
+
} catch {
|
|
154
|
+
setLoadError(true);
|
|
155
|
+
message.error('加载配置失败');
|
|
156
|
+
} finally {
|
|
157
|
+
setLoading(false);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
63
160
|
|
|
64
|
-
const
|
|
161
|
+
const openEditor = () => {
|
|
162
|
+
form.resetFields();
|
|
163
|
+
if (existing) {
|
|
164
|
+
form.setFieldsValue(existing);
|
|
165
|
+
}
|
|
166
|
+
setEditorOpen(true);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const handleSave = async (values: TenantConfigFormValues) => {
|
|
65
170
|
setSaving(true);
|
|
66
171
|
try {
|
|
172
|
+
const payload = existing
|
|
173
|
+
? { ...values, namespace: existing.namespace }
|
|
174
|
+
: values;
|
|
175
|
+
|
|
67
176
|
if (existing) {
|
|
68
177
|
await customRequest(
|
|
69
178
|
`temporalTenantConfig/updateUnique/${existing.id}`,
|
|
70
179
|
'PATCH',
|
|
71
|
-
|
|
180
|
+
payload,
|
|
72
181
|
);
|
|
73
|
-
message.success('
|
|
182
|
+
message.success('配置已更新,Temporal Worker 将自动加载最新连接');
|
|
74
183
|
} else {
|
|
75
|
-
await customRequest('temporalTenantConfig/createOne', 'POST',
|
|
76
|
-
message.success('
|
|
184
|
+
await customRequest('temporalTenantConfig/createOne', 'POST', payload);
|
|
185
|
+
message.success('本 GAdmin 已注册到 Temporal');
|
|
77
186
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{
|
|
82
|
-
take: 1,
|
|
83
|
-
skip: 0,
|
|
84
|
-
select: {
|
|
85
|
-
id: true,
|
|
86
|
-
adminUrl: true,
|
|
87
|
-
adminToken: true,
|
|
88
|
-
namespace: true,
|
|
89
|
-
dbUrl: true,
|
|
90
|
-
displayName: true,
|
|
91
|
-
runtimeBaseUrl: true,
|
|
92
|
-
runtimeToken: true,
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
);
|
|
96
|
-
setExisting(res?.data?.data?.[0] ?? null);
|
|
187
|
+
|
|
188
|
+
setEditorOpen(false);
|
|
189
|
+
await loadConfig();
|
|
97
190
|
} catch (e: any) {
|
|
98
|
-
message.error(e?.response?.data?.message || '
|
|
191
|
+
message.error(e?.response?.data?.message || '保存配置失败');
|
|
99
192
|
} finally {
|
|
100
193
|
setSaving(false);
|
|
101
194
|
}
|
|
102
195
|
};
|
|
103
196
|
|
|
197
|
+
const submitEditor = async () => {
|
|
198
|
+
try {
|
|
199
|
+
const values = await form.validateFields();
|
|
200
|
+
await handleSave(values);
|
|
201
|
+
} catch {
|
|
202
|
+
// Field-level validation messages are rendered by Ant Design.
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
104
206
|
if (!isAdmin) {
|
|
105
207
|
return (
|
|
106
208
|
<div style={{ padding: 24 }}>
|
|
@@ -109,158 +211,312 @@ export default function TemporalTenantPage() {
|
|
|
109
211
|
);
|
|
110
212
|
}
|
|
111
213
|
|
|
112
|
-
if (loading) {
|
|
113
|
-
return (
|
|
114
|
-
<div
|
|
115
|
-
style={{ display: 'flex', justifyContent: 'center', paddingTop: 80 }}
|
|
116
|
-
>
|
|
117
|
-
<Spin size="large" />
|
|
118
|
-
</div>
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
214
|
return (
|
|
123
|
-
<div
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
215
|
+
<div style={PAGE_WRAPPER_STYLE}>
|
|
216
|
+
<Card {...HEADER_CARD_PROPS}>
|
|
217
|
+
<div
|
|
218
|
+
style={{
|
|
219
|
+
display: 'flex',
|
|
220
|
+
alignItems: 'center',
|
|
221
|
+
justifyContent: 'space-between',
|
|
222
|
+
gap: 16,
|
|
223
|
+
flexWrap: 'wrap',
|
|
224
|
+
}}
|
|
225
|
+
>
|
|
226
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
227
|
+
<Button
|
|
228
|
+
type="text"
|
|
229
|
+
icon={<ArrowLeftOutlined />}
|
|
230
|
+
onClick={() => navigate(-1)}
|
|
231
|
+
/>
|
|
232
|
+
<div>
|
|
233
|
+
<Title level={4} style={{ margin: 0 }}>
|
|
234
|
+
服务注册
|
|
235
|
+
</Title>
|
|
236
|
+
<Text type="secondary" style={{ fontSize: 12 }}>
|
|
237
|
+
管理本 GAdmin 与 Temporal Worker 的连接配置
|
|
238
|
+
</Text>
|
|
239
|
+
</div>
|
|
143
240
|
</div>
|
|
241
|
+
|
|
242
|
+
{!loading && !loadError && existing && (
|
|
243
|
+
<Button
|
|
244
|
+
type="primary"
|
|
245
|
+
aria-label="修改配置"
|
|
246
|
+
icon={<EditOutlined />}
|
|
247
|
+
onClick={openEditor}
|
|
248
|
+
>
|
|
249
|
+
修改配置
|
|
250
|
+
</Button>
|
|
251
|
+
)}
|
|
144
252
|
</div>
|
|
145
253
|
</Card>
|
|
146
254
|
|
|
147
|
-
<Card
|
|
148
|
-
{
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
255
|
+
<Card>
|
|
256
|
+
{loading ? (
|
|
257
|
+
<div
|
|
258
|
+
style={{ display: 'grid', placeItems: 'center', minHeight: 320 }}
|
|
259
|
+
>
|
|
260
|
+
<Spin size="large" />
|
|
261
|
+
</div>
|
|
262
|
+
) : loadError ? (
|
|
263
|
+
<Result
|
|
264
|
+
status="error"
|
|
265
|
+
title="无法确认注册状态"
|
|
266
|
+
subTitle="未能读取本 GAdmin 的 Temporal 配置。请检查网络或权限后重试。"
|
|
267
|
+
extra={
|
|
268
|
+
<Button
|
|
269
|
+
type="primary"
|
|
270
|
+
icon={<ReloadOutlined />}
|
|
271
|
+
onClick={retryLoadConfig}
|
|
272
|
+
>
|
|
273
|
+
重新加载
|
|
274
|
+
</Button>
|
|
275
|
+
}
|
|
154
276
|
/>
|
|
277
|
+
) : existing ? (
|
|
278
|
+
<>
|
|
279
|
+
<div
|
|
280
|
+
style={{
|
|
281
|
+
display: 'flex',
|
|
282
|
+
alignItems: 'center',
|
|
283
|
+
justifyContent: 'space-between',
|
|
284
|
+
gap: 16,
|
|
285
|
+
flexWrap: 'wrap',
|
|
286
|
+
padding: '16px 20px',
|
|
287
|
+
background: 'var(--ant-color-fill-secondary)',
|
|
288
|
+
border: '1px solid var(--ant-color-border-secondary)',
|
|
289
|
+
borderRadius: 8,
|
|
290
|
+
}}
|
|
291
|
+
>
|
|
292
|
+
<Space size={12}>
|
|
293
|
+
<CheckCircleFilled
|
|
294
|
+
style={{
|
|
295
|
+
color: token.colorSuccess,
|
|
296
|
+
fontSize: 22,
|
|
297
|
+
}}
|
|
298
|
+
/>
|
|
299
|
+
<div>
|
|
300
|
+
<Text strong style={{ display: 'block' }}>
|
|
301
|
+
已注册到 Temporal
|
|
302
|
+
</Text>
|
|
303
|
+
<Text type="secondary">{existing.displayName}</Text>
|
|
304
|
+
</div>
|
|
305
|
+
</Space>
|
|
306
|
+
<Tag color="success">{existing.namespace}</Tag>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<Divider orientation="left">租户身份</Divider>
|
|
310
|
+
<Descriptions
|
|
311
|
+
bordered
|
|
312
|
+
column={{ xs: 1, sm: 1, md: 2, lg: 2, xl: 2, xxl: 2 }}
|
|
313
|
+
styles={{ label: { width: 180 } }}
|
|
314
|
+
>
|
|
315
|
+
<Descriptions.Item label="显示名称">
|
|
316
|
+
{existing.displayName}
|
|
317
|
+
</Descriptions.Item>
|
|
318
|
+
<Descriptions.Item label="Namespace">
|
|
319
|
+
<Text code>{existing.namespace}</Text>
|
|
320
|
+
</Descriptions.Item>
|
|
321
|
+
</Descriptions>
|
|
322
|
+
|
|
323
|
+
<Divider orientation="left">Temporal 管理连接</Divider>
|
|
324
|
+
<Descriptions
|
|
325
|
+
bordered
|
|
326
|
+
column={1}
|
|
327
|
+
styles={{ label: { width: 180 } }}
|
|
328
|
+
>
|
|
329
|
+
<Descriptions.Item label="Admin URL">
|
|
330
|
+
<Text code>{existing.adminUrl}</Text>
|
|
331
|
+
</Descriptions.Item>
|
|
332
|
+
<Descriptions.Item label="Admin Token">
|
|
333
|
+
<SensitiveValue
|
|
334
|
+
label="Admin Token"
|
|
335
|
+
value={existing.adminToken}
|
|
336
|
+
/>
|
|
337
|
+
</Descriptions.Item>
|
|
338
|
+
</Descriptions>
|
|
339
|
+
|
|
340
|
+
<Divider orientation="left">Worker 运行连接</Divider>
|
|
341
|
+
<Descriptions
|
|
342
|
+
bordered
|
|
343
|
+
column={1}
|
|
344
|
+
styles={{ label: { width: 180 } }}
|
|
345
|
+
>
|
|
346
|
+
<Descriptions.Item label="数据库连接地址">
|
|
347
|
+
<SensitiveValue
|
|
348
|
+
label="数据库连接地址"
|
|
349
|
+
value={existing.dbUrl}
|
|
350
|
+
maskedValue={maskDatabaseUrl(existing.dbUrl)}
|
|
351
|
+
/>
|
|
352
|
+
</Descriptions.Item>
|
|
353
|
+
<Descriptions.Item label="Runtime API 地址">
|
|
354
|
+
{existing.runtimeBaseUrl ? (
|
|
355
|
+
<Text code>{existing.runtimeBaseUrl}</Text>
|
|
356
|
+
) : (
|
|
357
|
+
<Text type="secondary">未配置</Text>
|
|
358
|
+
)}
|
|
359
|
+
</Descriptions.Item>
|
|
360
|
+
<Descriptions.Item label="Runtime Token">
|
|
361
|
+
<SensitiveValue
|
|
362
|
+
label="Runtime Token"
|
|
363
|
+
value={existing.runtimeToken}
|
|
364
|
+
/>
|
|
365
|
+
</Descriptions.Item>
|
|
366
|
+
</Descriptions>
|
|
367
|
+
|
|
368
|
+
<Alert
|
|
369
|
+
type="info"
|
|
370
|
+
showIcon
|
|
371
|
+
message="保存修改后,Temporal Worker 会自动加载本 GAdmin 的最新连接配置。"
|
|
372
|
+
style={{ marginTop: 20 }}
|
|
373
|
+
/>
|
|
374
|
+
</>
|
|
375
|
+
) : (
|
|
376
|
+
<Empty
|
|
377
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
378
|
+
description={
|
|
379
|
+
<Space direction="vertical" size={4}>
|
|
380
|
+
<Text strong>本 GAdmin 尚未注册</Text>
|
|
381
|
+
<Text type="secondary">
|
|
382
|
+
注册后,Temporal Worker 才能执行本项目的 Workflow。
|
|
383
|
+
</Text>
|
|
384
|
+
</Space>
|
|
385
|
+
}
|
|
386
|
+
>
|
|
387
|
+
<Button type="primary" icon={<PlusOutlined />} onClick={openEditor}>
|
|
388
|
+
注册服务
|
|
389
|
+
</Button>
|
|
390
|
+
</Empty>
|
|
155
391
|
)}
|
|
392
|
+
</Card>
|
|
393
|
+
|
|
394
|
+
<Drawer
|
|
395
|
+
title={existing ? '修改服务配置' : '注册本 GAdmin'}
|
|
396
|
+
width={600}
|
|
397
|
+
open={editorOpen}
|
|
398
|
+
destroyOnClose
|
|
399
|
+
maskClosable={!saving}
|
|
400
|
+
closable={!saving}
|
|
401
|
+
onClose={() => setEditorOpen(false)}
|
|
402
|
+
footer={
|
|
403
|
+
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
|
|
404
|
+
<Button disabled={saving} onClick={() => setEditorOpen(false)}>
|
|
405
|
+
取消
|
|
406
|
+
</Button>
|
|
407
|
+
<Button type="primary" loading={saving} onClick={submitEditor}>
|
|
408
|
+
{existing ? '保存修改' : '注册服务'}
|
|
409
|
+
</Button>
|
|
410
|
+
</div>
|
|
411
|
+
}
|
|
412
|
+
>
|
|
413
|
+
<Paragraph type="secondary" style={{ marginBottom: 24 }}>
|
|
414
|
+
这些地址都需要从 Temporal Worker
|
|
415
|
+
所在网络访问。敏感信息保存后默认遮挡。
|
|
416
|
+
</Paragraph>
|
|
417
|
+
|
|
156
418
|
<Form
|
|
419
|
+
id="temporal-tenant-form"
|
|
157
420
|
form={form}
|
|
158
421
|
layout="vertical"
|
|
159
422
|
onFinish={handleSave}
|
|
160
|
-
|
|
423
|
+
requiredMark="optional"
|
|
161
424
|
>
|
|
162
|
-
<Title level={5}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
display: 'grid',
|
|
168
|
-
gridTemplateColumns: '1fr 1fr',
|
|
169
|
-
gap: '0 16px',
|
|
170
|
-
}}
|
|
425
|
+
<Title level={5}>Temporal 管理连接</Title>
|
|
426
|
+
<Form.Item
|
|
427
|
+
label="Admin URL"
|
|
428
|
+
name="adminUrl"
|
|
429
|
+
rules={[{ required: true, message: '请输入 Admin URL' }]}
|
|
171
430
|
>
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
</Form.Item>
|
|
179
|
-
<Form.Item
|
|
180
|
-
label="Admin Token"
|
|
181
|
-
name="adminToken"
|
|
182
|
-
rules={[{ required: true, message: '请输入 Admin Token' }]}
|
|
183
|
-
>
|
|
184
|
-
<Input.Password placeholder="32 位 hex token" />
|
|
185
|
-
</Form.Item>
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
<Title
|
|
189
|
-
level={5}
|
|
190
|
-
style={{ color: 'var(--ant-color-text-secondary)', marginTop: 8 }}
|
|
431
|
+
<Input placeholder="http://localhost:8081" />
|
|
432
|
+
</Form.Item>
|
|
433
|
+
<Form.Item
|
|
434
|
+
label="Admin Token"
|
|
435
|
+
name="adminToken"
|
|
436
|
+
rules={[{ required: true, message: '请输入 Admin Token' }]}
|
|
191
437
|
>
|
|
192
|
-
|
|
438
|
+
<Input.Password
|
|
439
|
+
placeholder="Temporal Admin API Token"
|
|
440
|
+
autoComplete="new-password"
|
|
441
|
+
/>
|
|
442
|
+
</Form.Item>
|
|
443
|
+
|
|
444
|
+
<Title level={5} style={{ marginTop: 28 }}>
|
|
445
|
+
本 GAdmin
|
|
193
446
|
</Title>
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
447
|
+
<Form.Item
|
|
448
|
+
label="Namespace"
|
|
449
|
+
name="namespace"
|
|
450
|
+
extra={
|
|
451
|
+
existing
|
|
452
|
+
? 'Namespace 是远端租户标识,注册后不能在这里修改。'
|
|
453
|
+
: '用于隔离本 GAdmin 的 Workflow。'
|
|
454
|
+
}
|
|
455
|
+
rules={[{ required: true, message: '请输入 Namespace' }]}
|
|
200
456
|
>
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
name="displayName"
|
|
211
|
-
rules={[{ required: true, message: '请输入展示名' }]}
|
|
212
|
-
>
|
|
213
|
-
<Input placeholder="Gadmin Test" />
|
|
214
|
-
</Form.Item>
|
|
215
|
-
</div>
|
|
216
|
-
|
|
457
|
+
<Input disabled={Boolean(existing)} placeholder="gadmin-test" />
|
|
458
|
+
</Form.Item>
|
|
459
|
+
<Form.Item
|
|
460
|
+
label="显示名称"
|
|
461
|
+
name="displayName"
|
|
462
|
+
rules={[{ required: true, message: '请输入显示名称' }]}
|
|
463
|
+
>
|
|
464
|
+
<Input placeholder="GAdmin Test" />
|
|
465
|
+
</Form.Item>
|
|
217
466
|
<Form.Item
|
|
218
|
-
label="
|
|
467
|
+
label="数据库连接地址"
|
|
219
468
|
name="dbUrl"
|
|
220
|
-
|
|
469
|
+
extra="填写 Temporal Worker 能访问到的 PostgreSQL 地址。"
|
|
470
|
+
rules={[{ required: true, message: '请输入数据库连接地址' }]}
|
|
221
471
|
>
|
|
222
|
-
<Input
|
|
472
|
+
<Input.Password
|
|
473
|
+
placeholder="postgresql://user:password@host:5432/database"
|
|
474
|
+
autoComplete="new-password"
|
|
475
|
+
/>
|
|
223
476
|
</Form.Item>
|
|
224
477
|
|
|
225
|
-
<Title
|
|
226
|
-
|
|
227
|
-
style={{ color: 'var(--ant-color-text-secondary)', marginTop: 24 }}
|
|
228
|
-
>
|
|
229
|
-
Runtime API 连接
|
|
478
|
+
<Title level={5} style={{ marginTop: 28 }}>
|
|
479
|
+
Runtime 连接
|
|
230
480
|
</Title>
|
|
231
481
|
<Form.Item
|
|
232
|
-
label="Runtime
|
|
482
|
+
label="Runtime API 地址"
|
|
233
483
|
name="runtimeBaseUrl"
|
|
484
|
+
extra="填写 Temporal Worker 能访问到的 GAdmin API 地址,并以 /api 结尾。"
|
|
234
485
|
rules={[
|
|
235
|
-
{ required: true, message: '请输入 Runtime
|
|
236
|
-
{
|
|
486
|
+
{ required: true, message: '请输入 Runtime API 地址' },
|
|
487
|
+
{
|
|
488
|
+
validator: async (_, value?: string) => {
|
|
489
|
+
if (!value) return;
|
|
490
|
+
try {
|
|
491
|
+
const url = new URL(value);
|
|
492
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
493
|
+
throw new Error();
|
|
494
|
+
}
|
|
495
|
+
} catch {
|
|
496
|
+
throw new Error('请输入完整的 HTTP 或 HTTPS 地址');
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
},
|
|
237
500
|
]}
|
|
238
|
-
extra="填写 Temporal worker 能访问到的 Gadmin API 地址,并以 /api 结尾"
|
|
239
501
|
>
|
|
240
502
|
<Input placeholder="http://gadmin-server:8000/api" />
|
|
241
503
|
</Form.Item>
|
|
242
504
|
<Form.Item
|
|
243
505
|
label="Runtime Token"
|
|
244
506
|
name="runtimeToken"
|
|
507
|
+
extra="必须与本 GAdmin 的 WORKFLOW_RUNTIME_TOKEN 完全一致。"
|
|
245
508
|
rules={[
|
|
246
509
|
{ required: true, message: '请输入 Runtime Token' },
|
|
247
510
|
{ min: 32, message: 'Runtime Token 至少需要 32 个字符' },
|
|
248
511
|
]}
|
|
249
|
-
extra="必须与 Gadmin 的 WORKFLOW_RUNTIME_TOKEN 完全一致"
|
|
250
512
|
>
|
|
251
513
|
<Input.Password
|
|
252
|
-
placeholder="
|
|
514
|
+
placeholder="WORKFLOW_RUNTIME_TOKEN"
|
|
253
515
|
autoComplete="new-password"
|
|
254
516
|
/>
|
|
255
517
|
</Form.Item>
|
|
256
|
-
|
|
257
|
-
<Form.Item style={{ marginTop: 8 }}>
|
|
258
|
-
<Button type="primary" htmlType="submit" loading={saving}>
|
|
259
|
-
{existing ? '更新并同步到 Temporal' : '注册到 Temporal'}
|
|
260
|
-
</Button>
|
|
261
|
-
</Form.Item>
|
|
262
518
|
</Form>
|
|
263
|
-
</
|
|
519
|
+
</Drawer>
|
|
264
520
|
</div>
|
|
265
521
|
);
|
|
266
522
|
}
|