Files
im0o ac91b96ef8 写出配置落盘的保护链路
顺序是:检查基准是否过时 → 自己校验 → 临时文件上跑 mihomo -t → 备份 → 写入 → 重载,
最后两步任一失败就从备份还原并让内核载回原样。

自己校验那一步不能省:实测 v1.19.29,入站的 proxy 指向不存在的出站时 mihomo -t 会通过、
重载会成功,直到有流量走到那个入站才在日志里报错。这类错误只有 Patchbay 查得出来。

路径在读环境变量时就转成绝对路径,因为内核的 PUT /configs 拒绝相对路径。
2026-08-10 02:56:04 +08:00

67 lines
2.1 KiB
TypeScript

/**
* 运行所需的配置项,全部来自环境变量(Bun 会自动读取 .env)。
*
* 这里刻意在启动时就把缺失项报出来,而不是等到某个请求进来才失败——
* 配置类工具最糟糕的失败方式是「界面能打开、点保存才发现连不上内核」。
*/
import { resolve } from 'node:path'
function required(name: string): string {
const value = Bun.env[name]
if (!value) {
throw new Error(
`缺少环境变量 ${name}。把 .env.example 复制成 .env 再填,或者在启动命令里带上它。`,
)
}
return value
}
function optional(name: string, fallback: string): string {
return Bun.env[name] || fallback
}
/**
* 路径一律转成绝对路径。
*
* 不只是为了整洁:内核的 `PUT /configs` 接口拒绝相对路径,会直接回
* `path is not a absolute path`。在这里统一处理,别让 .env 里怎么写决定后面能不能跑。
*/
function absolute(name: string): string {
return resolve(required(name))
}
export interface Env {
/** mihomo 管理接口的地址,例如 http://127.0.0.1:9090 */
mihomoApi: string
/** 管理接口的访问密钥,即配置文件里 secret: 那一行 */
mihomoSecret: string
/** 要编辑的配置文件路径 */
configPath: string
/** mihomo 可执行文件路径,用于运行 `mihomo -t` 校验 */
mihomoBin: string
/** 传给 `mihomo -t -d` 的工作目录 */
mihomoHome: string
/** Patchbay 自己监听的地址 */
host: string
/** Patchbay 自己监听的端口 */
port: number
}
export function readEnv(): Env {
const port = Number(optional('PATCHBAY_PORT', '7580'))
if (!Number.isInteger(port) || port < 1 || port > 65535) {
throw new Error(`PATCHBAY_PORT 不是合法端口号:${String(Bun.env.PATCHBAY_PORT)}`)
}
return {
mihomoApi: required('MIHOMO_API').replace(/\/+$/, ''),
mihomoSecret: required('MIHOMO_SECRET'),
configPath: absolute('PATCHBAY_CONFIG'),
mihomoBin: absolute('PATCHBAY_MIHOMO_BIN'),
mihomoHome: absolute('PATCHBAY_MIHOMO_HOME'),
host: optional('PATCHBAY_HOST', '127.0.0.1'),
port,
}
}