Files
mnote/scripts/desktop-local.js
T
2026-01-15 20:54:21 +08:00

160 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* 一键启动:wolai-backend + 桌面端(Electron + 内置 Next standalone
*
* 目的:
* - 本地联调无需反复安装 NSIS 安装包
* - Electron 走内置 Next(更接近安装包运行方式)
*
* 可选环境变量:
* - SKIP_DESKTOP_NEXT_BUILD=1:跳过 build:desktop:next(仅启动)
* - PYTHON_BIN:默认 python
* - BACKEND_CMD:覆盖后端启动命令
* - ELECTRON_CMD:覆盖 Electron 启动命令
* - MNOTE_DATA_DIR:覆盖桌面端 data 目录(默认 <repo>/data-desktop-test
*/
const { spawnSync, spawn } = require("child_process");
const fs = require("fs");
const net = require("net");
const path = require("path");
const rootDir = path.resolve(__dirname, "..");
const backendDir = path.join(rootDir, "wolai-backend");
const pythonBin = process.env.PYTHON_BIN || "python";
const dataDir = process.env.MNOTE_DATA_DIR || path.join(rootDir, "data-desktop-test");
const backendCmd =
process.env.BACKEND_CMD || `${pythonBin} -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000`;
const electronCmd = process.env.ELECTRON_CMD || "electron desktop-electron/main.js";
function loadEnvFile(filePath) {
if (!fs.existsSync(filePath)) return {};
const content = fs.readFileSync(filePath, "utf8");
return content
.split(/\r?\n/)
.filter((line) => line.trim() && !line.trim().startsWith("#"))
.reduce((acc, line) => {
const idx = line.indexOf("=");
if (idx === -1) return acc;
const key = line.slice(0, idx).trim();
const value = line.slice(idx + 1).trim();
acc[key] = value;
return acc;
}, {});
}
function runOrExit(command, cwd, env) {
const r = spawnSync(command, {
cwd,
env,
stdio: "inherit",
shell: true,
});
if (r.status !== 0) process.exit(r.status ?? 1);
}
async function waitForPort(host, port, timeoutMs = 20_000) {
const start = Date.now();
// eslint-disable-next-line no-constant-condition
while (true) {
const ok = await new Promise((resolve) => {
const socket = net.createConnection({ host, port });
const timer = setTimeout(() => {
socket.destroy();
resolve(false);
}, 500);
socket.once("connect", () => {
clearTimeout(timer);
socket.end();
resolve(true);
});
socket.once("error", () => {
clearTimeout(timer);
resolve(false);
});
});
if (ok) return;
if (Date.now() - start > timeoutMs) {
throw new Error(`等待端口超时:${host}:${port}`);
}
// eslint-disable-next-line no-await-in-loop
await new Promise((r) => setTimeout(r, 120));
}
}
async function main() {
const envMerged = {
...process.env,
...loadEnvFile(path.join(rootDir, ".env.local")),
...loadEnvFile(path.join(rootDir, ".env")),
...loadEnvFile(path.join(backendDir, ".env")),
PYTHONUTF8: "1",
MNOTE_DATA_DIR: dataDir,
};
const skipBuild =
(process.env.SKIP_DESKTOP_NEXT_BUILD || "").toLowerCase() === "1" ||
(process.env.SKIP_DESKTOP_NEXT_BUILD || "").toLowerCase() === "true";
if (!skipBuild) {
console.log("[desktop-local] 构建桌面端内置 Nextstandalone)…");
runOrExit("pnpm run build:desktop:next", rootDir, envMerged);
} else {
console.log("[desktop-local] 已跳过 build:desktop:nextSKIP_DESKTOP_NEXT_BUILD=1");
}
console.log(`[desktop-local] data 目录:${dataDir}`);
console.log(`[desktop-local] 后端命令:${backendCmd}`);
console.log(`[desktop-local] Electron 命令:${electronCmd}`);
const children = [];
let shuttingDown = false;
const shutdown = () => {
if (shuttingDown) return;
shuttingDown = true;
console.log("[desktop-local] 正在关闭子进程…");
for (const child of children) {
try {
if (!child.killed) child.kill("SIGINT");
} catch {}
}
setTimeout(() => process.exit(0), 200);
};
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
const backend = spawn(backendCmd, {
cwd: backendDir,
env: envMerged,
stdio: "inherit",
shell: true,
});
children.push(backend);
backend.on("exit", () => shutdown());
await waitForPort("127.0.0.1", 8000).catch(() => {
console.log("[desktop-local] 提示:未检测到后端 8000 端口就绪(可能仍在启动或启动失败)。");
});
const electron = spawn(electronCmd, {
cwd: rootDir,
env: envMerged,
stdio: "inherit",
shell: true,
});
children.push(electron);
electron.on("exit", () => shutdown());
}
main().catch((err) => {
console.error(err instanceof Error ? err.stack : String(err));
process.exit(1);
});