69 lines
3.0 KiB
JavaScript
Executable File
69 lines
3.0 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
// backend: 运行带 Air 热加载的 Go control-plane。
|
|
import { spawn } from "node:child_process";
|
|
import { readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
import { fileURLToPath } from "node:url";
|
|
import { constants } from "node:os";
|
|
import path from "node:path";
|
|
|
|
const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
const envPath = path.join(root, ".env");
|
|
|
|
// 简易 KEY=value 解析(与 compose 的 env_file 语法一致,无需处理引号)。
|
|
const overrides = {};
|
|
if (existsSync(envPath)) {
|
|
for (const line of readFileSync(envPath, "utf8").split("\n")) {
|
|
const m = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(line.trim());
|
|
if (m && !(m[1] in overrides)) overrides[m[1]] = m[2];
|
|
}
|
|
} else {
|
|
console.warn("[dev-backend] 未找到 .env,使用本地开发默认值。");
|
|
}
|
|
|
|
if (!overrides.CONTROL_PLANE_USERNAME) overrides.CONTROL_PLANE_USERNAME = "admin";
|
|
if (!overrides.CONTROL_PLANE_PASSWORD) overrides.CONTROL_PLANE_PASSWORD = "admin123";
|
|
const devMasterKey = Buffer.from("creatorhub dev local master key", "utf8").toString("base64");
|
|
if (Buffer.from(overrides.CREATORHUB_CREDENTIAL_MASTER_KEY ?? "", "base64").length !== 32) {
|
|
if (overrides.CREATORHUB_CREDENTIAL_MASTER_KEY) {
|
|
console.error("[dev-backend] .env 的 CREATORHUB_CREDENTIAL_MASTER_KEY 不是 32 字节 base64,本地开发改用固定开发密钥。");
|
|
}
|
|
overrides.CREATORHUB_CREDENTIAL_MASTER_KEY = devMasterKey;
|
|
}
|
|
if (!overrides.LISTEN_ADDR) overrides.LISTEN_ADDR = ":8082";
|
|
if (!overrides.DATABASE_URL) {
|
|
const postgresPort = overrides.CREATORHUB_POSTGRES_PORT || "15433";
|
|
overrides.DATABASE_URL = `postgres://creatorhub@127.0.0.1:${postgresPort}/creatorhub?sslmode=disable`;
|
|
}
|
|
if (!overrides.WEB_DIR) overrides.WEB_DIR = path.join(root, "web", "dist");
|
|
if (!overrides.CREATOR_COVER_DIR) overrides.CREATOR_COVER_DIR = path.join(root, ".data", "covers");
|
|
if (!overrides.CREATORHUB_CREDENTIAL_STORE_DIR) overrides.CREATORHUB_CREDENTIAL_STORE_DIR = path.join(root, ".dev-credentials");
|
|
if (!overrides.LOG_LEVEL) overrides.LOG_LEVEL = "debug";
|
|
if (!overrides.NATIVE_GATEWAY_ENDPOINT) overrides.NATIVE_GATEWAY_ENDPOINT = "http://127.0.0.1:28187";
|
|
|
|
const env = { ...process.env, ...overrides };
|
|
const lockDir = path.join(root, ".data");
|
|
mkdirSync(lockDir, { recursive: true });
|
|
// flock 持有锁直到 Air 及其子进程退出,拒绝重复启动,不争抢同一个程序文件。
|
|
const air = spawn("flock", [
|
|
"--no-fork", "--nonblock", "--conflict-exit-code", "73",
|
|
path.join(lockDir, "dev-backend.lock"),
|
|
"air", "-c", ".air.toml", ...process.argv.slice(2),
|
|
], {
|
|
cwd: root,
|
|
env,
|
|
stdio: "inherit",
|
|
});
|
|
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
process.on(signal, () => air.kill(signal));
|
|
}
|
|
air.on("error", (error) => {
|
|
console.error(`[dev-backend] 启动失败:${error.message}`);
|
|
process.exit(1);
|
|
});
|
|
air.on("exit", (code, signal) => {
|
|
if (code === 73) {
|
|
console.error("[dev-backend] 后台已在运行,拒绝重复启动。请停止原进程后再重启。");
|
|
}
|
|
process.exit(code ?? (128 + constants.signals[signal]));
|
|
});
|