diff --git a/README.md b/README.md index 5efde7b..10646f5 100644 --- a/README.md +++ b/README.md @@ -64,16 +64,24 @@ docker compose down systemctl --user stop creatorhub-browser-gateway.service ``` -## 本地开发(热加载) +## 本地开发(分开启动) + +首次安装前端依赖: ```bash npm --prefix web ci -pnpm dev ``` -- `pnpm dev`:同时运行 Go control-plane 和 Vite HMR; -- `pnpm dev:backend`:启动 PostgreSQL 依赖,检查宿主机 native gateway 后运行 control-plane; -- `pnpm dev:frontend`:单独运行前端。 +分别在四个终端运行: + +```bash +pnpm env # PostgreSQL 依赖 +pnpm gateway # native browser gateway +pnpm backend # Go control-plane,Air 自动重载 +pnpm frontend # Vite 前端,支持 HMR +``` + +`pnpm backend` 不会自动启动数据库或 gateway;先启动 `env` 和 `gateway`。`pnpm gateway` 直接运行当前用户的 native gateway,不要求安装 systemd 服务。修改 `browser_gateway` 的 Python 文件后,重启该命令;新增或删除前端路由后,重跑 `pnpm --dir web run routes:generate`。 服务地址: diff --git a/docs/deployment.md b/docs/deployment.md index d719902..e901b7c 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -93,12 +93,22 @@ Compose 中的 control-plane 访问宿主机 gateway 时,网关 Endpoint 登 ### 裸机开发 +首次安装前端依赖: + ```bash npm --prefix web ci -pnpm dev ``` -`pnpm dev:backend` 只启动 PostgreSQL 依赖,并在启动 control-plane 前检查宿主机 gateway;它不会创建 browser runtime 或替代 gateway。 +分别在终端启动依赖和服务: + +```bash +pnpm env # PostgreSQL +pnpm gateway # native browser gateway +pnpm backend # Go control-plane,Air 热加载 +pnpm frontend # Vite,HMR +``` + +`pnpm backend` 不会隐式启动 PostgreSQL 或 gateway;`pnpm gateway` 直接运行当前用户的 native gateway,不要求 systemd。停止各服务时关闭对应终端即可。 默认联调地址: diff --git a/package.json b/package.json index 0170da1..224ee2c 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,9 @@ "private": true, "version": "0.1.0", "scripts": { - "dev:deps": "docker compose -f compose.yaml -f compose.dev.yaml up -d postgres", - "dev:backend": "node scripts/dev-backend.mjs", - "dev:frontend": "pnpm -C web run dev", - "dev": "node scripts/dev.mjs" + "env": "docker compose -f compose.yaml -f compose.dev.yaml up -d postgres", + "backend": "node scripts/dev-backend.mjs", + "frontend": "pnpm -C web run dev", + "gateway": "node scripts/dev-gateway.mjs" } } diff --git a/scripts/dev-backend.mjs b/scripts/dev-backend.mjs index 680c43e..9007c1b 100755 --- a/scripts/dev-backend.mjs +++ b/scripts/dev-backend.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -// dev-backend: 启动 PostgreSQL 依赖,并连接宿主机 native browser gateway 后运行 control-plane。 -import { spawn, spawnSync } from "node:child_process"; +// backend: 运行带 Air 热加载的 Go control-plane。 +import { spawn } from "node:child_process"; import { readFileSync, existsSync } from "node:fs"; import { fileURLToPath } from "node:url"; import path from "node:path"; @@ -37,25 +37,6 @@ if (!overrides.LOG_LEVEL) overrides.LOG_LEVEL = "debug"; if (!overrides.NATIVE_GATEWAY_ENDPOINT) overrides.NATIVE_GATEWAY_ENDPOINT = "http://127.0.0.1:8081"; const env = { ...process.env, ...overrides }; -const compose = spawnSync( - "docker", - ["compose", "-f", "compose.yaml", "-f", "compose.dev.yaml", "up", "-d", "postgres"], - { cwd: root, env, stdio: "inherit" }, -); -if (compose.status !== 0) { - console.error("启动 PostgreSQL 容器失败,请确认 Docker 只用于数据库依赖。"); - process.exit(compose.status ?? 1); -} - -try { - const response = await fetch(`${overrides.NATIVE_GATEWAY_ENDPOINT}/healthz`, { signal: AbortSignal.timeout(2000) }); - if (!response.ok) throw new Error(`HTTP ${response.status}`); -} catch (error) { - console.error(`[dev-backend] native browser gateway 不可达:${overrides.NATIVE_GATEWAY_ENDPOINT} (${error})`); - console.error("请先以非 root 用户启动 browser_gateway.server.http 或对应的 systemd user service。"); - process.exit(1); -} - const air = spawn("air", ["-c", ".air.toml", ...process.argv.slice(2)], { cwd: root, env, diff --git a/scripts/dev-gateway.mjs b/scripts/dev-gateway.mjs new file mode 100644 index 0000000..fdc3465 --- /dev/null +++ b/scripts/dev-gateway.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node +// gateway: 直接运行开发用 native browser gateway;不依赖 systemd。 +import { spawn } from "node:child_process"; +import { existsSync, readFileSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = path.dirname(path.dirname(fileURLToPath(import.meta.url))); + +function readEnvFile(filePath) { + if (!existsSync(filePath)) return {}; + const values = {}; + for (const line of readFileSync(filePath, "utf8").split("\n")) { + const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(line.trim()); + if (match) values[match[1]] = match[2]; + } + return values; +} + +const rootEnv = readEnvFile(path.join(root, ".env")); +const gatewayEnvPath = process.env.CREATORHUB_BROWSER_GATEWAY_ENV + ?? path.join(os.homedir(), ".config", "creatorhub", "browser-gateway.env"); +const gatewayEnv = readEnvFile(gatewayEnvPath); +const env = { ...process.env, ...rootEnv, ...gatewayEnv }; +if (!env.LISTEN_ADDR) env.LISTEN_ADDR = "0.0.0.0:8081"; + +const gateway = spawn("python3", ["-m", "browser_gateway.server.http"], { + cwd: root, + env, + stdio: "inherit", +}); + +for (const signal of ["SIGINT", "SIGTERM"]) { + process.on(signal, () => gateway.kill(signal)); +} + +gateway.on("exit", (code, signal) => { + if (signal) process.exit(1); + process.exit(code ?? 0); +}); diff --git a/scripts/dev.mjs b/scripts/dev.mjs deleted file mode 100644 index bdd40bd..0000000 --- a/scripts/dev.mjs +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env node -// dev: 同时跑 dev:backend(air 热加载 control-plane)与 dev:frontend(vite HMR)。 -// Ctrl-C 结束两个子进程。 -import { spawn } from "node:child_process"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const root = path.dirname(path.dirname(fileURLToPath(import.meta.url))); -const pnpm = process.env.npm_execpath || "pnpm"; - -const procs = [ - spawn(pnpm, ["run", "dev:backend"], { cwd: root, stdio: "inherit" }), - spawn(pnpm, ["run", "dev:frontend"], { cwd: root, stdio: "inherit" }), -]; - -for (const signal of ["SIGINT", "SIGTERM"]) { - process.on(signal, () => { - for (const p of procs) p.kill(signal); - }); -} - -let remaining = procs.length; -for (const p of procs) { - p.on("exit", (code) => { - if (--remaining === 0) process.exit(code ?? 0); - }); -}