2026-05-17 23:09:56 +08:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* dev:hot 启动入口。
|
|
|
|
|
*
|
|
|
|
|
* - 使用 cargo-watch 自动重编译并重启 mnote-web。
|
|
|
|
|
* - 通过 MNOTE_WEB_DEV_HOT_RELOAD 启用页面端轻量 reload 轮询。
|
2026-07-11 20:39:18 +08:00
|
|
|
* - 默认启用 MNOTE_WEB_ALLOW_DEV_FIXTURES,保证依赖 /api/dev/seed 的 smoke 可直接运行;
|
2026-07-28 17:04:27 +08:00
|
|
|
* 可显式设为 0 关闭;prod:start 保持关闭。
|
|
|
|
|
* - 实际进程编排在 scripts/mnote-web-hot.js。
|
2026-05-17 23:09:56 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const { spawn } = require("node:child_process");
|
|
|
|
|
const fs = require("node:fs");
|
|
|
|
|
const path = require("node:path");
|
|
|
|
|
|
|
|
|
|
const rootDir = path.resolve(__dirname, "..");
|
2026-07-28 17:04:27 +08:00
|
|
|
const hotPath = path.join(rootDir, "scripts", "mnote-web-hot.js");
|
2026-05-17 23:09:56 +08:00
|
|
|
|
|
|
|
|
function cargoWatchCommand(env = process.env) {
|
|
|
|
|
const extraArgs = String(env.MNOTE_WEB_DEV_HOT_CARGO_WATCH_ARGS || "").trim();
|
|
|
|
|
const watchArgs = [
|
|
|
|
|
"-w crates/mnote-web/src",
|
2026-05-26 16:47:03 +08:00
|
|
|
"-w crates/mnote-web/browser",
|
2026-05-17 23:09:56 +08:00
|
|
|
"-w crates/mnote-web/Cargo.toml",
|
|
|
|
|
"-w Cargo.toml",
|
|
|
|
|
"-x \"run -p mnote-web --bin mnote-web\"",
|
|
|
|
|
];
|
|
|
|
|
if (extraArgs) watchArgs.unshift(extraArgs);
|
|
|
|
|
return `cargo watch ${watchArgs.join(" ")}`;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 18:51:16 +08:00
|
|
|
function bindHost(bindAddr) {
|
|
|
|
|
const value = String(bindAddr || "").trim();
|
|
|
|
|
if (!value) return "";
|
|
|
|
|
const ipv6Match = value.match(/^\[([^\]]+)\]:(\d+)$/);
|
|
|
|
|
if (ipv6Match) return ipv6Match[1];
|
|
|
|
|
const lastColon = value.lastIndexOf(":");
|
|
|
|
|
if (lastColon <= 0) return "";
|
|
|
|
|
return value.slice(0, lastColon);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function bindPort(bindAddr, fallbackPort) {
|
|
|
|
|
const value = String(bindAddr || "").trim();
|
|
|
|
|
const match = value.match(/:(\d+)$/);
|
|
|
|
|
if (!match) return fallbackPort;
|
|
|
|
|
const port = Number(match[1]);
|
|
|
|
|
return Number.isFinite(port) && port > 0 ? Math.floor(port) : fallbackPort;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function devHotBindAddr(env = process.env) {
|
|
|
|
|
const frontendPort = bindPort(env.MNOTE_WEB_BIND, Number(env.FRONTEND_PORT || 3000));
|
|
|
|
|
const host = bindHost(env.MNOTE_WEB_BIND);
|
|
|
|
|
if (!host || host === "127.0.0.1" || host === "localhost" || host === "::1") {
|
|
|
|
|
return `0.0.0.0:${frontendPort}`;
|
|
|
|
|
}
|
|
|
|
|
return String(env.MNOTE_WEB_BIND).trim();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 23:09:56 +08:00
|
|
|
function buildDevHotEnv(baseEnv = process.env) {
|
2026-07-03 23:20:16 +08:00
|
|
|
const controlPlaneBackend = String(baseEnv.MNOTE_CONTROL_PLANE_BACKEND || "libsql-local").trim() || "libsql-local";
|
|
|
|
|
if (controlPlaneBackend === "sqlite") {
|
|
|
|
|
throw new Error("dev:hot 不再支持 SQLite control-plane fallback;请使用 libsql-local/turso-local-replica/turso-remote/turso-synced");
|
|
|
|
|
}
|
|
|
|
|
const defaultControlPlaneDir = "/mnt/Data1T/Mnote_data/control-plane";
|
|
|
|
|
const env = {
|
2026-05-17 23:09:56 +08:00
|
|
|
...baseEnv,
|
|
|
|
|
MNOTE_WEB_DEV_HOT_RELOAD: "1",
|
2026-07-11 20:39:18 +08:00
|
|
|
MNOTE_WEB_ALLOW_DEV_FIXTURES:
|
|
|
|
|
String(baseEnv.MNOTE_WEB_ALLOW_DEV_FIXTURES ?? "1").trim() || "1",
|
2026-06-04 18:51:16 +08:00
|
|
|
MNOTE_WEB_BIND: devHotBindAddr(baseEnv),
|
2026-05-17 23:09:56 +08:00
|
|
|
MNOTE_WEB_CMD: String(baseEnv.MNOTE_WEB_CMD || "").trim() || cargoWatchCommand(baseEnv),
|
2026-07-03 23:20:16 +08:00
|
|
|
MNOTE_KNOWLEDGE_PROVIDER: String(baseEnv.MNOTE_KNOWLEDGE_PROVIDER || "lightrag_legacy").trim(),
|
|
|
|
|
MNOTE_CONTROL_PLANE_BACKEND: controlPlaneBackend,
|
2026-07-10 10:54:34 +08:00
|
|
|
MNOTE_PAGE_AI_PI_WARMUP: String(baseEnv.MNOTE_PAGE_AI_PI_WARMUP ?? "1").trim() || "1",
|
2026-07-11 20:39:18 +08:00
|
|
|
MNOTE_PAGE_AI_PI_WARMUP_SEND: String(baseEnv.MNOTE_PAGE_AI_PI_WARMUP_SEND ?? "0").trim() || "0",
|
2026-05-17 23:09:56 +08:00
|
|
|
};
|
2026-07-03 23:20:16 +08:00
|
|
|
if (controlPlaneBackend === "libsql-local" || controlPlaneBackend === "turso-local" || controlPlaneBackend === "turso") {
|
|
|
|
|
env.MNOTE_TURSO_LOCAL_PATH =
|
|
|
|
|
baseEnv.MNOTE_TURSO_LOCAL_PATH || path.join(defaultControlPlaneDir, "control-plane-dev-hot-libsql.db");
|
|
|
|
|
} else if (controlPlaneBackend === "turso-local-replica" || controlPlaneBackend === "turso-remote-replica") {
|
|
|
|
|
env.MNOTE_TURSO_LOCAL_REPLICA_PATH =
|
|
|
|
|
baseEnv.MNOTE_TURSO_LOCAL_REPLICA_PATH || path.join(defaultControlPlaneDir, "control-plane-dev-hot-replica.db");
|
|
|
|
|
} else if (controlPlaneBackend === "turso-synced") {
|
|
|
|
|
env.MNOTE_TURSO_SYNCED_PATH =
|
|
|
|
|
baseEnv.MNOTE_TURSO_SYNCED_PATH || baseEnv.MNOTE_TURSO_LOCAL_REPLICA_PATH || path.join(defaultControlPlaneDir, "control-plane-dev-hot-synced.db");
|
|
|
|
|
}
|
|
|
|
|
return env;
|
2026-05-17 23:09:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function main() {
|
2026-07-28 17:04:27 +08:00
|
|
|
if (!fs.existsSync(hotPath)) {
|
|
|
|
|
throw new Error(`缺少 mnote-web-hot 入口:${hotPath}`);
|
2026-05-17 23:09:56 +08:00
|
|
|
}
|
|
|
|
|
|
2026-07-28 17:04:27 +08:00
|
|
|
const child = spawn(process.execPath, [hotPath], {
|
2026-05-17 23:09:56 +08:00
|
|
|
cwd: rootDir,
|
|
|
|
|
env: buildDevHotEnv(process.env),
|
|
|
|
|
stdio: "inherit",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
child.on("exit", (code, signal) => {
|
|
|
|
|
if (signal) {
|
|
|
|
|
process.kill(process.pid, signal);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
process.exit(code ?? 0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
child.on("error", (error) => {
|
|
|
|
|
console.error(`[dev-hot] 启动失败:${error.message}`);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (require.main === module) {
|
|
|
|
|
main();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
buildDevHotEnv,
|
|
|
|
|
cargoWatchCommand,
|
2026-06-04 18:51:16 +08:00
|
|
|
devHotBindAddr,
|
2026-05-17 23:09:56 +08:00
|
|
|
};
|