@gadmin2n/schematics 0.0.135 → 0.0.137

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 (95) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.server +2 -2
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/compose-ctl.sh +1 -1
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/canvas.prisma +1 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +14 -3
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/User.ts +4 -4
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.md +2 -2
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.yml +13 -92
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/readme.md +105 -7
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/server/ensure-database.js +1 -1
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +7 -6
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma.config.ts +9 -1
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/gen-jwt.ts +85 -0
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/notify-dev-servers.js +9 -8
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/wait-for-nest-ready.js +5 -5
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +43 -2
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +9 -1
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflows.ts +24 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas-export.dto.ts +27 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas-export.service.spec.ts +430 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas-export.service.ts +203 -0
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.controller.ts +36 -3
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.module.ts +2 -1
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.spec.ts +54 -0
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.ts +245 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.module.ts +10 -0
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.service.spec.ts +366 -0
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.service.ts +110 -0
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-execution.dto.ts +19 -1
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +15 -0
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +79 -0
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +41 -19
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/start-prod.sh +5 -5
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +37 -573
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/web/ecosystem.dev.config.cjs +12 -1
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +1 -0
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/header.tsx +3 -2
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +39 -26
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/i18n.ts +3 -1
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +3 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +3 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasEditPage.tsx +23 -5
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +66 -10
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +16 -7
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +21 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasApi.ts +30 -0
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/ExportModal.tsx +84 -0
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/ImportModal.tsx +166 -0
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/hooks/useCanvasHistory.ts +52 -13
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/DslView.tsx +6 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +6 -1
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +17 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +18 -6
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +225 -0
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/utilities/lazy-with-reload.ts +31 -0
  55. package/package.json +1 -1
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-dsl-validate.spec.ts +0 -220
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-dsl-validate.ts +0 -132
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/TODO.md +0 -152
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/config/development-sql.yaml +0 -5
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/docker-compose.yml +0 -25
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/package.json +0 -13
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/.dockerignore +0 -12
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/.env +0 -7
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/Dockerfile +0 -79
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/GRACEFUL-DEPLOYMENT.md +0 -270
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/SANDBOX.md +0 -122
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/package-lock.json +0 -4285
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/package.json +0 -28
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/code-execute.ts +0 -51
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/db-execute.ts +0 -85
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/db-query.ts +0 -35
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/http-request.ts +0 -54
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/index.ts +0 -6
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/reporting.ts +0 -85
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/send-notification.ts +0 -47
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/config.ts +0 -13
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/condition.ts +0 -101
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/context.ts +0 -58
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/graph.ts +0 -184
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/helpers.ts +0 -133
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/node-types.ts +0 -96
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/types.ts +0 -77
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/validate.ts +0 -109
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/index.ts +0 -101
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/outbox-poller.ts +0 -382
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/activities/code-execute.test.ts +0 -44
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/activities/http-request.test.ts +0 -87
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/cron-trigger-workflow.test.ts +0 -20
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/helpers.test.ts +0 -225
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/node-type-consistency.test.ts +0 -101
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/validate.test.ts +0 -205
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/workflows/dsl-workflow.ts +0 -499
  93. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/tsconfig.json +0 -19
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/vitest.config.ts +0 -8
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/yarn.lock +0 -1905
@@ -0,0 +1,110 @@
1
+ import { Inject, Injectable } from '@nestjs/common';
2
+ import { Prisma, TemporalTenantConfig } from '@prisma/client';
3
+ import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
4
+ import { PrismaService } from 'nestjs-prisma';
5
+ import { Logger } from 'winston';
6
+
7
+ import { ConfigService } from '@nestjs/config';
8
+
9
+ @Injectable()
10
+ export class TemporalTenantConfigService {
11
+ constructor(
12
+ private prisma: PrismaService,
13
+ private readonly config: ConfigService,
14
+ @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
15
+ ) {}
16
+
17
+ async createOne(
18
+ temporalTenantConfigCreateArgs: Prisma.TemporalTenantConfigCreateArgs,
19
+ ) {
20
+ return await this.prisma.temporalTenantConfig.create(
21
+ temporalTenantConfigCreateArgs,
22
+ );
23
+ }
24
+
25
+ async createMany(data: Prisma.TemporalTenantConfigCreateManyArgs['data']) {
26
+ return await this.prisma.temporalTenantConfig.createMany({
27
+ data,
28
+ skipDuplicates: true,
29
+ });
30
+ }
31
+
32
+ async findMany(
33
+ temporalTenantConfigFindManyArgs: Prisma.TemporalTenantConfigFindManyArgs,
34
+ ): Promise<{ itemCount: number; entities: TemporalTenantConfig[] }> {
35
+ const itemCount = await this.prisma.temporalTenantConfig.count({
36
+ where: temporalTenantConfigFindManyArgs.where,
37
+ });
38
+ const entities = await this.prisma.temporalTenantConfig.findMany(
39
+ temporalTenantConfigFindManyArgs,
40
+ );
41
+ return { itemCount, entities };
42
+ }
43
+
44
+ async findUnique(
45
+ id: number,
46
+ select: Prisma.TemporalTenantConfigFindUniqueArgs['select'],
47
+ ) {
48
+ return await this.prisma.temporalTenantConfig.findUnique({
49
+ where: { id: id },
50
+ select,
51
+ });
52
+ }
53
+
54
+ async updateMany(
55
+ temporalTenantConfigUpdateManyArgs: Prisma.TemporalTenantConfigUpdateManyArgs,
56
+ ) {
57
+ return await this.prisma.temporalTenantConfig.updateMany(
58
+ temporalTenantConfigUpdateManyArgs,
59
+ );
60
+ }
61
+
62
+ async updateUnique(
63
+ id: number,
64
+ data: Prisma.TemporalTenantConfigUpdateManyArgs['data'],
65
+ ) {
66
+ return await this.prisma.temporalTenantConfig.update({
67
+ where: { id: id },
68
+ data,
69
+ });
70
+ }
71
+
72
+ async deleteMany(
73
+ temporalTenantConfigDeleteManyArgs: Prisma.TemporalTenantConfigDeleteManyArgs,
74
+ ) {
75
+ return await this.prisma.temporalTenantConfig.deleteMany(
76
+ temporalTenantConfigDeleteManyArgs,
77
+ );
78
+ }
79
+
80
+ async deleteUnique(id: number) {
81
+ await this.prisma.temporalTenantConfig.delete({
82
+ where: { id: id },
83
+ });
84
+ return { count: 1 };
85
+ }
86
+
87
+ async count(
88
+ temporalTenantConfigCountArgs?: Prisma.TemporalTenantConfigCountArgs,
89
+ ) {
90
+ return await this.prisma.temporalTenantConfig.count(
91
+ temporalTenantConfigCountArgs,
92
+ );
93
+ }
94
+
95
+ async groupBy(
96
+ temporalTenantConfigGroupByArgs: Prisma.TemporalTenantConfigGroupByArgs,
97
+ ) {
98
+ return await this.prisma.temporalTenantConfig.groupBy(
99
+ temporalTenantConfigGroupByArgs as any,
100
+ );
101
+ }
102
+
103
+ async aggregate(
104
+ temporalTenantConfigAggregateArgs: Prisma.TemporalTenantConfigAggregateArgs,
105
+ ) {
106
+ return await this.prisma.temporalTenantConfig.aggregate(
107
+ temporalTenantConfigAggregateArgs,
108
+ );
109
+ }
110
+ }
@@ -1,4 +1,10 @@
1
- import { IsInt, IsObject, IsOptional, IsString } from 'class-validator';
1
+ import {
2
+ IsBoolean,
3
+ IsInt,
4
+ IsObject,
5
+ IsOptional,
6
+ IsString,
7
+ } from 'class-validator';
2
8
 
3
9
  export class ExecuteWorkflowDto {
4
10
  @IsOptional()
@@ -17,3 +23,15 @@ export class SignalWorkflowDto {
17
23
  @IsObject()
18
24
  payload: Record<string, any>;
19
25
  }
26
+
27
+ export class ApproveInstanceDto {
28
+ @IsString()
29
+ nodeId: string;
30
+
31
+ @IsBoolean()
32
+ approved: boolean;
33
+
34
+ @IsOptional()
35
+ @IsString()
36
+ comment?: string;
37
+ }
@@ -20,6 +20,7 @@ import { Request } from 'express';
20
20
  import AuthGuard, { AllowUnauthorizedRequest } from '../../lib/auth.guard';
21
21
  import { TemporalService } from './temporal.service';
22
22
  import {
23
+ ApproveInstanceDto,
23
24
  ExecuteWorkflowDto,
24
25
  SignalWorkflowDto,
25
26
  } from './workflow-execution.dto';
@@ -222,6 +223,20 @@ export class WorkflowController {
222
223
  );
223
224
  }
224
225
 
226
+ @Post('instance/:instanceId/approve')
227
+ async approveInstance(
228
+ @Param('instanceId', ParseIntPipe) instanceId: number,
229
+ @Body() dto: ApproveInstanceDto,
230
+ @Req() req,
231
+ ) {
232
+ return this.workflowService.approveInstance(
233
+ BigInt(instanceId),
234
+ dto,
235
+ req.user.userid,
236
+ this.temporalService,
237
+ );
238
+ }
239
+
225
240
  /**
226
241
  * Public webhook entry point.
227
242
  * Triggers any published workflow with a webhook_trigger node matching (path, method).
@@ -501,3 +501,82 @@ describe('WorkflowService — receiveWebhook', () => {
501
501
  ).rejects.toBeInstanceOf(UnauthorizedException);
502
502
  });
503
503
  });
504
+
505
+ describe('WorkflowService — approveInstance', () => {
506
+ let service: WorkflowService;
507
+ let prisma: any;
508
+ let temporal: any;
509
+
510
+ beforeEach(async () => {
511
+ prisma = {
512
+ workflowInstance: { findUnique: jest.fn() },
513
+ workflowNodeExecution: { findUnique: jest.fn() },
514
+ };
515
+ temporal = { signalWorkflow: jest.fn() };
516
+ const module: TestingModule = await Test.createTestingModule({
517
+ providers: [
518
+ WorkflowService,
519
+ { provide: PrismaService, useValue: prisma },
520
+ { provide: TemporalService, useValue: temporal },
521
+ ],
522
+ }).compile();
523
+ service = module.get(WorkflowService);
524
+ });
525
+
526
+ it('rejects when caller is not in the resolved approver list', async () => {
527
+ prisma.workflowInstance.findUnique.mockResolvedValue({
528
+ id: BigInt(1),
529
+ temporalRunId: 'run-1',
530
+ });
531
+ prisma.workflowNodeExecution.findUnique.mockResolvedValue({
532
+ input: { approvers: ['alice'] },
533
+ });
534
+ await expect(
535
+ service.approveInstance(
536
+ BigInt(1),
537
+ { nodeId: 'n1', approved: true },
538
+ 'bob',
539
+ temporal,
540
+ ),
541
+ ).rejects.toThrow('not an approver');
542
+ expect(temporal.signalWorkflow).not.toHaveBeenCalled();
543
+ });
544
+
545
+ it('forwards signal with real approverId when caller is authorized', async () => {
546
+ prisma.workflowInstance.findUnique.mockResolvedValue({
547
+ id: BigInt(1),
548
+ temporalRunId: 'run-1',
549
+ });
550
+ prisma.workflowNodeExecution.findUnique.mockResolvedValue({
551
+ input: { approvers: ['alice'] },
552
+ });
553
+ await service.approveInstance(
554
+ BigInt(1),
555
+ { nodeId: 'n1', approved: true, comment: 'ok' },
556
+ 'alice',
557
+ temporal,
558
+ );
559
+ expect(temporal.signalWorkflow).toHaveBeenCalledWith('run-1', 'approval', {
560
+ nodeId: 'n1',
561
+ approved: true,
562
+ approvedBy: 'alice',
563
+ comment: 'ok',
564
+ });
565
+ });
566
+
567
+ it('throws NotFoundException when no node execution exists for nodeId', async () => {
568
+ prisma.workflowInstance.findUnique.mockResolvedValue({
569
+ id: BigInt(1),
570
+ temporalRunId: 'run-1',
571
+ });
572
+ prisma.workflowNodeExecution.findUnique.mockResolvedValue(null);
573
+ await expect(
574
+ service.approveInstance(
575
+ BigInt(1),
576
+ { nodeId: 'missing', approved: true },
577
+ 'alice',
578
+ temporal,
579
+ ),
580
+ ).rejects.toThrow('Node execution not found');
581
+ });
582
+ });
@@ -1,3 +1,4 @@
1
+ import { validateDslHandles } from '@gadmin2n/nest-common/workflow-dsl';
1
2
  import {
2
3
  BadRequestException,
3
4
  Injectable,
@@ -10,10 +11,6 @@ import { PrismaService } from 'nestjs-prisma';
10
11
  import { validateDslGraph } from './dsl-validate.util';
11
12
  import { TemporalService } from './temporal.service';
12
13
  import { verifyWebhookSignature } from './webhook-signature.util';
13
- import {
14
- validateDslHandles,
15
- type NodeOutputsMap,
16
- } from './workflow-dsl-validate';
17
14
 
18
15
  @Injectable()
19
16
  export class WorkflowService {
@@ -289,21 +286,11 @@ export class WorkflowService {
289
286
  throw new BadRequestException(`Invalid DSL: ${err?.message ?? err}`);
290
287
  }
291
288
 
292
- // Per-edge sourceHandle validation against node-type outputs declared in the
293
- // database (t_workflow_node_type.outputs). Hard-fail with a structured error
294
- // list so the frontend can highlight invalid edges.
295
- const nodeTypes = await this.prisma.workflowNodeType.findMany({
296
- select: { type: true, outputs: true },
297
- });
298
- const outputsMap: NodeOutputsMap = {};
299
- for (const nt of nodeTypes) {
300
- // Pass through: null = dynamic, [] = single anonymous, [..] = enumerated
301
- outputsMap[nt.type] =
302
- nt.outputs === undefined
303
- ? []
304
- : (nt.outputs as unknown as string[] | null);
305
- }
306
- const handleErrors = validateDslHandles(dslToPublish as any, outputsMap);
289
+ // Per-edge sourceHandle validation against node-type outputs declared in
290
+ // @gadmin2n/nest-common (NODE_TYPE_META). nest-common is the single source
291
+ // of truth for handle metadata; the seeded t_workflow_node_type.outputs
292
+ // column mirrors it (verified at seed time in workflow-node-types.ts).
293
+ const handleErrors = validateDslHandles(dslToPublish as any);
307
294
  if (handleErrors.length > 0) {
308
295
  throw new BadRequestException({
309
296
  message: 'DSL validation failed: invalid edge sourceHandle(s)',
@@ -786,4 +773,39 @@ export class WorkflowService {
786
773
  );
787
774
  return { success: true };
788
775
  }
776
+
777
+ async approveInstance(
778
+ instanceId: bigint,
779
+ dto: { nodeId: string; approved: boolean; comment?: string },
780
+ approverId: string,
781
+ temporalService: any,
782
+ ) {
783
+ const instance = await this.prisma.workflowInstance.findUnique({
784
+ where: { id: instanceId },
785
+ });
786
+ if (!instance) throw new NotFoundException('Instance not found');
787
+ if (!instance.temporalRunId) {
788
+ throw new Error('Instance has no Temporal run ID');
789
+ }
790
+
791
+ const nodeExecution = await this.prisma.workflowNodeExecution.findUnique({
792
+ where: { instanceId_nodeId: { instanceId, nodeId: dto.nodeId } },
793
+ });
794
+ if (!nodeExecution) throw new NotFoundException('Node execution not found');
795
+
796
+ const approvers: string[] = (nodeExecution.input as any)?.approvers || [];
797
+ if (!approvers.includes(approverId)) {
798
+ throw new UnauthorizedException(
799
+ `${approverId} is not an approver for this node`,
800
+ );
801
+ }
802
+
803
+ await temporalService.signalWorkflow(instance.temporalRunId, 'approval', {
804
+ nodeId: dto.nodeId,
805
+ approved: dto.approved,
806
+ approvedBy: approverId,
807
+ comment: dto.comment,
808
+ });
809
+ return { success: true };
810
+ }
789
811
  }
@@ -24,7 +24,7 @@ fi
24
24
 
25
25
  echo "原始 DATABASE_URL: ${DATABASE_URL}"
26
26
 
27
- # 确保目标数据库已存在;prisma db push 只建 schema/表,不会建 database 本身。
27
+ # 确保目标数据库已存在;migrate:deploy 只建 schema/表,不会建 database 本身。
28
28
  # 若部署环境已通过 DBA/init 流程保证 DB 存在,可设 SKIP_ENSURE_DATABASE=1 跳过。
29
29
  node ensure-database.js || { echo "ensure-database 失败"; exit 1; }
30
30
 
@@ -73,7 +73,7 @@ if [ "$DEPLOY_PREFIX_MATCH" -eq 1 ]; then
73
73
  echo "更新后的 DATABASE_URL: ${DATABASE_URL}"
74
74
  export DATABASE_URL="${DATABASE_URL}"
75
75
 
76
- yarn push:db || { echo "同步表结构失败"; exit 1; }
76
+ yarn migrate:deploy || { echo "同步表结构失败"; exit 1; }
77
77
 
78
78
  # 设置迁移脚本的源和目标 schema
79
79
  # 源 schema 为 public(生产数据),目标 schema 为分支 schema
@@ -135,11 +135,11 @@ else
135
135
  NEEDS_INIT=$(node -e "const{PrismaClient}=require('@prisma/client');const p=new PrismaClient();p.\$queryRawUnsafe('SELECT COUNT(*)::int AS c FROM t_role').then(r=>{process.stdout.write(Number(r[0].c)>0?'no':'yes');return p.\$disconnect();}).catch(()=>{process.stdout.write('yes');return p.\$disconnect().catch(()=>{});});" 2>/dev/null || echo "yes")
136
136
 
137
137
  if [ "$NEEDS_INIT" != "no" ]; then
138
- echo "权限表缺失或无数据(NEEDS_INIT=${NEEDS_INIT}),执行 push:db + seed ..."
139
- yarn push:db || { echo "同步表结构失败"; exit 1; }
138
+ echo "权限表缺失或无数据(NEEDS_INIT=${NEEDS_INIT}),执行 migrate:deploy + seed ..."
139
+ yarn migrate:deploy || { echo "同步表结构失败"; exit 1; }
140
140
  yarn seed || { echo "seed 失败"; exit 1; }
141
141
  else
142
- echo "权限数据已就绪,跳过 push:db 与 seed"
142
+ echo "权限数据已就绪,跳过 migrate:deploy 与 seed"
143
143
  fi
144
144
  fi
145
145