@gadmin2n/schematics 0.0.90 → 0.0.92
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/Dockerfile.server +17 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/ensure-database.js +79 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +0 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/start-prod.sh +23 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/title.tsx +1 -1
- package/package.json +1 -1
|
@@ -44,9 +44,14 @@ ENV HUSKY=0 \
|
|
|
44
44
|
# 从 builder 复制依赖描述文件(其源头是 codegen 镜像)
|
|
45
45
|
COPY --from=builder --chown=node:node /app/server/package.json /app/server/yarn.lock ./
|
|
46
46
|
|
|
47
|
-
#
|
|
47
|
+
# 注意:这里不能用 `yarn install --production`。
|
|
48
|
+
# start-prod.sh 启动时需要:
|
|
49
|
+
# - prisma CLI(运行 `yarn push:db`)
|
|
50
|
+
# - ts-node + dotenv-cli(运行 `yarn seed`)
|
|
51
|
+
# 这三者在 package.json 中位于 devDependencies,--production 会跳过它们。
|
|
52
|
+
# 故安装全量依赖;产物体积增加约 80MB,换来启动脚本统一可用。
|
|
48
53
|
RUN --mount=type=cache,target=/root/.yarn \
|
|
49
|
-
yarn install --
|
|
54
|
+
yarn install --frozen-lockfile
|
|
50
55
|
|
|
51
56
|
# 从构建阶段复制 Prisma 生成的 Client(含平台原生二进制)
|
|
52
57
|
COPY --from=builder --chown=node:node /app/server/node_modules/.prisma ./node_modules/.prisma
|
|
@@ -57,13 +62,22 @@ COPY --from=builder --chown=node:node /app/server/dist ./dist
|
|
|
57
62
|
# 复制 Prisma schema(feature 分支部署时 prisma db push 需要)
|
|
58
63
|
COPY --from=builder --chown=node:node /app/server/prisma ./prisma
|
|
59
64
|
|
|
65
|
+
# 复制 seed 源码与其依赖的 scripts/lib(启动时 `yarn seed` 走 ts-node 直接执行 .ts)
|
|
66
|
+
COPY --from=builder --chown=node:node /app/server/seed ./seed
|
|
67
|
+
COPY --from=builder --chown=node:node /app/server/scripts ./scripts
|
|
68
|
+
COPY --from=builder --chown=node:node /app/server/tsconfig.json ./tsconfig.json
|
|
69
|
+
|
|
60
70
|
# 复制启动脚本和数据库迁移脚本(codegen 不会修改这些文件,但统一从 builder 拿,避免 COPY 源混用)
|
|
61
|
-
COPY --from=builder --chown=node:node /app/server/start-prod.sh /app/server/migrate-between-pg-schemas.js ./
|
|
71
|
+
COPY --from=builder --chown=node:node /app/server/start-prod.sh /app/server/migrate-between-pg-schemas.js /app/server/ensure-database.js ./
|
|
62
72
|
RUN chmod +x start-prod.sh
|
|
63
73
|
|
|
64
74
|
# 复制环境变量配置(作为默认值,K8s 注入的同名变量优先级更高)
|
|
65
75
|
COPY --from=builder --chown=node:node /app/server/.env ./.env
|
|
66
76
|
|
|
77
|
+
# package.json 的 push:db / seed 都走 `dotenv -e .env.local -e .env -- ...`,
|
|
78
|
+
# dotenv-cli v7 在指定文件不存在时会报错;镜像里没有 .env.local,touch 空文件兜底。
|
|
79
|
+
RUN touch .env.local && chown node:node .env.local
|
|
80
|
+
|
|
67
81
|
# 切换到非 root 用户(node 镜像内置 uid/gid 1000)
|
|
68
82
|
USER node
|
|
69
83
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 启动前确保 DATABASE_URL 指向的 PostgreSQL 数据库已存在,不存在则创建。
|
|
3
|
+
*
|
|
4
|
+
* 背景:
|
|
5
|
+
* - `prisma db push` 会创建 schema(namespace)和表,但不会创建 PG database 本身。
|
|
6
|
+
* - K8s / compose 首次部署时,DBA / init 容器可能还没建好这张数据库;
|
|
7
|
+
* start-prod.sh 在这种情况下会卡在 `prisma db push` 报 "database does not exist"。
|
|
8
|
+
*
|
|
9
|
+
* 行为:
|
|
10
|
+
* - 解析 DATABASE_URL 拿到目标 dbname。
|
|
11
|
+
* - 连到同集群的 `postgres` 系统库,查 pg_database。
|
|
12
|
+
* - 不存在则 `CREATE DATABASE "<name>"`,已存在则直接返回。
|
|
13
|
+
*
|
|
14
|
+
* 失败语义:
|
|
15
|
+
* - 任何步骤异常(连接失败 / 权限不足 / SQL 报错)都直接 exit 1,让 start-prod.sh 终止。
|
|
16
|
+
* 下游 push:db / 应用启动反正都会失败,越早暴露越好。
|
|
17
|
+
*
|
|
18
|
+
* 关闭开关:
|
|
19
|
+
* - 若部署侧确认 DB 由外部流程保证存在、不希望本步执行,
|
|
20
|
+
* 设置环境变量 `SKIP_ENSURE_DATABASE=1` 即可跳过。
|
|
21
|
+
*/
|
|
22
|
+
const { Client } = require('pg');
|
|
23
|
+
const { URL } = require('url');
|
|
24
|
+
|
|
25
|
+
(async () => {
|
|
26
|
+
if (process.env.SKIP_ENSURE_DATABASE === '1') {
|
|
27
|
+
console.log('[ensure-database] SKIP_ENSURE_DATABASE=1,跳过');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
32
|
+
if (!dbUrl) {
|
|
33
|
+
console.error('[ensure-database] DATABASE_URL 为空');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = new URL(dbUrl);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.error('[ensure-database] DATABASE_URL 解析失败:', e.message);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const dbName = decodeURIComponent(parsed.pathname.replace(/^\//, ''));
|
|
46
|
+
if (!dbName) {
|
|
47
|
+
console.error('[ensure-database] DATABASE_URL 中未指定数据库名');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 构造指向 `postgres` 系统库的连接串,并剔除 prisma 专属的 schema 参数
|
|
52
|
+
const adminUrl = new URL(dbUrl);
|
|
53
|
+
adminUrl.pathname = '/postgres';
|
|
54
|
+
adminUrl.searchParams.delete('schema');
|
|
55
|
+
|
|
56
|
+
const client = new Client({ connectionString: adminUrl.toString() });
|
|
57
|
+
try {
|
|
58
|
+
await client.connect();
|
|
59
|
+
const r = await client.query(
|
|
60
|
+
'SELECT 1 FROM pg_database WHERE datname = $1',
|
|
61
|
+
[dbName],
|
|
62
|
+
);
|
|
63
|
+
if (r.rowCount > 0) {
|
|
64
|
+
console.log(`[ensure-database] 数据库 "${dbName}" 已存在`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// PostgreSQL 标识符引用:双引号包裹,内部双引号转义为两个双引号
|
|
69
|
+
const escaped = `"${dbName.replace(/"/g, '""')}"`;
|
|
70
|
+
console.log(`[ensure-database] 数据库 "${dbName}" 不存在,正在创建 ...`);
|
|
71
|
+
await client.query(`CREATE DATABASE ${escaped}`);
|
|
72
|
+
console.log(`[ensure-database] 数据库 "${dbName}" 创建成功`);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
console.error('[ensure-database] 失败:', e.message);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
} finally {
|
|
77
|
+
await client.end().catch(() => {});
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
} from '../scripts/lib/page-helpers';
|
|
8
8
|
import { seedWorkflowEventTrigger } from './workflow-event-trigger';
|
|
9
9
|
import { seedNodeTypes } from './workflow-node-types';
|
|
10
|
-
import { seedWorkflows } from './workflows';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Workflow 模块 seed
|
|
@@ -103,6 +102,5 @@ export async function seedWorkflowModule(prisma: PrismaClient): Promise<void> {
|
|
|
103
102
|
|
|
104
103
|
// ── 业务数据 ───────────────────────────────────────────────────────────────
|
|
105
104
|
await seedNodeTypes(prisma);
|
|
106
|
-
await seedWorkflows(prisma);
|
|
107
105
|
await seedWorkflowEventTrigger(prisma);
|
|
108
106
|
}
|
|
@@ -24,6 +24,10 @@ fi
|
|
|
24
24
|
|
|
25
25
|
echo "原始 DATABASE_URL: ${DATABASE_URL}"
|
|
26
26
|
|
|
27
|
+
# 确保目标数据库已存在;prisma db push 只建 schema/表,不会建 database 本身。
|
|
28
|
+
# 若部署环境已通过 DBA/init 流程保证 DB 存在,可设 SKIP_ENSURE_DATABASE=1 跳过。
|
|
29
|
+
node ensure-database.js || { echo "ensure-database 失败"; exit 1; }
|
|
30
|
+
|
|
27
31
|
# 如果存在DEPLOY_NAME环境变量,且变量值以feature或者bugfix开头,则执行数据库同步
|
|
28
32
|
# 检查 DEPLOY_NAME 是否以 feature/ 或 bugfix/ 或 hotfix/ 开头
|
|
29
33
|
DEPLOY_PREFIX_MATCH=0
|
|
@@ -77,8 +81,12 @@ if [ "$DEPLOY_PREFIX_MATCH" -eq 1 ]; then
|
|
|
77
81
|
export PG_SCHEMA_TARGET="${SCHEMA_NAME}"
|
|
78
82
|
|
|
79
83
|
node migrate-between-pg-schemas.js || { echo "迁移数据库失败"; exit 1; }
|
|
84
|
+
|
|
85
|
+
# 临时环境每次启动都做一次 seed(幂等:upsert / skipDuplicates 风格,重复跑无害)
|
|
86
|
+
echo "临时环境执行 seed ..."
|
|
87
|
+
yarn seed || { echo "seed 失败"; exit 1; }
|
|
80
88
|
else
|
|
81
|
-
echo "
|
|
89
|
+
echo "非临时环境,仅在权限表缺失或为空时同步表结构和数据"
|
|
82
90
|
# 如果DATABASE_URL中已经包含了schema=xxx,则不添加,如果DATABASE_URL中没有schema=,则添加schema=public,如果DATABASE_URL中已经包含了schema=但没有具体值,则添加public
|
|
83
91
|
case "$DATABASE_URL" in
|
|
84
92
|
*"schema="*)
|
|
@@ -119,10 +127,20 @@ else
|
|
|
119
127
|
echo "更新后的 DATABASE_URL: ${DATABASE_URL}"
|
|
120
128
|
export DATABASE_URL="${DATABASE_URL}"
|
|
121
129
|
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
130
|
+
# 通过查询权限表 t_role 判断是否需要初始化:
|
|
131
|
+
# - t_role 不存在(schema 全新)→ 抛错被 catch → 输出 "yes"
|
|
132
|
+
# - t_role 行数为 0(数据被清空)→ 输出 "yes"
|
|
133
|
+
# - 其它情况 → 输出 "no"
|
|
134
|
+
# 任何意外异常(连接失败等)也保守地走初始化路径。
|
|
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
|
+
|
|
137
|
+
if [ "$NEEDS_INIT" != "no" ]; then
|
|
138
|
+
echo "权限表缺失或无数据(NEEDS_INIT=${NEEDS_INIT}),执行 push:db + seed ..."
|
|
139
|
+
yarn push:db || { echo "同步表结构失败"; exit 1; }
|
|
140
|
+
yarn seed || { echo "seed 失败"; exit 1; }
|
|
141
|
+
else
|
|
142
|
+
echo "权限数据已就绪,跳过 push:db 与 seed"
|
|
143
|
+
fi
|
|
126
144
|
fi
|
|
127
145
|
|
|
128
146
|
# 启动应用(在同一个 shell 会话中,确保环境变量可用)
|