@gadmin2n/schematics 0.0.87 → 0.0.89

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 (99) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/.dockerignore +16 -2
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.codegen +40 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.server +76 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.web +53 -0
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/Jenkinsfile +219 -33
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/compose-ctl.sh +250 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +4 -1
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/dev/postgres/init.sql +12 -0
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.md +170 -0
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.yml +254 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +8 -7
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/lib/page-helpers.ts +1 -1
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/prismaModels.ts +1 -1
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/agenda.seed.ts +39 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/audit.seed.ts +40 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/bootstrap.ts +56 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/canvas.seed.ts +39 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/{scripts/sync-data-mngt-pages.ts → seed/data-mngt.seed.ts} +36 -20
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/game.seed.ts +44 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +30 -6
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permission.seed.ts +130 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-event-trigger.ts +60 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +11 -25
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +108 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/main.ts +1 -0
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.spec.ts +31 -2
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.spec.ts +31 -2
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.spec.ts +41 -57
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.spec.ts +31 -2
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.spec.ts +309 -1
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.spec.ts +31 -2
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.spec.ts +315 -1
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.spec.ts +31 -2
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.spec.ts +312 -2
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.spec.ts +31 -2
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.spec.ts +317 -1
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.spec.ts +31 -2
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.spec.ts +309 -1
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.spec.ts +31 -2
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.spec.ts +299 -1
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.spec.ts +31 -2
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.spec.ts +307 -1
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.spec.ts +31 -2
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +309 -1
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.spec.ts +205 -0
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.ts +116 -0
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.spec.ts +158 -0
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +110 -1
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/webhook-signature.util.spec.ts +79 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/webhook-signature.util.ts +54 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +34 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +457 -0
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +241 -4
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.spec.ts +34 -2
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.spec.ts +24 -30
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.spec.ts +34 -2
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.spec.ts +36 -36
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.spec.ts +34 -2
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.spec.ts +48 -24
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +312 -3
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/TODO.md +152 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/.dockerignore +12 -0
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/Dockerfile +79 -0
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/GRACEFUL-DEPLOYMENT.md +270 -0
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/index.ts +1 -1
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/reporting.ts +23 -0
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/index.ts +70 -5
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/outbox-poller.ts +246 -90
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/cron-trigger-workflow.test.ts +20 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/workflows/dsl-workflow.ts +96 -8
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/web/nginx.conf +74 -0
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/ElementInspector.tsx +18 -0
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/promptGenerator.ts +1 -1
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/form.tsx +1 -1
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +3 -3
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +3 -3
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts +4 -1
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasEditPage.tsx +9 -0
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +156 -139
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +14 -2
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +62 -0
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/PublishModal.tsx +4 -6
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasApi.ts +18 -27
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasDefaults.ts +32 -11
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/demos.ts +48 -61
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas-page/index.tsx +3 -6
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/DslView.tsx +16 -16
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +28 -35
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +34 -3
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +1 -1
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +1 -1
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.css +6 -0
  93. package/package.json +1 -1
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile +0 -63
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/sync-resources.ts +0 -100
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +0 -302
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.controller.spec.ts +0 -20
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/sql/create-event-trigger.sql +0 -87
  99. /package/dist/lib/application/files/gadmin2-game-angle-demo/{GRACEFUL-DEPLOYMENT.md → server/GRACEFUL-DEPLOYMENT.md} +0 -0
@@ -0,0 +1,254 @@
1
+ # =============================================================================
2
+ # gadmin2 本地联调环境(完整版)
3
+ # =============================================================================
4
+ # 用途:开发者一行命令起一整套服务(PG + Temporal + Server + Worker + Web)。
5
+ # 生产部署走 K8s(Helm chart);本文件仅用于本地集成验证,不进 CI/CD。
6
+ #
7
+ # 启动流程:
8
+ # 首次 / config/ 有变更后:
9
+ # docker compose --profile codegen build codegen # 构建 codegen 中间镜像
10
+ # docker compose up -d --build # 起全套服务
11
+ # 日常 up(codegen 镜像已存在):
12
+ # docker compose up -d --build
13
+ # 停止: docker compose down
14
+ # 重置: docker compose down -v # 连数据卷一起删,下次首启会重建 schema
15
+ # 查看: docker compose logs -f <service>
16
+ #
17
+ # 端口分配(已避免冲突):
18
+ # 5432 -> PostgreSQL
19
+ # 7233 -> Temporal gRPC(Server)
20
+ # 8000 -> NestJS Server(API)
21
+ # 8080 -> Temporal Web UI
22
+ # 3000 -> Web 前端(nginx,容器内 80)
23
+ #
24
+ # 镜像构建依赖(与 Jenkinsfile 保持一致的方案 C):
25
+ # server / web 通过 ARG CODEGEN_IMAGE + FROM ${CODEGEN_IMAGE} 引用本地 codegen 镜像,
26
+ # compose 不支持 build-time 依赖图,因此 codegen 镜像必须**先于** web/server 构建。
27
+ # 配置变更(config/ui/** 或 config/prisma/**)后需 rebuild codegen,否则用旧产物。
28
+ #
29
+ # 首次启动顺序(depends_on + healthcheck 自动编排):
30
+ # 1. postgres 起来 → healthy(pg_isready)
31
+ # 2. temporal-server auto-setup 跑 schema 初始化(约 30~60s)→ healthy
32
+ # 3. server / temporal-worker / temporal-ui 起来
33
+ # 4. web 起来
34
+ #
35
+ # 提示:
36
+ # - 仅起 Temporal(不起 web/server/worker)请用 ./temporal/docker-compose.yml
37
+ # - web 是生产 nginx 镜像,不支持热更新;前端开发请直接 `cd web && yarn dev`
38
+ # =============================================================================
39
+
40
+ services:
41
+ # ---------------------------------------------------------------------------
42
+ # codegen 中间镜像(仅 build,不 run)
43
+ # ---------------------------------------------------------------------------
44
+ # 执行 `gadmin2 g prisma`,生成 web/ 与 server/ 的代码并固化到镜像内。
45
+ # web / server 服务通过 ARG CODEGEN_IMAGE 引用本镜像。
46
+ #
47
+ # 用法:
48
+ # docker compose --profile codegen build codegen # 仅构建,不启动
49
+ #
50
+ # 注意:profile 不为空 → 不会被默认的 `docker compose up` 自动启动。
51
+ codegen:
52
+ image: ${COMPOSE_PROJECT_NAME}-codegen:local
53
+ build:
54
+ context: .
55
+ dockerfile: Dockerfile.codegen
56
+ profiles: ["codegen"]
57
+ # 即使被显式启动也立即退出(保险,本服务的存在仅为 build 入口)
58
+ command: ["true"]
59
+
60
+ # ---------------------------------------------------------------------------
61
+ # PostgreSQL:业务库 + Temporal 元数据库 + Temporal visibility 库(共用同一实例)
62
+ # ---------------------------------------------------------------------------
63
+ postgres:
64
+ image: postgres:15-alpine
65
+ container_name: postgresql
66
+ restart: unless-stopped
67
+ environment:
68
+ POSTGRES_USER: ${POSTGRES_USER:-kavenma}
69
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-kavenma}
70
+ # POSTGRES_DB 设成 postgres(默认库),实际三个业务库由 init.sql 创建
71
+ POSTGRES_DB: postgres
72
+ ports:
73
+ - "5432:5432"
74
+ volumes:
75
+ - pgdata:/var/lib/postgresql/data
76
+ - ./dev/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
77
+ healthcheck:
78
+ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-kavenma} -d postgres"]
79
+ interval: 5s
80
+ timeout: 5s
81
+ retries: 10
82
+
83
+ # ---------------------------------------------------------------------------
84
+ # Temporal Server (auto-setup):自动建 schema、起 frontend/history/matching/worker
85
+ # 不开 ENABLE_ES,visibility 走 PG(与本地联调最小依赖一致)
86
+ # ---------------------------------------------------------------------------
87
+ temporal-server:
88
+ # 生产 Helm chart 1.2.0 捆绑的 Temporal Server 是 1.31.0(见 docs/temporal-server-argocd.md),
89
+ # 但 Docker Hub 上 temporalio/auto-setup 镜像目前最新只到 1.29.6.1(auto-setup tag 通常滞后于 server)。
90
+ # 本地联调用最接近的 1.29.6.1 即可,schema 与 1.31.0 兼容。
91
+ image: temporalio/auto-setup:1.29.6.1
92
+ container_name: temporal-server
93
+ restart: unless-stopped
94
+ depends_on:
95
+ postgres:
96
+ condition: service_healthy
97
+ environment:
98
+ DB: postgres12
99
+ DB_PORT: 5432
100
+ POSTGRES_SEEDS: postgres
101
+ POSTGRES_USER: ${POSTGRES_USER:-kavenma}
102
+ POSTGRES_PWD: ${POSTGRES_PASSWORD:-kavenma}
103
+ DBNAME: temporal
104
+ VISIBILITY_DBNAME: temporal_visibility
105
+ DYNAMIC_CONFIG_FILE_PATH: config/dynamicconfig/development-sql.yaml
106
+ # 显式绑 0.0.0.0:podman rootless CNI 下 server 默认只绑容器主网卡 IP(如 10.89.x.x),
107
+ # 容器内 127.0.0.1:7233 连不通 → healthcheck 永远 unhealthy → worker 不启动。
108
+ # 设成 0.0.0.0 后 docker / podman 都可达。
109
+ BIND_ON_IP: 0.0.0.0
110
+ volumes:
111
+ - ./temporal/config:/etc/temporal/config/dynamicconfig
112
+ ports:
113
+ - "7233:7233"
114
+ healthcheck:
115
+ # auto-setup 镜像自带 temporal CLI;healthcheck 在容器内执行,地址用 127.0.0.1
116
+ test: ["CMD-SHELL", "temporal operator cluster health --address 127.0.0.1:7233 || exit 1"]
117
+ interval: 10s
118
+ timeout: 5s
119
+ retries: 12
120
+ start_period: 30s
121
+
122
+ # ---------------------------------------------------------------------------
123
+ # Temporal Web UI
124
+ # ---------------------------------------------------------------------------
125
+ temporal-ui:
126
+ image: temporalio/ui:latest
127
+ container_name: temporal-ui
128
+ restart: unless-stopped
129
+ depends_on:
130
+ temporal-server:
131
+ condition: service_started
132
+ environment:
133
+ TEMPORAL_ADDRESS: temporal-server:7233
134
+ ports:
135
+ - "8080:8080"
136
+
137
+ # ---------------------------------------------------------------------------
138
+ # Server 一次性初始化:prisma db push + seed
139
+ # ---------------------------------------------------------------------------
140
+ # 直接复用 codegen 镜像(它已含 server 全量依赖:ts-node / prisma CLI / seed/ 源码)。
141
+ # 业务 server 镜像走 `--production` 安装、且不复制 seed/,因此 seed 不能在 server
142
+ # 容器里跑;这里独立成 init 容器,跑完即退出。
143
+ #
144
+ # 幂等:
145
+ # - prisma db push 多次执行无害(只补 schema 差异)
146
+ # - seed 全部用 createMany skipDuplicates / upsert 风格,重复跑不会重复插
147
+ #
148
+ # 依赖:
149
+ # - 必须先 `docker compose --profile codegen build codegen` 把镜像构出来
150
+ # - postgres 必须 healthy(schema 写入目标)
151
+ #
152
+ # 行为:
153
+ # - restart: "no" —— 一次性
154
+ # - 退出码 0 时 server / temporal-worker 才会启动(service_completed_successfully)
155
+ server-init:
156
+ image: ${COMPOSE_PROJECT_NAME}-codegen:local
157
+ container_name: gadmin2-init
158
+ restart: "no"
159
+ depends_on:
160
+ postgres:
161
+ condition: service_healthy
162
+ working_dir: /app/server
163
+ environment:
164
+ DATABASE_URL: postgresql://${POSTGRES_USER:-kavenma}:${POSTGRES_PASSWORD:-kavenma}@postgres:5432/gadmin_demo?schema=public
165
+ command:
166
+ - sh
167
+ - -c
168
+ - |
169
+ set -e
170
+ echo "[server-init] prisma db push ..."
171
+ yarn push:db
172
+ echo "[server-init] seed ..."
173
+ yarn seed
174
+ echo "[server-init] done."
175
+
176
+ # ---------------------------------------------------------------------------
177
+ # NestJS Server(业务 API)
178
+ # 镜像构建依赖 codegen:先 `docker compose --profile codegen build codegen`
179
+ # 镜像内已 COPY server/.env,TAIHU_* / OTEL_* 等用其默认值;
180
+ # 这里只覆盖 docker 网络相关的连接字符串。
181
+ # ---------------------------------------------------------------------------
182
+ server:
183
+ image: ${COMPOSE_PROJECT_NAME}-nest-server:local
184
+ build:
185
+ context: .
186
+ dockerfile: Dockerfile.server
187
+ args:
188
+ CODEGEN_IMAGE: ${COMPOSE_PROJECT_NAME}-codegen:local
189
+ container_name: gadmin2-nest-server
190
+ restart: unless-stopped
191
+ depends_on:
192
+ postgres:
193
+ condition: service_healthy
194
+ server-init:
195
+ condition: service_completed_successfully
196
+ # 不等 temporal-server healthy:server 启动不强依赖 Temporal
197
+ temporal-server:
198
+ condition: service_started
199
+ environment:
200
+ DATABASE_URL: postgresql://${POSTGRES_USER:-kavenma}:${POSTGRES_PASSWORD:-kavenma}@postgres:5432/gadmin_demo?schema=public
201
+ TEMPORAL_ADDRESS: temporal-server:7233
202
+ TEMPORAL_NAMESPACE: ${TEMPORAL_NAMESPACE:-default}
203
+ TEMPORAL_TASK_QUEUE: ${TEMPORAL_TASK_QUEUE:-workflow-execution}
204
+ NODE_ENV: ${NODE_ENV:-development}
205
+ ports:
206
+ - "8000:8000"
207
+
208
+ # ---------------------------------------------------------------------------
209
+ # Temporal Worker(Node.js 长驻进程)
210
+ # ---------------------------------------------------------------------------
211
+ temporal-worker:
212
+ image: ${COMPOSE_PROJECT_NAME}-temporal-worker:local
213
+ build:
214
+ context: ./temporal/worker
215
+ dockerfile: Dockerfile
216
+ container_name: gadmin2-temporal-worker
217
+ restart: on-failure
218
+ depends_on:
219
+ postgres:
220
+ condition: service_healthy
221
+ server-init:
222
+ condition: service_completed_successfully
223
+ temporal-server:
224
+ condition: service_healthy
225
+ environment:
226
+ TEMPORAL_ADDRESS: temporal-server:7233
227
+ TEMPORAL_NAMESPACE: ${TEMPORAL_NAMESPACE:-default}
228
+ TEMPORAL_TASK_QUEUE: ${TEMPORAL_TASK_QUEUE:-workflow-execution}
229
+ DATABASE_URL: postgresql://${POSTGRES_USER:-kavenma}:${POSTGRES_PASSWORD:-kavenma}@postgres:5432/gadmin_demo?schema=public
230
+ # 无对外端口、无 health endpoint
231
+
232
+ # ---------------------------------------------------------------------------
233
+ # Web 前端(生产 nginx 镜像;本地仅用于完整集成验证)
234
+ # 镜像构建依赖 codegen:先 `docker compose --profile codegen build codegen`
235
+ # 开发态请用 `cd web && yarn dev`,热更新更顺手
236
+ # ---------------------------------------------------------------------------
237
+ web:
238
+ image: ${COMPOSE_PROJECT_NAME}-web:local
239
+ build:
240
+ context: .
241
+ dockerfile: Dockerfile.web
242
+ args:
243
+ CODEGEN_IMAGE: ${COMPOSE_PROJECT_NAME}-codegen:local
244
+ NODE_ENV: production
245
+ container_name: gadmin2-web
246
+ restart: unless-stopped
247
+ depends_on:
248
+ server:
249
+ condition: service_started
250
+ ports:
251
+ - "3000:80"
252
+
253
+ volumes:
254
+ pgdata:
@@ -7,12 +7,12 @@
7
7
  "license": "UNLICENSED",
8
8
  "scripts": {
9
9
  "prebuild": "rimraf dist",
10
- "build": "gadmin2 build",
10
+ "build": "nest build -c gadmin-cli.json",
11
11
  "format": "prettier --write \"../config/*.ts\" \"src/**/*.ts\" \"test/**/*.ts\"",
12
12
  "prepare": "cd .. && husky install",
13
- "start": "dotenv -e .env.local -e .env -- gadmin2 start",
14
- "start:dev": "dotenv -e .env.local -e .env -- gadmin2 start --watch",
15
- "start:debug": "dotenv -e .env.local -e .env -- gadmin2 start --debug --watch",
13
+ "start": "dotenv -e .env.local -e .env -- nest start -c gadmin-cli.json",
14
+ "start:dev": "dotenv -e .env.local -e .env -- nest start -c gadmin-cli.json --watch",
15
+ "start:debug": "dotenv -e .env.local -e .env -- nest start -c gadmin-cli.json --debug --watch",
16
16
  "start:prod": "/bin/sh start-prod.sh",
17
17
  "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
18
18
  "test": "jest",
@@ -26,7 +26,7 @@
26
26
  "migrate:status": "dotenv -e .env.local -e .env -- prisma migrate status",
27
27
  "generate": "dotenv -e .env.local -e .env -- npx prisma generate",
28
28
  "generate:dev": "npm run migrate:dev && dotenv -e .env.local -e .env -- npx prisma generate",
29
- "postgenerate:dev": "dotenv -e .env.local -e .env -- ts-node scripts/sync-data-mngt-pages.ts && dotenv -e .env.local -e .env -- ts-node scripts/sync-resources.ts",
29
+ "postgenerate:dev": "dotenv -e .env.local -e .env -- ts-node seed/data-mngt.seed.ts",
30
30
  "generate:web": "dotenv -e .env.local -e .env -- npx prisma generate --generator react",
31
31
  "generate:server": "dotenv -e .env.local -e .env -- npx prisma generate --generator client --generator nestjs",
32
32
  "push:db": "dotenv -e .env.local -e .env -- prisma db push --skip-generate",
@@ -46,7 +46,6 @@
46
46
  "@nestjs/schedule": "^4.0.1",
47
47
  "@nestjs/serve-static": "^4.0.1",
48
48
  "@nestjs/swagger": "^7.3.0",
49
- "@temporalio/client": "^1.11.6",
50
49
  "@opentelemetry/api": "^1.9.0",
51
50
  "@opentelemetry/exporter-trace-otlp-grpc": "^0.211.0",
52
51
  "@opentelemetry/instrumentation-express": "^0.47.0",
@@ -58,6 +57,7 @@
58
57
  "@opentelemetry/semantic-conventions": "^1.39.0",
59
58
  "@prisma/client": "^6.19.0",
60
59
  "@prisma/instrumentation": "6.19.0",
60
+ "@temporalio/client": "^1.11.6",
61
61
  "agenda": "^6.2.5",
62
62
  "axios": "^1.13.2",
63
63
  "cache-manager": "^6.4.3",
@@ -88,8 +88,9 @@
88
88
  },
89
89
  "devDependencies": {
90
90
  "@faker-js/faker": "^10.4.0",
91
- "@gadmin2n/prisma-nest-generator": "^0.0.44",
91
+ "@gadmin2n/prisma-nest-generator": "^0.0.45",
92
92
  "@gadmin2n/prisma-react-generator": "^0.0.60",
93
+ "@nestjs/cli": "^11.0.21",
93
94
  "@nestjs/testing": "^10.4.15",
94
95
  "@types/cookie-parser": "^1.4.3",
95
96
  "@types/express": "^4.17.21",
@@ -2,7 +2,7 @@
2
2
  * page-helpers.ts — 页面与权限管理的共享 DB 操作原语
3
3
  *
4
4
  * 接受 PrismaClient 作为参数,可被 CLI 脚本(page-manage.ts / permission-manage.ts)
5
- * 和 ../sync-data-mngt-pages.ts / seed/permissions.ts)共同调用。
5
+ * 和 seed/*.seed.ts(含 data-mngt.seed.ts)共同调用。
6
6
  */
7
7
 
8
8
  import { PrismaClient } from '@prisma/client';
@@ -28,7 +28,7 @@ const PRISMA_CONFIG_DIR = path.resolve(
28
28
 
29
29
  /**
30
30
  * Prisma files that define system/permission models and must NOT be scanned.
31
- * These are already handled by seedPermissions().
31
+ * These are already handled by static seed modules (bootstrap + permission/audit/etc.).
32
32
  */
33
33
  const EXCLUDED_FILES = new Set(['system.prisma']);
34
34
 
@@ -0,0 +1,39 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import {
3
+ bindResourceToPage,
4
+ getPageResourceIds,
5
+ grantRoleAccess,
6
+ upsertPage,
7
+ } from '../scripts/lib/page-helpers';
8
+
9
+ /**
10
+ * Agenda 模块 seed
11
+ *
12
+ * 职责:agenda page (admin 子节点) + agenda resource + 自家 SYSTEM_ADMIN 授权。
13
+ * 前置:seedBootstrap 已建好 admin 与 SYSTEM_ADMIN。
14
+ */
15
+ export async function seedAgendaModule(prisma: PrismaClient): Promise<void> {
16
+ const role = await prisma.role.findUniqueOrThrow({
17
+ where: { name: 'SYSTEM_ADMIN' },
18
+ });
19
+ const admin = await prisma.page.findUniqueOrThrow({
20
+ where: { code: 'admin' },
21
+ });
22
+
23
+ const page = await upsertPage(prisma, {
24
+ code: 'agenda',
25
+ name: 'agenda',
26
+ zhName: 'Cron Jobs',
27
+ enName: 'Cron Jobs',
28
+ sortOrder: 1020,
29
+ parentId: admin.id,
30
+ });
31
+ console.log(`[agenda] Page: ${page.code} (id=${page.id})`);
32
+
33
+ await bindResourceToPage(prisma, page.id, 'agenda', 'Agenda job management');
34
+ console.log(`[agenda] PageResource: agenda ↔ agenda`);
35
+
36
+ const resourceIds = await getPageResourceIds(prisma, page.id);
37
+ await grantRoleAccess(prisma, role.id, page.id, resourceIds);
38
+ console.log(`[agenda] Grant: SYSTEM_ADMIN → agenda`);
39
+ }
@@ -0,0 +1,40 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import {
3
+ bindResourceToPage,
4
+ getPageResourceIds,
5
+ grantRoleAccess,
6
+ upsertPage,
7
+ } from '../scripts/lib/page-helpers';
8
+
9
+ /**
10
+ * Audit 模块 seed
11
+ *
12
+ * 职责:audits page (admin 子节点) + audit resource + 自家 SYSTEM_ADMIN 授权。
13
+ * 注意:page.code 是 'audits',但 resource.code 是 'audit'(与 backend module 一致)。
14
+ * 前置:seedBootstrap 已建好 admin 与 SYSTEM_ADMIN。
15
+ */
16
+ export async function seedAuditModule(prisma: PrismaClient): Promise<void> {
17
+ const role = await prisma.role.findUniqueOrThrow({
18
+ where: { name: 'SYSTEM_ADMIN' },
19
+ });
20
+ const admin = await prisma.page.findUniqueOrThrow({
21
+ where: { code: 'admin' },
22
+ });
23
+
24
+ const page = await upsertPage(prisma, {
25
+ code: 'audits',
26
+ name: 'audits',
27
+ zhName: '审计日志',
28
+ enName: 'Audit Log',
29
+ sortOrder: 1060,
30
+ parentId: admin.id,
31
+ });
32
+ console.log(`[audit] Page: ${page.code} (id=${page.id})`);
33
+
34
+ await bindResourceToPage(prisma, page.id, 'audit', 'Audit logs');
35
+ console.log(`[audit] PageResource: audits ↔ audit`);
36
+
37
+ const resourceIds = await getPageResourceIds(prisma, page.id);
38
+ await grantRoleAccess(prisma, role.id, page.id, resourceIds);
39
+ console.log(`[audit] Grant: SYSTEM_ADMIN → audits`);
40
+ }
@@ -0,0 +1,56 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import { grantRoleAccess, upsertPage } from '../scripts/lib/page-helpers';
3
+
4
+ /**
5
+ * Bootstrap seed — 全模块共享的最小公共集
6
+ *
7
+ * 任何模块 seed 都假设以下 3 个对象已存在:
8
+ * 1. SYSTEM_ADMIN 角色 —— 各模块 grant SYSTEM_ADMIN 时通过 name 查找
9
+ * 2. admin 顶级 page —— 各业务模块 page 的 parentId
10
+ * 3. dataMngt 分组 page —— 自动生成 CRUD 页面(data-mngt.seed.ts)的 parentId
11
+ *
12
+ * 同时把 SYSTEM_ADMIN 对 admin / dataMngt 这两个分组节点的 RolePages 也建好,
13
+ * 否则它们的子菜单父节点不会展开。
14
+ *
15
+ * 必须在所有模块 seed 之前运行(由 seed/index.ts 编排)。
16
+ * 幂等:upsert / skipDuplicates。
17
+ */
18
+ export async function seedBootstrap(prisma: PrismaClient): Promise<void> {
19
+ // ── SYSTEM_ADMIN 角色 ──────────────────────────────────────────────────────
20
+ const systemAdminRole = await prisma.role.upsert({
21
+ where: { name: 'SYSTEM_ADMIN' },
22
+ update: {},
23
+ create: {
24
+ name: 'SYSTEM_ADMIN',
25
+ description: 'System administrator with full access',
26
+ },
27
+ });
28
+ console.log(`[bootstrap] Role: SYSTEM_ADMIN (id=${systemAdminRole.id})`);
29
+
30
+ // ── admin 顶级分组 page ────────────────────────────────────────────────────
31
+ const adminPage = await upsertPage(prisma, {
32
+ code: 'admin',
33
+ name: 'admin',
34
+ zhName: '系统管理',
35
+ enName: 'System Mgnt',
36
+ sortOrder: 1000,
37
+ });
38
+ console.log(`[bootstrap] Page: ${adminPage.code} (id=${adminPage.id})`);
39
+
40
+ // ── dataMngt 分组 page(自动 CRUD 菜单容器) ──────────────────────────────
41
+ const dataMngtPage = await upsertPage(prisma, {
42
+ code: 'dataMngt',
43
+ name: '数据管理',
44
+ zhName: '数据管理',
45
+ enName: 'Data Management',
46
+ sortOrder: 1000,
47
+ parentId: adminPage.id,
48
+ });
49
+ console.log(`[bootstrap] Page: ${dataMngtPage.code} (id=${dataMngtPage.id})`);
50
+
51
+ // ── 分组 page 的 SYSTEM_ADMIN RolePages(无 resource) ──────────────────────
52
+ // 分组节点本身不绑定 resource,只为让侧边栏父节点对 SYSTEM_ADMIN 可见
53
+ await grantRoleAccess(prisma, systemAdminRole.id, adminPage.id, []);
54
+ await grantRoleAccess(prisma, systemAdminRole.id, dataMngtPage.id, []);
55
+ console.log(`[bootstrap] RolePages: SYSTEM_ADMIN → admin, dataMngt`);
56
+ }
@@ -0,0 +1,39 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import {
3
+ bindResourceToPage,
4
+ getPageResourceIds,
5
+ grantRoleAccess,
6
+ upsertPage,
7
+ } from '../scripts/lib/page-helpers';
8
+
9
+ /**
10
+ * Canvas 模块 seed
11
+ *
12
+ * 职责:canvas page (admin 子节点) + canvas resource + 自家 SYSTEM_ADMIN 授权。
13
+ * 前置:seedBootstrap 已建好 admin 与 SYSTEM_ADMIN。
14
+ */
15
+ export async function seedCanvasModule(prisma: PrismaClient): Promise<void> {
16
+ const role = await prisma.role.findUniqueOrThrow({
17
+ where: { name: 'SYSTEM_ADMIN' },
18
+ });
19
+ const admin = await prisma.page.findUniqueOrThrow({
20
+ where: { code: 'admin' },
21
+ });
22
+
23
+ const page = await upsertPage(prisma, {
24
+ code: 'canvas',
25
+ name: 'canvas',
26
+ zhName: 'Canvas',
27
+ enName: 'Canvas',
28
+ sortOrder: 1010,
29
+ parentId: admin.id,
30
+ });
31
+ console.log(`[canvas] Page: ${page.code} (id=${page.id})`);
32
+
33
+ await bindResourceToPage(prisma, page.id, 'canvas', 'Canvas management');
34
+ console.log(`[canvas] PageResource: canvas ↔ canvas`);
35
+
36
+ const resourceIds = await getPageResourceIds(prisma, page.id);
37
+ await grantRoleAccess(prisma, role.id, page.id, resourceIds);
38
+ console.log(`[canvas] Grant: SYSTEM_ADMIN → canvas`);
39
+ }
@@ -4,8 +4,8 @@ import {
4
4
  getPageResourceIds,
5
5
  grantRoleAccess,
6
6
  upsertPage,
7
- } from './lib/page-helpers';
8
- import { readPrismaModels } from './prismaModels';
7
+ } from '../scripts/lib/page-helpers';
8
+ import { readPrismaModels } from '../scripts/prismaModels';
9
9
 
10
10
  /**
11
11
  * Models whose Page record should be marked `isVirtual = true`,
@@ -35,7 +35,11 @@ const HIDDEN_FROM_DATA_MNGT_MENU = new Set<string>([
35
35
  * 5. Upsert a RoleResource record (SYSTEM_ADMIN → resource, full actions)
36
36
  *
37
37
  * Safe to re-run — every write uses upsert / skipDuplicates.
38
- * Depends on seedPermissions() having already created SYSTEM_ADMIN role and dataMngt page.
38
+ *
39
+ * Prerequisites: SYSTEM_ADMIN role and dataMngt page must already exist
40
+ * (created by seedBootstrap). The standalone entry point at the bottom of
41
+ * this file delegates to seed/index.ts main() in case the DB is fresh, so
42
+ * `ts-node seed/data-mngt.seed.ts` always works.
39
43
  */
40
44
  export async function seedDataMngtPages(prisma: PrismaClient): Promise<void> {
41
45
  const models = readPrismaModels();
@@ -48,31 +52,25 @@ export async function seedDataMngtPages(prisma: PrismaClient): Promise<void> {
48
52
  }
49
53
 
50
54
  // ── Anchors ────────────────────────────────────────────────────────────────
51
- // Auto-bootstrap: if seed/index.ts has never been run (e.g. fresh DB),
52
- // run the full seed pipeline now (users + permissions + pages).
53
- const existingRole = await prisma.role.findUnique({
55
+ // SYSTEM_ADMIN role and dataMngt page are created by seedBootstrap, which
56
+ // seed/index.ts main() always runs before this function. If they're missing,
57
+ // it means this function was called out of order — fail fast with a clear msg.
58
+ const systemAdminRole = await prisma.role.findUnique({
54
59
  where: { name: 'SYSTEM_ADMIN' },
55
60
  });
56
- if (!existingRole) {
57
- console.log(
58
- '[seedDataMngtPages] SYSTEM_ADMIN not found running full seed (index.ts) first',
61
+ if (!systemAdminRole) {
62
+ throw new Error(
63
+ '[seedDataMngtPages] SYSTEM_ADMIN role missing. Run seedBootstrap (via `yarn seed`) first.',
59
64
  );
60
- const { main } = await import('../seed/index');
61
- await main();
62
- // main() already calls seedDataMngtPages, so we're done.
63
- return;
64
65
  }
65
66
 
66
- const systemAdminRole = existingRole;
67
-
68
67
  const dataMngtPage = await prisma.page.findUnique({
69
68
  where: { code: 'dataMngt' },
70
69
  });
71
70
  if (!dataMngtPage) {
72
- console.error(
73
- '[seedDataMngtPages] dataMngt page still missing after seed. Aborting.',
71
+ throw new Error(
72
+ '[seedDataMngtPages] dataMngt page missing. Run seedBootstrap (via `yarn seed`) first.',
74
73
  );
75
- return;
76
74
  }
77
75
 
78
76
  // ── Process models ─────────────────────────────────────────────────────────
@@ -128,12 +126,30 @@ export async function seedDataMngtPages(prisma: PrismaClient): Promise<void> {
128
126
  }
129
127
 
130
128
  // ─── Standalone Entry Point ───────────────────────────────────────────────────
131
- // Called directly by `postgenerate:dev` (via ts-node), NOT by seed/index.ts.
129
+ // Called directly by `postgenerate:dev` (via ts-node) for incremental sync
130
+ // after `yarn generate:dev` adds new business models. Also callable manually.
131
+ //
132
+ // Fresh-DB safety: if SYSTEM_ADMIN / dataMngt are missing, we delegate to
133
+ // seed/index.ts main() which runs the full seed pipeline (which itself calls
134
+ // seedDataMngtPages). Otherwise we just run seedDataMngtPages incrementally.
132
135
 
133
136
  if (require.main === module) {
134
137
  const prisma = new PrismaClient();
135
138
 
136
- seedDataMngtPages(prisma)
139
+ (async () => {
140
+ const adminRole = await prisma.role.findUnique({
141
+ where: { name: 'SYSTEM_ADMIN' },
142
+ });
143
+ if (!adminRole) {
144
+ console.log(
145
+ '[seedDataMngtPages] Fresh DB detected — delegating to full seed pipeline…',
146
+ );
147
+ const { main } = await import('./index');
148
+ await main();
149
+ return;
150
+ }
151
+ await seedDataMngtPages(prisma);
152
+ })()
137
153
  .catch((e) => {
138
154
  console.error('[seedDataMngtPages] Fatal error:', e);
139
155
  process.exit(1);
@@ -0,0 +1,44 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+ import {
3
+ bindResourceToPage,
4
+ getPageResourceIds,
5
+ grantRoleAccess,
6
+ upsertPage,
7
+ } from '../scripts/lib/page-helpers';
8
+
9
+ /**
10
+ * Game 模块 seed
11
+ *
12
+ * 职责:game page (admin 子节点) + game resource + 自家 SYSTEM_ADMIN 授权。
13
+ * 前置:seedBootstrap 已建好 admin 与 SYSTEM_ADMIN。
14
+ */
15
+ export async function seedGameModule(prisma: PrismaClient): Promise<void> {
16
+ const role = await prisma.role.findUniqueOrThrow({
17
+ where: { name: 'SYSTEM_ADMIN' },
18
+ });
19
+ const admin = await prisma.page.findUniqueOrThrow({
20
+ where: { code: 'admin' },
21
+ });
22
+
23
+ const page = await upsertPage(prisma, {
24
+ code: 'game',
25
+ name: 'game',
26
+ zhName: '业务管理',
27
+ enName: 'Business Mgnt',
28
+ sortOrder: 1050,
29
+ parentId: admin.id,
30
+ });
31
+ console.log(`[game] Page: ${page.code} (id=${page.id})`);
32
+
33
+ await bindResourceToPage(
34
+ prisma,
35
+ page.id,
36
+ 'game',
37
+ 'Business / Game management',
38
+ );
39
+ console.log(`[game] PageResource: game ↔ game`);
40
+
41
+ const resourceIds = await getPageResourceIds(prisma, page.id);
42
+ await grantRoleAccess(prisma, role.id, page.id, resourceIds);
43
+ console.log(`[game] Grant: SYSTEM_ADMIN → game`);
44
+ }