fix: 分离日常发布与首次初始化(09-09-existing-deploy-seed-separation)
这个提交包含在:
@@ -0,0 +1,99 @@
|
||||
const { spawnSync } = require("node:child_process");
|
||||
const path = require("node:path");
|
||||
|
||||
function executeCommand(command, args) {
|
||||
const result = spawnSync(command, args, {
|
||||
cwd: path.resolve(__dirname, "../.."),
|
||||
env: process.env,
|
||||
stdio: "inherit",
|
||||
});
|
||||
if (result.error || result.status !== 0) {
|
||||
throw new Error("deployment.init_command_failed");
|
||||
}
|
||||
}
|
||||
|
||||
async function inspectRequiredConfiguration(prisma) {
|
||||
return prisma.$transaction(async (transaction) => {
|
||||
await transaction.$executeRaw`SET TRANSACTION READ ONLY`;
|
||||
const checks = [
|
||||
{
|
||||
code: "expense_types",
|
||||
count: await transaction.expenseType.count({ where: { enabled: true } }),
|
||||
maintenance: "管理员核对 /api/expense-types;缺失数据需另批修复",
|
||||
},
|
||||
{
|
||||
code: "approval_rules",
|
||||
count: await transaction.approvalRule.count({ where: { enabled: true } }),
|
||||
maintenance: "/system/approval-rules",
|
||||
},
|
||||
{
|
||||
code: "reimbursement_workflow",
|
||||
count: await transaction.workflowDefinition.count({
|
||||
where: { entity_type: "reimbursement", is_active: true },
|
||||
}),
|
||||
maintenance: "/system/workflow-definitions",
|
||||
},
|
||||
];
|
||||
return checks;
|
||||
}, { isolationLevel: "RepeatableRead", maxWait: 5000, timeout: 10000 });
|
||||
}
|
||||
|
||||
async function checkRequiredConfiguration() {
|
||||
const { createPrismaClient } = require("../prismaClient");
|
||||
const { prisma, pool } = createPrismaClient();
|
||||
try {
|
||||
return await inspectRequiredConfiguration(prisma);
|
||||
} finally {
|
||||
try {
|
||||
await prisma.$disconnect();
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function runInitialization({
|
||||
mode,
|
||||
runCommand = executeCommand,
|
||||
checkConfiguration = checkRequiredConfiguration,
|
||||
report = (result) => process.stdout.write(JSON.stringify(result) + "\n"),
|
||||
}) {
|
||||
if (mode !== "existing" && mode !== "initialize") {
|
||||
throw new Error("deployment.data_mode_required");
|
||||
}
|
||||
report({ stage: "validate_config", mode });
|
||||
await runCommand(process.execPath, ["-e", 'require("./src/config").getConfig()']);
|
||||
report({ stage: "migrate", mode });
|
||||
await runCommand("npm", ["run", "prisma:migrate:deploy"]);
|
||||
if (mode === "initialize") {
|
||||
for (const script of ["seed:config", "seed:trialrun"]) {
|
||||
report({ stage: script, mode });
|
||||
await runCommand("npm", ["run", script]);
|
||||
}
|
||||
}
|
||||
report({ stage: "check_required_configuration", mode });
|
||||
const checks = await checkConfiguration();
|
||||
const missing = checks.filter((check) => check.count === 0);
|
||||
if (missing.length) {
|
||||
report({ status: "REQUIRED_CONFIGURATION_MISSING", checks: missing });
|
||||
throw new Error("deployment.required_configuration_missing");
|
||||
}
|
||||
report({ status: "INIT_PASS", mode, checks });
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
runInitialization({ mode: process.env.DEPLOY_DATA_MODE }).catch((error) => {
|
||||
const allowed = new Set([
|
||||
"deployment.data_mode_required",
|
||||
"deployment.init_command_failed",
|
||||
"deployment.required_configuration_missing",
|
||||
]);
|
||||
process.stderr.write(JSON.stringify({
|
||||
status: "INIT_FAILED",
|
||||
code: allowed.has(error.message) ? error.message : "deployment.init_failed",
|
||||
}) + "\n");
|
||||
process.exitCode = 1;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { runInitialization, inspectRequiredConfiguration };
|
||||
在新工单中引用
屏蔽一个用户