@gadmin2n/schematics 0.0.150 → 0.0.152

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.
Files changed (82) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/CODEBUDDY.md +125 -36
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +42 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/gitignore +2 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.seed-timestamp +0 -0
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +2 -0
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/check-dev-env.js +218 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/page-manage.ts +78 -12
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +37 -67
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/index.ts +0 -17
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.controller.ts +46 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.controller.ts +60 -1
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-callback.controller.ts +3 -0
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.controller.ts +9 -1
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.ts +55 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.module.ts +2 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/page-transfer.service.ts +491 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/perm-transfer.dto.ts +213 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/perm-transfer.module.ts +28 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/resource-transfer.service.ts +137 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/role-transfer.service.ts +248 -0
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/transfer-token.service.ts +65 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/user-transfer.service.ts +146 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.ts +55 -0
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.module.ts +2 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.ts +55 -0
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.module.ts +2 -0
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/providers/user-group.provider.spec.ts +46 -0
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/providers/user-group.provider.ts +3 -2
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.ts +55 -0
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.module.ts +2 -0
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-cancellation.spec.ts +1 -0
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +96 -14
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.ts +41 -41
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-database-function.spec.ts +36 -0
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-database-function.ts +90 -0
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-catalog.spec.ts +4 -0
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-trigger.pg.spec.ts +1 -0
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-execution-idempotency.spec.ts +115 -63
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-outbox.service.spec.ts +166 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-outbox.service.ts +217 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-runtime.controller.spec.ts +33 -0
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-runtime.controller.ts +57 -0
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle.service.spec.ts +196 -0
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle.service.ts +125 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-node-type.controller.ts +9 -3
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +148 -4
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.module.ts +12 -1
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +14 -0
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +75 -45
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/node-config-validator.service.spec.ts +25 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/TransferExportModal.tsx +115 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/TransferImportModal.tsx +257 -0
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +0 -2
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +40 -0
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +15 -13
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +40 -0
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +15 -13
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts +2 -1
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/list.tsx +89 -65
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +51 -127
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/permissionReadme/index.tsx +53 -109
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +45 -41
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +51 -72
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +49 -40
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.test.tsx +16 -5
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ReadonlyNodePanel.test.tsx +28 -10
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.test.tsx +60 -1
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +94 -12
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-instance-show.css +13 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/default-widgets.tsx +3 -3
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/show.tsx +8 -1
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/DbExecuteConfigEditor.tsx +167 -94
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.tsx +95 -95
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerLifecycleConfigEditor.test.tsx +81 -0
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerLifecycleConfigEditor.tsx +126 -0
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SimplifiedNodeEditors.test.tsx +48 -52
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/forEachConnection.ts +2 -2
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/newNodeConfig.ts +1 -3
  79. package/package.json +1 -1
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/CODEBUDDY.local.md +0 -8
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/ac.config.ts +0 -24
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/SqlModal.tsx +0 -495
@@ -0,0 +1,125 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { Prisma, WorkflowInstanceStatus } from '@prisma/client';
3
+ import { PrismaService } from 'nestjs-prisma';
4
+
5
+ export interface MarkWorkflowInstanceFailedInput {
6
+ instanceId: bigint;
7
+ expectedStatuses: WorkflowInstanceStatus[];
8
+ temporalRunId?: string | null;
9
+ result: Prisma.InputJsonValue;
10
+ startedAt?: Date;
11
+ finishedAt?: Date;
12
+ }
13
+
14
+ export interface WorkflowFailureCallback {
15
+ connectionRef: string;
16
+ path: string;
17
+ }
18
+
19
+ @Injectable()
20
+ export class WorkflowLifecycleService {
21
+ constructor(private readonly prisma: PrismaService) {}
22
+
23
+ async markFailed(input: MarkWorkflowInstanceFailedInput): Promise<boolean> {
24
+ return this.prisma.$transaction(async (tx) => {
25
+ const failedAt = input.finishedAt ?? new Date();
26
+ const updated = await tx.workflowInstance.updateMany({
27
+ where: {
28
+ id: input.instanceId,
29
+ status: { in: input.expectedStatuses },
30
+ ...(input.temporalRunId !== undefined && {
31
+ temporalRunId: input.temporalRunId,
32
+ }),
33
+ },
34
+ data: {
35
+ status: 'FAILED',
36
+ result: input.result,
37
+ ...(input.startedAt && { startedAt: input.startedAt }),
38
+ finishedAt: failedAt,
39
+ },
40
+ });
41
+ if (updated.count !== 1) return false;
42
+
43
+ const instance = await tx.workflowInstance.findUniqueOrThrow({
44
+ where: { id: input.instanceId },
45
+ select: {
46
+ versionId: true,
47
+ sourceOutboxId: true,
48
+ workflow: { select: { code: true } },
49
+ },
50
+ });
51
+ if (instance.sourceOutboxId === null) return true;
52
+
53
+ const version = await tx.workflowVersion.findUniqueOrThrow({
54
+ where: { id: instance.versionId },
55
+ select: { dsl: true },
56
+ });
57
+ const callback = extractWorkflowFailureCallback(version.dsl);
58
+ if (!callback) return true;
59
+
60
+ const source = await tx.workflowEventOutbox.findUnique({
61
+ where: { id: instance.sourceOutboxId },
62
+ select: {
63
+ schemaName: true,
64
+ tableName: true,
65
+ operation: true,
66
+ primaryKey: true,
67
+ },
68
+ });
69
+ if (!source)
70
+ throw new Error(
71
+ `Workflow event ${instance.sourceOutboxId} was not found for lifecycle notification`,
72
+ );
73
+
74
+ const eventKey = `workflow-instance:${input.instanceId}:FAILED`;
75
+ await tx.workflowLifecycleOutbox.upsert({
76
+ where: { eventKey },
77
+ create: {
78
+ eventKey,
79
+ workflowInstanceId: input.instanceId,
80
+ eventType: 'FAILED',
81
+ workflowCode: instance.workflow.code,
82
+ businessReference: {
83
+ schema: source.schemaName,
84
+ table: source.tableName,
85
+ operation: source.operation,
86
+ primaryKey: source.primaryKey,
87
+ },
88
+ connectionRef: callback.connectionRef,
89
+ callbackPath: callback.path,
90
+ failedAt,
91
+ },
92
+ update: {},
93
+ });
94
+ return true;
95
+ });
96
+ }
97
+ }
98
+
99
+ export function extractWorkflowFailureCallback(
100
+ dsl: unknown,
101
+ ): WorkflowFailureCallback | null {
102
+ const nodes = asRecord(dsl).nodes;
103
+ if (!Array.isArray(nodes)) return null;
104
+ const trigger = nodes
105
+ .map(asRecord)
106
+ .find((node) => node.type === 'event_trigger');
107
+ if (!trigger) return null;
108
+ const config = asRecord(trigger.config);
109
+ if (config.failureCallback === undefined) return null;
110
+ const callback = asRecord(config.failureCallback);
111
+ const connectionRef =
112
+ typeof callback.connectionRef === 'string'
113
+ ? callback.connectionRef.trim()
114
+ : '';
115
+ const path = typeof callback.path === 'string' ? callback.path.trim() : '';
116
+ if (!connectionRef || !path.startsWith('/'))
117
+ throw new Error('WORKFLOW_FAILURE_CALLBACK_CONFIG_INVALID');
118
+ return { connectionRef, path };
119
+ }
120
+
121
+ function asRecord(value: unknown): Record<string, any> {
122
+ return value && typeof value === 'object' && !Array.isArray(value)
123
+ ? (value as Record<string, any>)
124
+ : {};
125
+ }
@@ -1,15 +1,21 @@
1
+ import { ACGuard } from '@gadmin2n/nest-common';
1
2
  import { Controller, Get, UseGuards } from '@nestjs/common';
2
3
  import { ApiTags } from '@nestjs/swagger';
3
- import AuthGuard from '../../lib/auth.guard';
4
+ import { UseRoles } from 'nest-access-control';
4
5
  import { WorkflowNodeTypeService } from './workflow-node-type.service';
5
6
 
6
- @ApiTags('workflow-node-types')
7
- @UseGuards(AuthGuard)
7
+ @UseGuards(ACGuard)
8
8
  @Controller('workflow-node-types')
9
+ @ApiTags('workflow-node-types')
9
10
  export class WorkflowNodeTypeController {
10
11
  constructor(private readonly service: WorkflowNodeTypeService) {}
11
12
 
12
13
  @Get()
14
+ @UseRoles({
15
+ resource: 'workflow',
16
+ action: 'read',
17
+ possession: 'any',
18
+ })
13
19
  findAll() {
14
20
  return this.service.findAll();
15
21
  }
@@ -22,6 +22,7 @@ import { UseRoles } from 'nest-access-control';
22
22
  import { PrismaService } from 'nestjs-prisma';
23
23
  import { AllowUnauthorizedRequest } from '../../lib/auth.guard';
24
24
  import { TemporalService } from './temporal.service';
25
+ import { listWorkflowDatabaseFunctions } from './workflow-database-function';
25
26
  import {
26
27
  ApproveInstanceDto,
27
28
  ExecuteWorkflowDto,
@@ -43,8 +44,9 @@ import {
43
44
  } from './workflow.dto';
44
45
  import { WorkflowService } from './workflow.service';
45
46
 
46
- @ApiTags('workflow')
47
+ @UseGuards(ACGuard)
47
48
  @Controller('workflow')
49
+ @ApiTags('workflow')
48
50
  export class WorkflowController {
49
51
  constructor(
50
52
  private readonly workflowService: WorkflowService,
@@ -55,6 +57,11 @@ export class WorkflowController {
55
57
  ) {}
56
58
 
57
59
  @Get()
60
+ @UseRoles({
61
+ resource: 'workflow',
62
+ action: 'read',
63
+ possession: 'any',
64
+ })
58
65
  findAll(@Query() query: WorkflowListQueryDto) {
59
66
  return this.workflowService.findMany({
60
67
  status: query.status,
@@ -66,7 +73,6 @@ export class WorkflowController {
66
73
  }
67
74
 
68
75
  @Post('event-catalog/register')
69
- @UseGuards(ACGuard)
70
76
  @UseRoles({
71
77
  resource: 'workflowEventRegistration',
72
78
  action: 'create',
@@ -77,31 +83,71 @@ export class WorkflowController {
77
83
  }
78
84
 
79
85
  @Get('overview')
86
+ @UseRoles({
87
+ resource: 'workflow',
88
+ action: 'read',
89
+ possession: 'any',
90
+ })
80
91
  getOverview() {
81
92
  return this.workflowService.getOverview();
82
93
  }
83
94
 
84
95
  @Get('callable')
96
+ @UseRoles({
97
+ resource: 'workflow',
98
+ action: 'read',
99
+ possession: 'any',
100
+ })
85
101
  findCallable() {
86
102
  return this.workflowService.findCallableWorkflows();
87
103
  }
88
104
 
89
105
  @Get('external-interaction-options')
106
+ @UseRoles({
107
+ resource: 'workflow',
108
+ action: 'read',
109
+ possession: 'any',
110
+ })
90
111
  findExternalInteractionOptions() {
91
112
  return this.workflowService.findExternalInteractionOptions();
92
113
  }
93
114
 
115
+ @Get('database-functions')
116
+ @UseRoles({
117
+ resource: 'workflow',
118
+ action: 'read',
119
+ possession: 'any',
120
+ })
121
+ findDatabaseFunctions() {
122
+ return listWorkflowDatabaseFunctions(this.prisma);
123
+ }
124
+
94
125
  @Get('event-catalog/overview')
126
+ @UseRoles({
127
+ resource: 'workflowEventRegistration',
128
+ action: 'read',
129
+ possession: 'any',
130
+ })
95
131
  findBusinessEventCatalogOverview() {
96
132
  return this.workflowService.findBusinessEventCatalogOverview();
97
133
  }
98
134
 
99
135
  @Get('event-catalog/sources')
136
+ @UseRoles({
137
+ resource: 'workflowEventRegistration',
138
+ action: 'read',
139
+ possession: 'any',
140
+ })
100
141
  findBusinessEventSources() {
101
142
  return this.workflowService.findBusinessEventSources();
102
143
  }
103
144
 
104
145
  @Get('event-catalog/:registrationCode/instances')
146
+ @UseRoles({
147
+ resource: 'workflowEventRegistration',
148
+ action: 'read',
149
+ possession: 'any',
150
+ })
105
151
  findBusinessEventInstances(
106
152
  @Param('registrationCode') registrationCode: string,
107
153
  @Query() query: WorkflowInstanceListQueryDto,
@@ -114,7 +160,6 @@ export class WorkflowController {
114
160
  }
115
161
 
116
162
  @Post('event-catalog/unregister')
117
- @UseGuards(ACGuard)
118
163
  @UseRoles({
119
164
  resource: 'workflowEventRegistration',
120
165
  action: 'delete',
@@ -125,6 +170,11 @@ export class WorkflowController {
125
170
  }
126
171
 
127
172
  @Get('event-catalog')
173
+ @UseRoles({
174
+ resource: 'workflowEventRegistration',
175
+ action: 'read',
176
+ possession: 'any',
177
+ })
128
178
  findBusinessEventCatalog() {
129
179
  return this.workflowService.findBusinessEventCatalog();
130
180
  }
@@ -132,6 +182,11 @@ export class WorkflowController {
132
182
  // ─── Export/Import Endpoints ──────────────────────────────────────────────────
133
183
 
134
184
  @Post(':id/export-token')
185
+ @UseRoles({
186
+ resource: 'workflow',
187
+ action: 'read',
188
+ possession: 'any',
189
+ })
135
190
  async generateExportToken(@Param('id', ParseIntPipe) id: number, @Req() req) {
136
191
  const baseUrl = `${req.protocol}://${req.get('host')}/api`;
137
192
  return this.workflowExportService.generateExportToken(BigInt(id), baseUrl);
@@ -150,11 +205,21 @@ export class WorkflowController {
150
205
  }
151
206
 
152
207
  @Post('import-preview')
208
+ @UseRoles({
209
+ resource: 'workflow',
210
+ action: 'update',
211
+ possession: 'any',
212
+ })
153
213
  async importPreview(@Body() dto: ImportPayloadDto) {
154
214
  return this.workflowExportService.importPreview(dto.payload);
155
215
  }
156
216
 
157
217
  @Post('import-confirm')
218
+ @UseRoles({
219
+ resource: 'workflow',
220
+ action: 'update',
221
+ possession: 'any',
222
+ })
158
223
  async importConfirm(@Body() dto: ImportPayloadDto, @Req() req) {
159
224
  return this.workflowExportService.importConfirm(
160
225
  dto.payload,
@@ -163,16 +228,31 @@ export class WorkflowController {
163
228
  }
164
229
 
165
230
  @Post()
231
+ @UseRoles({
232
+ resource: 'workflow',
233
+ action: 'create',
234
+ possession: 'any',
235
+ })
166
236
  create(@Req() req, @Body() dto: CreateWorkflowDto) {
167
237
  return this.workflowService.create(dto, req.user.userid);
168
238
  }
169
239
 
170
240
  @Get(':id')
241
+ @UseRoles({
242
+ resource: 'workflow',
243
+ action: 'read',
244
+ possession: 'any',
245
+ })
171
246
  findOne(@Param('id', ParseIntPipe) id: number) {
172
247
  return this.workflowService.findOne(BigInt(id));
173
248
  }
174
249
 
175
250
  @Patch(':id')
251
+ @UseRoles({
252
+ resource: 'workflow',
253
+ action: 'update',
254
+ possession: 'any',
255
+ })
176
256
  update(
177
257
  @Param('id', ParseIntPipe) id: number,
178
258
  @Body() dto: UpdateWorkflowDto,
@@ -181,11 +261,21 @@ export class WorkflowController {
181
261
  }
182
262
 
183
263
  @Patch(':id/save')
264
+ @UseRoles({
265
+ resource: 'workflow',
266
+ action: 'update',
267
+ possession: 'any',
268
+ })
184
269
  saveDraft(@Param('id', ParseIntPipe) id: number, @Body() dto: SaveDraftDto) {
185
270
  return this.workflowService.saveDraft(BigInt(id), dto.dsl);
186
271
  }
187
272
 
188
273
  @Patch(':id/toggle')
274
+ @UseRoles({
275
+ resource: 'workflow',
276
+ action: 'update',
277
+ possession: 'any',
278
+ })
189
279
  toggleEnabled(
190
280
  @Param('id', ParseIntPipe) id: number,
191
281
  @Body() dto: ToggleEnabledDto,
@@ -194,16 +284,31 @@ export class WorkflowController {
194
284
  }
195
285
 
196
286
  @Delete(':id')
287
+ @UseRoles({
288
+ resource: 'workflow',
289
+ action: 'delete',
290
+ possession: 'any',
291
+ })
197
292
  remove(@Param('id', ParseIntPipe) id: number) {
198
293
  return this.workflowService.remove(BigInt(id));
199
294
  }
200
295
 
201
296
  @Get(':id/versions')
297
+ @UseRoles({
298
+ resource: 'workflow',
299
+ action: 'read',
300
+ possession: 'any',
301
+ })
202
302
  getVersions(@Param('id', ParseIntPipe) id: number) {
203
303
  return this.workflowService.getVersions(BigInt(id));
204
304
  }
205
305
 
206
306
  @Get(':id/versions/:version')
307
+ @UseRoles({
308
+ resource: 'workflow',
309
+ action: 'read',
310
+ possession: 'any',
311
+ })
207
312
  getVersionDsl(
208
313
  @Param('id', ParseIntPipe) id: number,
209
314
  @Param('version', ParseIntPipe) version: number,
@@ -212,6 +317,11 @@ export class WorkflowController {
212
317
  }
213
318
 
214
319
  @Post(':id/publish')
320
+ @UseRoles({
321
+ resource: 'workflow',
322
+ action: 'update',
323
+ possession: 'any',
324
+ })
215
325
  publish(
216
326
  @Param('id', ParseIntPipe) id: number,
217
327
  @Req() req,
@@ -221,6 +331,11 @@ export class WorkflowController {
221
331
  }
222
332
 
223
333
  @Get(':id/instances')
334
+ @UseRoles({
335
+ resource: 'workflowExecution',
336
+ action: 'read',
337
+ possession: 'any',
338
+ })
224
339
  getInstances(
225
340
  @Param('id', ParseIntPipe) id: number,
226
341
  @Query() query: WorkflowInstanceListQueryDto,
@@ -233,6 +348,11 @@ export class WorkflowController {
233
348
  }
234
349
 
235
350
  @Get(':id/stats')
351
+ @UseRoles({
352
+ resource: 'workflow',
353
+ action: 'read',
354
+ possession: 'any',
355
+ })
236
356
  getStats(@Param('id', ParseIntPipe) id: number) {
237
357
  return this.workflowService.getStats(BigInt(id));
238
358
  }
@@ -240,7 +360,6 @@ export class WorkflowController {
240
360
  // ─── Execution Endpoints ──────────────────────────────────────────────────────
241
361
 
242
362
  @Post('actions/:code/execute')
243
- @UseGuards(ACGuard)
244
363
  @UseRoles({
245
364
  resource: 'workflowExecution',
246
365
  action: 'create',
@@ -261,6 +380,11 @@ export class WorkflowController {
261
380
  }
262
381
 
263
382
  @Get('instance/:instanceId')
383
+ @UseRoles({
384
+ resource: 'workflowExecution',
385
+ action: 'read',
386
+ possession: 'any',
387
+ })
264
388
  getInstanceDetail(
265
389
  @Param('instanceId', ParseIntPipe) instanceId: number,
266
390
  @Req() req,
@@ -272,6 +396,11 @@ export class WorkflowController {
272
396
  }
273
397
 
274
398
  @Post('instance/:instanceId/cancel')
399
+ @UseRoles({
400
+ resource: 'workflowExecution',
401
+ action: 'update',
402
+ possession: 'any',
403
+ })
275
404
  async cancelInstance(
276
405
  @Param('instanceId', ParseIntPipe) instanceId: number,
277
406
  @Req() req,
@@ -286,6 +415,11 @@ export class WorkflowController {
286
415
  }
287
416
 
288
417
  @Post('instance/:instanceId/retry')
418
+ @UseRoles({
419
+ resource: 'workflowExecution',
420
+ action: 'update',
421
+ possession: 'any',
422
+ })
289
423
  async retryInstance(
290
424
  @Param('instanceId', ParseIntPipe) instanceId: number,
291
425
  @Req() req,
@@ -298,6 +432,11 @@ export class WorkflowController {
298
432
  }
299
433
 
300
434
  @Post('instance/:instanceId/retry-from-node')
435
+ @UseRoles({
436
+ resource: 'workflowExecution',
437
+ action: 'update',
438
+ possession: 'any',
439
+ })
301
440
  async retryInstanceFromNode(
302
441
  @Param('instanceId', ParseIntPipe) instanceId: number,
303
442
  @Body() dto: RetryFromNodeDto,
@@ -312,6 +451,11 @@ export class WorkflowController {
312
451
  }
313
452
 
314
453
  @Post('instance/:instanceId/approve')
454
+ @UseRoles({
455
+ resource: 'workflowExecution',
456
+ action: 'update',
457
+ possession: 'any',
458
+ })
315
459
  async approveInstance(
316
460
  @Param('instanceId', ParseIntPipe) instanceId: number,
317
461
  @Body() dto: ApproveInstanceDto,
@@ -1,11 +1,15 @@
1
1
  import { Module } from '@nestjs/common';
2
2
  import { HttpRequestModule } from '../httpRequest/httpRequest.module';
3
3
  import { HumanApprovalModule } from '../humanApproval/humanApproval.module';
4
+ import { IntegrationConnectionModule } from '../integrationConnection/integrationConnection.module';
4
5
  import { UserModule } from '../user/user.module';
5
6
  import { WorkflowConfigModule } from '../workflowConfig/workflowConfig.module';
6
7
  import { TemporalModule } from './temporal.module';
7
8
  import { WorkflowCompilerService } from './workflow-compiler.service';
8
9
  import { WorkflowExportService } from './workflow-export.service';
10
+ import { WorkflowLifecycleOutboxService } from './workflow-lifecycle-outbox.service';
11
+ import { WorkflowLifecycleRuntimeController } from './workflow-lifecycle-runtime.controller';
12
+ import { WorkflowLifecycleService } from './workflow-lifecycle.service';
9
13
  import { WorkflowNodeTypeController } from './workflow-node-type.controller';
10
14
  import { WorkflowNodeTypeService } from './workflow-node-type.service';
11
15
  import { WorkflowRuntimeFacade } from './workflow-runtime.facade';
@@ -18,15 +22,22 @@ import { WorkflowService } from './workflow.service';
18
22
  TemporalModule,
19
23
  HumanApprovalModule,
20
24
  HttpRequestModule,
25
+ IntegrationConnectionModule,
21
26
  WorkflowConfigModule,
22
27
  ],
23
- controllers: [WorkflowController, WorkflowNodeTypeController],
28
+ controllers: [
29
+ WorkflowController,
30
+ WorkflowNodeTypeController,
31
+ WorkflowLifecycleRuntimeController,
32
+ ],
24
33
  providers: [
25
34
  WorkflowService,
26
35
  WorkflowNodeTypeService,
27
36
  WorkflowExportService,
28
37
  WorkflowCompilerService,
29
38
  WorkflowRuntimeFacade,
39
+ WorkflowLifecycleService,
40
+ WorkflowLifecycleOutboxService,
30
41
  ],
31
42
  exports: [WorkflowService, WorkflowCompilerService, WorkflowRuntimeFacade],
32
43
  })
@@ -5,6 +5,7 @@ import { HumanApprovalService } from '../humanApproval/human-approval.service';
5
5
  import { TemporalService } from './temporal.service';
6
6
  import { signWebhookPayload } from './webhook-signature.util';
7
7
  import { WorkflowCompilerService } from './workflow-compiler.service';
8
+ import { WorkflowLifecycleService } from './workflow-lifecycle.service';
8
9
  import { WorkflowService } from './workflow.service';
9
10
 
10
11
  describe('WorkflowService — workflow list run counts', () => {
@@ -37,6 +38,7 @@ describe('WorkflowService — workflow list run counts', () => {
37
38
  {} as any,
38
39
  {} as any,
39
40
  {} as any,
41
+ {} as any,
40
42
  );
41
43
 
42
44
  const result = await service.findMany({});
@@ -152,6 +154,10 @@ describe('WorkflowService — cron lifecycle hooks', () => {
152
154
  { provide: TemporalService, useValue: temporal },
153
155
  { provide: HumanApprovalService, useValue: {} },
154
156
  { provide: WorkflowCompilerService, useValue: compiler },
157
+ {
158
+ provide: WorkflowLifecycleService,
159
+ useValue: { markFailed: jest.fn() },
160
+ },
155
161
  ],
156
162
  }).compile();
157
163
 
@@ -1563,6 +1569,10 @@ describe('WorkflowService — receiveWebhook', () => {
1563
1569
  provide: WorkflowCompilerService,
1564
1570
  useValue: { compile: jest.fn(async (dsl: any) => dsl) },
1565
1571
  },
1572
+ {
1573
+ provide: WorkflowLifecycleService,
1574
+ useValue: { markFailed: jest.fn() },
1575
+ },
1566
1576
  ],
1567
1577
  }).compile();
1568
1578
 
@@ -1815,6 +1825,10 @@ describe('WorkflowService — approveInstance', () => {
1815
1825
  { provide: TemporalService, useValue: temporal },
1816
1826
  { provide: HumanApprovalService, useValue: humanApproval },
1817
1827
  { provide: WorkflowCompilerService, useValue: {} },
1828
+ {
1829
+ provide: WorkflowLifecycleService,
1830
+ useValue: { markFailed: jest.fn() },
1831
+ },
1818
1832
  ],
1819
1833
  }).compile();
1820
1834
  service = module.get(WorkflowService);