@gadmin2n/schematics 0.0.141 → 0.0.143
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/prisma/workflow.prisma +5 -23
- 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/lib/page-helpers.ts +4 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/page-manage.ts +36 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/agenda.seed.ts +14 -14
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/bootstrap.ts +18 -13
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/canvas.seed.ts +14 -14
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +67 -404
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +16 -40
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflows.ts +12 -124
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.module.ts +3 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/index.ts +0 -21
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-runtime.controller.ts +3 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/codeRuntime.module.ts +1 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-callback.controller.spec.ts +48 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-callback.controller.ts +96 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-runtime.controller.spec.ts +1 -59
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-runtime.controller.ts +8 -43
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-runtime.dto.ts +0 -23
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction.service.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-wait.handler.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/externalInteraction.module.ts +7 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.controller.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.service.spec.ts +18 -123
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.service.ts +4 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/http-request-runtime.controller.ts +3 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/httpRequest.module.ts +1 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.controller.ts +0 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.dto.ts +12 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.errors.ts +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.external-decision.spec.ts +42 -209
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.service.ts +108 -225
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/humanApproval.module.ts +0 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.controller.ts +27 -22
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.dto.ts +9 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/runtime-token.guard.spec.ts +30 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/runtime-token.guard.ts +8 -15
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/index.ts +0 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/runtime-connectivity-probe.service.spec.ts +14 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/runtime-connectivity-probe.service.ts +37 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.spec.ts +41 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.ts +53 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.module.ts +7 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/workflow-runtime-health.controller.ts +21 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.spec.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-callable.spec.ts +16 -88
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-callable.ts +23 -59
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +125 -39
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.ts +95 -28
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-handle-validator.ts +0 -20
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.spec.ts +23 -26
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.ts +6 -52
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +24 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +26 -21
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/widget-catalog.service.spec.ts +0 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/widget-catalog.service.ts +0 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowRuntimeConfig/workflow-runtime-config.service.spec.ts +32 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowRuntimeConfig/workflow-runtime-config.service.ts +41 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowRuntimeConfig/workflowRuntimeConfig.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/tsconfig.build.json +10 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +16 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/PageHeader.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/inspectorActions.ts +19 -19
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/contexts/color-mode/index.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/header.tsx +15 -11
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +0 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/AddDataModal.tsx +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/AddPageModal.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/DeleteDataConfirm.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/EditDataModal.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +85 -95
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +85 -95
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/list.tsx +21 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/game/list.tsx +21 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/human-approval/inbox.tsx +14 -66
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +4 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CustomNode.test.tsx +29 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CustomNode.tsx +4 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/EnhancedFlowRenderer.tsx +4 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExecutionStatusNode.test.tsx +3 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExecutionStatusNode.tsx +3 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/FlowRenderer.tsx +4 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NewNodeDefaults.test.tsx +23 -92
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeOutputSummary.tsx +200 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.test.tsx +61 -136
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +96 -195
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeTypeBadge.tsx +36 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/CreateNodeInstanceModal.tsx +5 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/NodeInstanceForm.tsx +47 -22
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/create.tsx +7 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/edit.tsx +8 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-config.css +35 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/default-widgets.tsx +1 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ApproverSourceEditor.tsx +1 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/CodeConfigEditor.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalWaitConfigEditor.tsx +82 -259
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HttpRequestConfigEditor.tsx +1 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.test.tsx +7 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.tsx +77 -71
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.test.tsx +10 -57
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.tsx +8 -273
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.test.tsx +54 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +110 -20
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/newNodeConfig.ts +4 -34
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveOutputs.test.ts +3 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveOutputs.ts +2 -37
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/list.tsx +26 -2
- package/package.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.controller.ts +0 -89
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.dto.ts +0 -68
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.service.spec.ts +0 -177
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.service.ts +0 -283
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approvalChannel.module.ts +0 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/ApprovalChannelModal.test.tsx +0 -84
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/ApprovalChannelModal.tsx +0 -141
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/approval-channel.types.ts +0 -16
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/index.tsx +0 -210
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ApprovalRequestTriggerConfigEditor.tsx +0 -39
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubmitApprovalResultConfigEditor.tsx +0 -104
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubmitExternalResultConfigEditor.tsx +0 -98
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/AssignRolesModal.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/CreatePageModal.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/EditPageModal.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/PageDetailDrawer.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/{Components → components}/PageFormModal.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/{Components → components}/CreateModal.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/{Components → components}/EditModal.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/{Components → components}/ResourceDetailDrawer.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/{Components → components}/modal.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/{Components → components}/CreateModal.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/{Components → components}/EditModal.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/{Components → components}/RoleDetailDrawer.tsx +0 -0
- /package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/{Components → components}/modal.tsx +0 -0
|
@@ -20,25 +20,26 @@ import { seedNodeTypes } from './workflow-node-types';
|
|
|
20
20
|
* 业务数据 seed 函数仍来自 ./workflow-{node-types,workflows,event-trigger}.ts
|
|
21
21
|
* (文件较大,保持独立但只被本入口 import,不再由 seed/index.ts 直接调用)。
|
|
22
22
|
*
|
|
23
|
-
* 前置:seedBootstrap 已建好
|
|
23
|
+
* 前置:seedBootstrap 已建好 SYSTEM_ADMIN。
|
|
24
24
|
*/
|
|
25
25
|
export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
|
|
26
26
|
const role = await prisma.role.findUniqueOrThrow({
|
|
27
27
|
where: { name: 'SYSTEM_ADMIN' },
|
|
28
28
|
});
|
|
29
|
-
const admin = await prisma.page.findUniqueOrThrow({
|
|
30
|
-
where: { code: 'admin' },
|
|
31
|
-
});
|
|
32
29
|
|
|
33
|
-
// ── 父分组 page
|
|
34
|
-
const workflowPage = await upsertPage(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
// ── 父分组 page(与 admin 平级的顶级菜单) ─────────────────────────────────
|
|
31
|
+
const workflowPage = await upsertPage(
|
|
32
|
+
prisma,
|
|
33
|
+
{
|
|
34
|
+
code: 'workflow',
|
|
35
|
+
name: 'workflow',
|
|
36
|
+
zhName: 'Workflow',
|
|
37
|
+
enName: 'Workflow',
|
|
38
|
+
sortOrder: 1030,
|
|
39
|
+
},
|
|
40
|
+
// 从「系统管理」子菜单提升为顶级菜单:同步 parentId/排序到已有 DB
|
|
41
|
+
{ parentId: null, sortOrder: 1030 },
|
|
42
|
+
);
|
|
42
43
|
console.log(`[workflow] Page: ${workflowPage.code} (id=${workflowPage.id})`);
|
|
43
44
|
|
|
44
45
|
// ── leaf pages + resource 绑定 ─────────────────────────────────────────────
|
|
@@ -82,20 +83,11 @@ export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
|
|
|
82
83
|
resourceCode: 'temporalTenantConfig',
|
|
83
84
|
resourceDescription: 'Temporal tenant config management',
|
|
84
85
|
},
|
|
85
|
-
{
|
|
86
|
-
code: 'workflow_approval_channels',
|
|
87
|
-
zhName: '审批通道',
|
|
88
|
-
enName: 'Approval Channels',
|
|
89
|
-
sortOrder: 5,
|
|
90
|
-
resourceCode: 'approvalChannel',
|
|
91
|
-
resourceDescription:
|
|
92
|
-
'Reusable dispatch and callback workflows for external approvals',
|
|
93
|
-
},
|
|
94
86
|
{
|
|
95
87
|
code: 'approval_tasks',
|
|
96
88
|
zhName: '审批任务',
|
|
97
89
|
enName: 'Approval Tasks',
|
|
98
|
-
sortOrder:
|
|
90
|
+
sortOrder: 5,
|
|
99
91
|
resourceCode: 'humanApproval',
|
|
100
92
|
resourceDescription: 'Read and decide assigned Human Approval tasks',
|
|
101
93
|
},
|
|
@@ -185,10 +177,7 @@ export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
|
|
|
185
177
|
const connectionsPage = allPages.find(
|
|
186
178
|
(page) => page.code === 'workflow_connections',
|
|
187
179
|
);
|
|
188
|
-
|
|
189
|
-
(page) => page.code === 'workflow_approval_channels',
|
|
190
|
-
);
|
|
191
|
-
if (nodeTemplatePage && connectionsPage && approvalChannelsPage) {
|
|
180
|
+
if (nodeTemplatePage && connectionsPage) {
|
|
192
181
|
const authorRoles = await prisma.rolePages.findMany({
|
|
193
182
|
where: { pageId: nodeTemplatePage.id },
|
|
194
183
|
select: { roleId: true },
|
|
@@ -197,10 +186,6 @@ export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
|
|
|
197
186
|
prisma,
|
|
198
187
|
connectionsPage.id,
|
|
199
188
|
);
|
|
200
|
-
const approvalChannelResourceIds = await getPageResourceIds(
|
|
201
|
-
prisma,
|
|
202
|
-
approvalChannelsPage.id,
|
|
203
|
-
);
|
|
204
189
|
for (const { roleId } of authorRoles) {
|
|
205
190
|
await grantRoleAccess(
|
|
206
191
|
prisma,
|
|
@@ -211,15 +196,6 @@ export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
|
|
|
211
196
|
console.log(
|
|
212
197
|
`[workflow] Grant: role#${roleId} → workflow_connections (inherits Node Template access)`,
|
|
213
198
|
);
|
|
214
|
-
await grantRoleAccess(
|
|
215
|
-
prisma,
|
|
216
|
-
roleId,
|
|
217
|
-
approvalChannelsPage.id,
|
|
218
|
-
approvalChannelResourceIds,
|
|
219
|
-
);
|
|
220
|
-
console.log(
|
|
221
|
-
`[workflow] Grant: role#${roleId} → workflow_approval_channels (inherits Node Template access)`,
|
|
222
|
-
);
|
|
223
199
|
}
|
|
224
200
|
}
|
|
225
201
|
|
|
@@ -40,26 +40,18 @@ const WORKFLOWS = [
|
|
|
40
40
|
position: { x: 850, y: 200 },
|
|
41
41
|
config: {},
|
|
42
42
|
},
|
|
43
|
-
{
|
|
44
|
-
id: 'action_4',
|
|
45
|
-
type: 'send_notification',
|
|
46
|
-
label: '发送通知',
|
|
47
|
-
position: { x: 1100, y: 200 },
|
|
48
|
-
config: {},
|
|
49
|
-
},
|
|
50
43
|
],
|
|
51
44
|
edges: [
|
|
52
45
|
{ id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
|
|
53
46
|
{ id: 'e-action_1-action_2', source: 'action_1', target: 'action_2' },
|
|
54
47
|
{ id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
|
|
55
|
-
{ id: 'e-action_3-action_4', source: 'action_3', target: 'action_4' },
|
|
56
48
|
],
|
|
57
49
|
},
|
|
58
50
|
},
|
|
59
51
|
{
|
|
60
52
|
code: 'user_reg_approval',
|
|
61
53
|
name: '新用户注册审批',
|
|
62
|
-
description: '
|
|
54
|
+
description: '新用户注册后触发审批流程,管理员审批通过后激活用户',
|
|
63
55
|
status: 'PUBLISHED' as const,
|
|
64
56
|
isEnabled: true,
|
|
65
57
|
creator: 'admin',
|
|
@@ -71,13 +63,6 @@ const WORKFLOWS = [
|
|
|
71
63
|
label: 'Webhook 接收注册',
|
|
72
64
|
position: { x: 100, y: 200 },
|
|
73
65
|
},
|
|
74
|
-
{
|
|
75
|
-
id: 'action_1',
|
|
76
|
-
type: 'set_variable',
|
|
77
|
-
label: '提取用户信息',
|
|
78
|
-
position: { x: 350, y: 200 },
|
|
79
|
-
config: {},
|
|
80
|
-
},
|
|
81
66
|
{
|
|
82
67
|
id: 'approval_1',
|
|
83
68
|
type: 'approval',
|
|
@@ -99,26 +84,11 @@ const WORKFLOWS = [
|
|
|
99
84
|
position: { x: 1100, y: 100 },
|
|
100
85
|
config: {},
|
|
101
86
|
},
|
|
102
|
-
{
|
|
103
|
-
id: 'action_3',
|
|
104
|
-
type: 'send_notification',
|
|
105
|
-
label: '发送欢迎邮件',
|
|
106
|
-
position: { x: 1350, y: 100 },
|
|
107
|
-
config: {},
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
id: 'action_4',
|
|
111
|
-
type: 'send_notification',
|
|
112
|
-
label: '发送拒绝通知',
|
|
113
|
-
position: { x: 1100, y: 300 },
|
|
114
|
-
config: {},
|
|
115
|
-
},
|
|
116
87
|
],
|
|
117
88
|
edges: [
|
|
118
|
-
{ id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
|
|
119
89
|
{
|
|
120
|
-
id: 'e-
|
|
121
|
-
source: '
|
|
90
|
+
id: 'e-trigger_1-approval_1',
|
|
91
|
+
source: 'trigger_1',
|
|
122
92
|
target: 'approval_1',
|
|
123
93
|
},
|
|
124
94
|
{
|
|
@@ -132,20 +102,13 @@ const WORKFLOWS = [
|
|
|
132
102
|
target: 'action_2',
|
|
133
103
|
label: 'true',
|
|
134
104
|
},
|
|
135
|
-
{
|
|
136
|
-
id: 'e-condition_1-action_4',
|
|
137
|
-
source: 'condition_1',
|
|
138
|
-
target: 'action_4',
|
|
139
|
-
label: 'false',
|
|
140
|
-
},
|
|
141
|
-
{ id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
|
|
142
105
|
],
|
|
143
106
|
},
|
|
144
107
|
},
|
|
145
108
|
{
|
|
146
109
|
code: 'error_monitor_alert',
|
|
147
110
|
name: '异常监控告警',
|
|
148
|
-
description: '
|
|
111
|
+
description: '监听系统事件并记录异常监控结果',
|
|
149
112
|
status: 'PUBLISHED' as const,
|
|
150
113
|
isEnabled: true,
|
|
151
114
|
creator: 'system',
|
|
@@ -172,13 +135,6 @@ const WORKFLOWS = [
|
|
|
172
135
|
position: { x: 600, y: 200 },
|
|
173
136
|
config: {},
|
|
174
137
|
},
|
|
175
|
-
{
|
|
176
|
-
id: 'action_2',
|
|
177
|
-
type: 'send_notification',
|
|
178
|
-
label: '发送告警',
|
|
179
|
-
position: { x: 850, y: 100 },
|
|
180
|
-
config: {},
|
|
181
|
-
},
|
|
182
138
|
{
|
|
183
139
|
id: 'action_3',
|
|
184
140
|
type: 'code',
|
|
@@ -194,12 +150,6 @@ const WORKFLOWS = [
|
|
|
194
150
|
source: 'action_1',
|
|
195
151
|
target: 'condition_1',
|
|
196
152
|
},
|
|
197
|
-
{
|
|
198
|
-
id: 'e-condition_1-action_2',
|
|
199
|
-
source: 'condition_1',
|
|
200
|
-
target: 'action_2',
|
|
201
|
-
label: 'true',
|
|
202
|
-
},
|
|
203
153
|
{
|
|
204
154
|
id: 'e-condition_1-action_3',
|
|
205
155
|
source: 'condition_1',
|
|
@@ -212,7 +162,7 @@ const WORKFLOWS = [
|
|
|
212
162
|
{
|
|
213
163
|
code: 'batch_data_export',
|
|
214
164
|
name: '批量数据导出',
|
|
215
|
-
description: '手动触发导出指定表的数据为CSV
|
|
165
|
+
description: '手动触发导出指定表的数据为 CSV 文件',
|
|
216
166
|
status: 'DRAFT' as const,
|
|
217
167
|
isEnabled: false,
|
|
218
168
|
creator: 'admin',
|
|
@@ -245,19 +195,11 @@ const WORKFLOWS = [
|
|
|
245
195
|
position: { x: 850, y: 200 },
|
|
246
196
|
config: {},
|
|
247
197
|
},
|
|
248
|
-
{
|
|
249
|
-
id: 'action_3',
|
|
250
|
-
type: 'send_notification',
|
|
251
|
-
label: '发送邮件',
|
|
252
|
-
position: { x: 1100, y: 200 },
|
|
253
|
-
config: {},
|
|
254
|
-
},
|
|
255
198
|
],
|
|
256
199
|
edges: [
|
|
257
200
|
{ id: 'e-trigger_1-action_1', source: 'trigger_1', target: 'action_1' },
|
|
258
201
|
{ id: 'e-action_1-loop_1', source: 'action_1', target: 'loop_1' },
|
|
259
202
|
{ id: 'e-loop_1-action_2', source: 'loop_1', target: 'action_2' },
|
|
260
|
-
{ id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
|
|
261
203
|
],
|
|
262
204
|
},
|
|
263
205
|
},
|
|
@@ -304,13 +246,6 @@ const WORKFLOWS = [
|
|
|
304
246
|
position: { x: 850, y: 300 },
|
|
305
247
|
config: {},
|
|
306
248
|
},
|
|
307
|
-
{
|
|
308
|
-
id: 'action_3',
|
|
309
|
-
type: 'send_notification',
|
|
310
|
-
label: '部署完成通知',
|
|
311
|
-
position: { x: 1100, y: 200 },
|
|
312
|
-
config: {},
|
|
313
|
-
},
|
|
314
249
|
],
|
|
315
250
|
edges: [
|
|
316
251
|
{
|
|
@@ -333,16 +268,13 @@ const WORKFLOWS = [
|
|
|
333
268
|
source: 'approval_1',
|
|
334
269
|
target: 'action_2',
|
|
335
270
|
},
|
|
336
|
-
{ id: 'e-action_1-action_3', source: 'action_1', target: 'action_3' },
|
|
337
|
-
{ id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
|
|
338
271
|
],
|
|
339
272
|
},
|
|
340
273
|
},
|
|
341
274
|
{
|
|
342
275
|
code: 'user_role_change',
|
|
343
|
-
name: '
|
|
344
|
-
description:
|
|
345
|
-
'当 t_admin_user_role 表中用户角色发生变更时,记录审计日志并通知相关人员',
|
|
276
|
+
name: '用户角色变更审计',
|
|
277
|
+
description: '当 t_admin_user_role 表中用户角色发生变更时,记录审计日志',
|
|
346
278
|
status: 'PUBLISHED' as const,
|
|
347
279
|
isEnabled: true,
|
|
348
280
|
creator: 'admin',
|
|
@@ -386,17 +318,6 @@ const WORKFLOWS = [
|
|
|
386
318
|
],
|
|
387
319
|
},
|
|
388
320
|
},
|
|
389
|
-
{
|
|
390
|
-
id: 'action_3',
|
|
391
|
-
type: 'send_notification',
|
|
392
|
-
label: '发送安全告警',
|
|
393
|
-
position: { x: 1100, y: 100 },
|
|
394
|
-
config: {
|
|
395
|
-
channel: 'wecom',
|
|
396
|
-
recipients: ['security-team'],
|
|
397
|
-
message: '用户 {{username}} 被授予 SYSTEM_ADMIN 角色',
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
321
|
{
|
|
401
322
|
id: 'action_4',
|
|
402
323
|
type: 'db_execute',
|
|
@@ -414,9 +335,9 @@ const WORKFLOWS = [
|
|
|
414
335
|
target: 'condition_1',
|
|
415
336
|
},
|
|
416
337
|
{
|
|
417
|
-
id: 'e-condition_1-
|
|
338
|
+
id: 'e-condition_1-action_4-true',
|
|
418
339
|
source: 'condition_1',
|
|
419
|
-
target: '
|
|
340
|
+
target: 'action_4',
|
|
420
341
|
label: 'true',
|
|
421
342
|
},
|
|
422
343
|
{
|
|
@@ -425,15 +346,13 @@ const WORKFLOWS = [
|
|
|
425
346
|
target: 'action_4',
|
|
426
347
|
label: 'false',
|
|
427
348
|
},
|
|
428
|
-
{ id: 'e-action_3-action_4', source: 'action_3', target: 'action_4' },
|
|
429
349
|
],
|
|
430
350
|
},
|
|
431
351
|
},
|
|
432
352
|
{
|
|
433
353
|
code: 'event_remind_sync',
|
|
434
354
|
name: 'Event活动提醒与同步',
|
|
435
|
-
description:
|
|
436
|
-
'当 t_event 表新增活动或活动即将开始时,自动同步到 ServiceNow 并通知参与人员',
|
|
355
|
+
description: '当 t_event 表新增活动或活动即将开始时,自动同步到 ServiceNow',
|
|
437
356
|
status: 'PUBLISHED' as const,
|
|
438
357
|
isEnabled: true,
|
|
439
358
|
creator: 'admin',
|
|
@@ -504,17 +423,6 @@ const WORKFLOWS = [
|
|
|
504
423
|
position: { x: 1600, y: 80 },
|
|
505
424
|
config: { operation: 'update', table: 't_event', where: 'id = $1' },
|
|
506
425
|
},
|
|
507
|
-
{
|
|
508
|
-
id: 'action_4',
|
|
509
|
-
type: 'send_notification',
|
|
510
|
-
label: '通知OIT负责人',
|
|
511
|
-
position: { x: 1350, y: 250 },
|
|
512
|
-
config: {
|
|
513
|
-
channel: 'email',
|
|
514
|
-
message:
|
|
515
|
-
'活动 {{currentItem.city}} - {{currentItem.eventType}} 将于 {{currentItem.start_date}} 开始',
|
|
516
|
-
},
|
|
517
|
-
},
|
|
518
426
|
{
|
|
519
427
|
id: 'action_5',
|
|
520
428
|
type: 'code',
|
|
@@ -551,14 +459,7 @@ const WORKFLOWS = [
|
|
|
551
459
|
target: 'action_2',
|
|
552
460
|
label: 'false',
|
|
553
461
|
},
|
|
554
|
-
{
|
|
555
|
-
id: 'e-condition_2-action_4',
|
|
556
|
-
source: 'condition_2',
|
|
557
|
-
target: 'action_4',
|
|
558
|
-
label: 'true',
|
|
559
|
-
},
|
|
560
462
|
{ id: 'e-action_2-action_3', source: 'action_2', target: 'action_3' },
|
|
561
|
-
{ id: 'e-action_3-action_4', source: 'action_3', target: 'action_4' },
|
|
562
463
|
],
|
|
563
464
|
},
|
|
564
465
|
},
|
|
@@ -566,7 +467,7 @@ const WORKFLOWS = [
|
|
|
566
467
|
code: 'new_user_onboard',
|
|
567
468
|
name: '新增用户自动化流程',
|
|
568
469
|
description:
|
|
569
|
-
'当 t_admin_user_role
|
|
470
|
+
'当 t_admin_user_role 新增用户时,自动分配默认角色、同步到外部系统并记录审计日志',
|
|
570
471
|
status: 'PUBLISHED' as const,
|
|
571
472
|
isEnabled: true,
|
|
572
473
|
creator: 'admin',
|
|
@@ -632,18 +533,6 @@ const WORKFLOWS = [
|
|
|
632
533
|
method: 'POST',
|
|
633
534
|
},
|
|
634
535
|
},
|
|
635
|
-
{
|
|
636
|
-
id: 'action_5',
|
|
637
|
-
type: 'send_notification',
|
|
638
|
-
label: '发送欢迎通知',
|
|
639
|
-
position: { x: 1350, y: 200 },
|
|
640
|
-
config: {
|
|
641
|
-
channel: 'wecom',
|
|
642
|
-
recipients: ['{{userid}}'],
|
|
643
|
-
message:
|
|
644
|
-
'欢迎加入系统!您的初始角色为 GUEST,如需更多权限请联系管理员。',
|
|
645
|
-
},
|
|
646
|
-
},
|
|
647
536
|
{
|
|
648
537
|
id: 'action_6',
|
|
649
538
|
type: 'db_execute',
|
|
@@ -677,8 +566,7 @@ const WORKFLOWS = [
|
|
|
677
566
|
label: 'false',
|
|
678
567
|
},
|
|
679
568
|
{ id: 'e-action_3-action_4', source: 'action_3', target: 'action_4' },
|
|
680
|
-
{ id: 'e-action_4-
|
|
681
|
-
{ id: 'e-action_5-action_6', source: 'action_5', target: 'action_6' },
|
|
569
|
+
{ id: 'e-action_4-action_6', source: 'action_4', target: 'action_6' },
|
|
682
570
|
],
|
|
683
571
|
},
|
|
684
572
|
},
|
|
@@ -23,6 +23,7 @@ import { HealthModule } from './modules/health/health.module';
|
|
|
23
23
|
import { RoleModule } from './modules/role/role.module';
|
|
24
24
|
import { RoleService } from './modules/role/role.service';
|
|
25
25
|
import { RolesRefresherService } from './modules/role/roles-refresher.service';
|
|
26
|
+
import { WorkflowRuntimeConfigModule } from './modules/workflowRuntimeConfig/workflowRuntimeConfig.module';
|
|
26
27
|
|
|
27
28
|
@Module({
|
|
28
29
|
imports: [
|
|
@@ -84,6 +85,8 @@ import { RolesRefresherService } from './modules/role/roles-refresher.service';
|
|
|
84
85
|
ttl: 5 * 60 * 1000, // 5 minutes
|
|
85
86
|
}),
|
|
86
87
|
|
|
88
|
+
WorkflowRuntimeConfigModule,
|
|
89
|
+
|
|
87
90
|
...modules,
|
|
88
91
|
AgendaModule,
|
|
89
92
|
HealthModule,
|
|
@@ -53,7 +53,6 @@ const config: Record<string, any> = {
|
|
|
53
53
|
address: 'http://127.0.0.1:3000/upload/',
|
|
54
54
|
maxSize: 1024 * 1024 * 10,
|
|
55
55
|
},
|
|
56
|
-
humanApprovalRuntime: runtimeConfig(),
|
|
57
56
|
workflow: {
|
|
58
57
|
publicBaseUrl: process.env.WORKFLOW_PUBLIC_BASE_URL ?? '',
|
|
59
58
|
publicHost: process.env.WORKFLOW_PUBLIC_HOST ?? '',
|
|
@@ -63,24 +62,4 @@ const config: Record<string, any> = {
|
|
|
63
62
|
},
|
|
64
63
|
};
|
|
65
64
|
|
|
66
|
-
function runtimeConfig() {
|
|
67
|
-
const rawEnabled =
|
|
68
|
-
process.env.WORKFLOW_RUNTIME_ENABLED ??
|
|
69
|
-
process.env.HUMAN_APPROVAL_RUNTIME_ENABLED;
|
|
70
|
-
if (rawEnabled !== undefined && !['true', 'false'].includes(rawEnabled))
|
|
71
|
-
throw new Error('WORKFLOW_RUNTIME_ENABLED must be exactly true or false');
|
|
72
|
-
const enabled = rawEnabled === 'true';
|
|
73
|
-
const token = process.env.WORKFLOW_RUNTIME_TOKEN ?? '';
|
|
74
|
-
if (enabled && token.length < 32)
|
|
75
|
-
throw new Error(
|
|
76
|
-
'WORKFLOW_RUNTIME_TOKEN must contain at least 32 characters when Workflow runtime is enabled',
|
|
77
|
-
);
|
|
78
|
-
const tenantNamespace = process.env.GADMIN_TENANT_NAMESPACE ?? '';
|
|
79
|
-
if (enabled && !tenantNamespace)
|
|
80
|
-
throw new Error(
|
|
81
|
-
'GADMIN_TENANT_NAMESPACE is required when Workflow runtime is enabled',
|
|
82
|
-
);
|
|
83
|
-
return { enabled, token, tenantNamespace };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
65
|
export default () => config;
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
Post,
|
|
7
7
|
UseGuards,
|
|
8
8
|
} from '@nestjs/common';
|
|
9
|
-
import { ConfigService } from '@nestjs/config';
|
|
10
9
|
import { PrismaService } from 'nestjs-prisma';
|
|
11
10
|
import { AllowUnauthorizedRequest } from '../../lib/auth.guard';
|
|
12
11
|
import { RuntimeTokenGuard } from '../humanApproval/runtime/runtime-token.guard';
|
|
12
|
+
import { WorkflowRuntimeConfigService } from '../workflowRuntimeConfig/workflow-runtime-config.service';
|
|
13
13
|
import { CodeNodeConfig, executeCode } from './code-executor';
|
|
14
14
|
import { ExecuteCodeRuntimeDto } from './code-runtime.dto';
|
|
15
15
|
|
|
@@ -18,20 +18,13 @@ import { ExecuteCodeRuntimeDto } from './code-runtime.dto';
|
|
|
18
18
|
export class CodeRuntimeController {
|
|
19
19
|
constructor(
|
|
20
20
|
private readonly prisma: PrismaService,
|
|
21
|
-
private readonly
|
|
21
|
+
private readonly runtimeConfig: WorkflowRuntimeConfigService,
|
|
22
22
|
) {}
|
|
23
23
|
|
|
24
24
|
@Post('execute')
|
|
25
25
|
@AllowUnauthorizedRequest()
|
|
26
26
|
async execute(@Body() input: ExecuteCodeRuntimeDto) {
|
|
27
|
-
|
|
28
|
-
'humanApprovalRuntime',
|
|
29
|
-
);
|
|
30
|
-
if (input.tenantNamespace !== runtime?.tenantNamespace)
|
|
31
|
-
throw runtimeError(
|
|
32
|
-
'WORKFLOW_RUNTIME_TENANT_MISMATCH',
|
|
33
|
-
'Runtime tenant does not match this application',
|
|
34
|
-
);
|
|
27
|
+
await this.runtimeConfig.assertTenant(input.tenantNamespace);
|
|
35
28
|
|
|
36
29
|
const instanceId = parseId(input.workflowInstanceId);
|
|
37
30
|
const versionId = parseId(input.workflowVersionId);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Injectable, Module, OnModuleInit } from '@nestjs/common';
|
|
2
|
-
import { RuntimeTokenGuard } from '../humanApproval/runtime/runtime-token.guard';
|
|
3
2
|
import { WidgetCatalogService } from '../workflowConfig/widget-catalog.service';
|
|
4
3
|
import { WorkflowConfigModule } from '../workflowConfig/workflowConfig.module';
|
|
5
4
|
import { CodeRuntimeController } from './code-runtime.controller';
|
|
@@ -16,6 +15,6 @@ class CodeWidgetRegistrar implements OnModuleInit {
|
|
|
16
15
|
@Module({
|
|
17
16
|
imports: [WorkflowConfigModule],
|
|
18
17
|
controllers: [CodeRuntimeController],
|
|
19
|
-
providers: [
|
|
18
|
+
providers: [CodeWidgetRegistrar],
|
|
20
19
|
})
|
|
21
20
|
export class CodeRuntimeModule {}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ExternalCallbackController } from './external-callback.controller';
|
|
2
|
+
|
|
3
|
+
describe('ExternalCallbackController', () => {
|
|
4
|
+
it('extracts the callback token from MyOA data and submits the raw callback', async () => {
|
|
5
|
+
const interactions = {
|
|
6
|
+
submitResult: jest.fn().mockResolvedValue({ accepted: true }),
|
|
7
|
+
};
|
|
8
|
+
const controller = new ExternalCallbackController(
|
|
9
|
+
interactions as any,
|
|
10
|
+
{
|
|
11
|
+
getRequired: jest.fn().mockResolvedValue({ namespace: 'tenant-a' }),
|
|
12
|
+
} as any,
|
|
13
|
+
);
|
|
14
|
+
const body = {
|
|
15
|
+
id: 'workitem-1',
|
|
16
|
+
status: 'approved',
|
|
17
|
+
data: [
|
|
18
|
+
{ key: 'business_id', value: ['SO-1'] },
|
|
19
|
+
{ key: 'gadmin_callback_token', value: ['ext_cb_approval'] },
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
await expect(
|
|
24
|
+
controller.receive(body, { 'content-type': 'text/json' }),
|
|
25
|
+
).resolves.toEqual({ accepted: true });
|
|
26
|
+
expect(interactions.submitResult).toHaveBeenCalledWith('tenant-a', {
|
|
27
|
+
callbackToken: 'ext_cb_approval',
|
|
28
|
+
eventId: 'external:workitem-1',
|
|
29
|
+
outcome: 'RECEIVED',
|
|
30
|
+
data: { callback: body, contentType: 'text/json' },
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('rejects a callback without the one-time token', async () => {
|
|
35
|
+
const controller = new ExternalCallbackController(
|
|
36
|
+
{ submitResult: jest.fn() } as any,
|
|
37
|
+
{
|
|
38
|
+
getRequired: jest.fn().mockResolvedValue({ namespace: 'tenant-a' }),
|
|
39
|
+
} as any,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
await expect(
|
|
43
|
+
controller.receive({ id: 'workitem-1' }, {}),
|
|
44
|
+
).rejects.toMatchObject({
|
|
45
|
+
response: { code: 'EXTERNAL_CALLBACK_TOKEN_MISSING' },
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BadRequestException,
|
|
3
|
+
Body,
|
|
4
|
+
Controller,
|
|
5
|
+
Headers,
|
|
6
|
+
Post,
|
|
7
|
+
} from '@nestjs/common';
|
|
8
|
+
import { createHash } from 'node:crypto';
|
|
9
|
+
import { AllowUnauthorizedRequest } from '../../lib/auth.guard';
|
|
10
|
+
import { WorkflowRuntimeConfigService } from '../workflowRuntimeConfig/workflow-runtime-config.service';
|
|
11
|
+
import { ExternalInteractionService } from './external-interaction.service';
|
|
12
|
+
|
|
13
|
+
@Controller('workflow/external-callback')
|
|
14
|
+
export class ExternalCallbackController {
|
|
15
|
+
constructor(
|
|
16
|
+
private readonly interactions: ExternalInteractionService,
|
|
17
|
+
private readonly runtimeConfig: WorkflowRuntimeConfigService,
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
@Post()
|
|
21
|
+
@AllowUnauthorizedRequest()
|
|
22
|
+
async receive(
|
|
23
|
+
@Body() body: unknown,
|
|
24
|
+
@Headers() headers: Record<string, string | string[] | undefined>,
|
|
25
|
+
) {
|
|
26
|
+
const callbackToken = extractCallbackToken(body);
|
|
27
|
+
if (!callbackToken)
|
|
28
|
+
throw new BadRequestException({
|
|
29
|
+
code: 'EXTERNAL_CALLBACK_TOKEN_MISSING',
|
|
30
|
+
message: '回调数据中缺少 gadmin_callback_token',
|
|
31
|
+
});
|
|
32
|
+
const runtime = await this.runtimeConfig.getRequired();
|
|
33
|
+
return this.interactions.submitResult(runtime.namespace, {
|
|
34
|
+
callbackToken,
|
|
35
|
+
eventId: externalEventId(body),
|
|
36
|
+
outcome: 'RECEIVED',
|
|
37
|
+
data: {
|
|
38
|
+
callback: body,
|
|
39
|
+
contentType: headerValue(headers['content-type']),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function extractCallbackToken(value: unknown): string | undefined {
|
|
46
|
+
const body = asObject(value);
|
|
47
|
+
if (typeof body.callbackToken === 'string' && body.callbackToken.trim())
|
|
48
|
+
return body.callbackToken.trim();
|
|
49
|
+
|
|
50
|
+
const data = body.data;
|
|
51
|
+
if (Array.isArray(data)) {
|
|
52
|
+
for (const rawItem of data) {
|
|
53
|
+
const item = asObject(rawItem);
|
|
54
|
+
if (item.key !== 'gadmin_callback_token') continue;
|
|
55
|
+
const token = Array.isArray(item.value) ? item.value[0] : item.value;
|
|
56
|
+
if (typeof token === 'string' && token.trim()) return token.trim();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const dataObject = asObject(data);
|
|
60
|
+
const nested = dataObject.gadmin_callback_token;
|
|
61
|
+
if (typeof nested === 'string' && nested.trim()) return nested.trim();
|
|
62
|
+
if (Array.isArray(nested) && typeof nested[0] === 'string')
|
|
63
|
+
return nested[0].trim() || undefined;
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function externalEventId(body: unknown): string {
|
|
68
|
+
const value = asObject(body);
|
|
69
|
+
for (const key of [
|
|
70
|
+
'eventId',
|
|
71
|
+
'event_id',
|
|
72
|
+
'id',
|
|
73
|
+
'workitemId',
|
|
74
|
+
'workitem_id',
|
|
75
|
+
]) {
|
|
76
|
+
const candidate = value[key];
|
|
77
|
+
if (
|
|
78
|
+
(typeof candidate === 'string' || typeof candidate === 'number') &&
|
|
79
|
+
String(candidate).trim()
|
|
80
|
+
)
|
|
81
|
+
return `external:${String(candidate).trim()}`;
|
|
82
|
+
}
|
|
83
|
+
return `external:${createHash('sha256')
|
|
84
|
+
.update(JSON.stringify(body ?? null))
|
|
85
|
+
.digest('hex')}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function headerValue(value: string | string[] | undefined): string | undefined {
|
|
89
|
+
return Array.isArray(value) ? value[0] : value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function asObject(value: unknown): Record<string, any> {
|
|
93
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
94
|
+
? (value as Record<string, any>)
|
|
95
|
+
: {};
|
|
96
|
+
}
|