Files
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

136 lines
7.1 KiB
Markdown

# 在无 UI 的 Linux 服务器上部署 NomiFun WebUI
本目录是 **headless(无图形界面)** 部署产物。服务器上**不要**装 Tauri 桌面包(`.deb`/`.AppImage` 需要图形会话);要跑的是 `nomifun-web` —— 一个纯 axum 的 HTTP/WS 服务,后端 in-process + 同端口托管前端 SPA,**零 GUI 依赖**,可在没有显示器的服务器上运行。
## 访问与认证模型(重要)
- **`nomifun-web` 默认要求登录。** 第一次用浏览器打开时,登录框里**你输入的用户名 + 密码就成为初始管理员账号**(首启 setup,一次性)。之后该端点关闭(再调返回 409),只能用这套凭据登录。
- **桌面版(Tauri)不受影响**,仍是本机免认证。
- 默认建议先只监听 `127.0.0.1`,完成首启管理员设置或预置
`NOMIFUN_ADMIN_PASSWORD` 后,再显式改为 `0.0.0.0` 暴露给 LAN/VPN。
**面向公网请务必在前面加 TLS**(见下文 Caddy)。
- 可选:设置 `NOMIFUN_ADMIN_PASSWORD` 在启动时**预置**管理员,跳过交互式首启(适合自动化部署 / 想关闭首启竞态窗口的场景)。
---
## 方式一:Docker(推荐)
仓库根目录已带 `Dockerfile``docker-compose.yml``Caddyfile`
```bash
# 在仓库根目录
docker compose up -d --build
# 默认发布 8787;首次访问会创建管理员。公开暴露前建议预置管理员或先在可信网络完成 setup。
```
`restart: unless-stopped` 已让它**开机自启 + 崩溃自拉**——装好就等于"自动开启 webui"。数据(SQLite、日志、bun 运行时缓存、agent 状态)落在命名卷 `nomifun-data:/data`
查看启动是否就绪:
```bash
docker compose logs -f nomifun # 看到 "nomifun-web: embedded backend + SPA on one port" 即 OK
```
### 加 TLS(公网必做)
`docker-compose.yml` 里有一段注释掉的 `caddy` 服务。启用步骤:
1. 编辑 `Caddyfile`,把 `your.domain.com` 换成你的域名;
2.`nomifun``environment` 里加 `NOMIFUN_HTTPS: "true"`(让会话 cookie 带 `Secure`);
3.`nomifun``ports: ["8787:8787"]` 改成 `expose: ["8787"]`(只让 Caddy 对外);
4. 取消 `caddy` 服务和 `caddy-*` 卷的注释;
5. `docker compose up -d`
Caddy 会自动签 HTTPS 证书,WebSocket(`/ws`)自动透传,无需额外配置。应用自带登录,所以 **Caddy 不需要再配 basic_auth**
---
## 方式二:原生二进制 + systemd(不想用 Docker)
需要一台 Linux 构建机(Windows 交叉编译这堆 C 依赖很痛,不建议;也可直接从 Docker 镜像里 `docker cp` 出二进制)。
### 1) 构建产物
```bash
bun install
bun run build:ui # → ui/dist(SPA,~21MB)
cargo build --release -p nomifun-web # → target/release/nomifun-web
```
### 2) 部署布局
```
/opt/nomifun/nomifun-web # 二进制
/opt/nomifun/web/ # = ui/dist 的内容
/var/lib/nomifun/ # data 目录(systemd StateDirectory 自动创建)
```
```bash
sudo useradd --system --home /var/lib/nomifun --shell /usr/sbin/nologin nomifun
sudo mkdir -p /opt/nomifun/web
sudo cp target/release/nomifun-web /opt/nomifun/
sudo cp -r ui/dist/. /opt/nomifun/web/
```
> ⚠️ **bun ≥ 1.3.13 必须在「系统」PATH 上**。`nomifun` 是 nologin 账户,装到某人 `~/.bun/bin/` 里它读不到(systemd 服务也不会用你的 `~/.bun`)。可靠做法二选一:
> - **系统级安装**:`curl -fsSL https://bun.sh/install | bash`,然后 `sudo install ~/.bun/bin/bun /usr/local/bin/bun`(或用包管理器 / `npm i -g bun`);
> - **编进二进制**(免 PATH 依赖):构建时 `NOMIFUN_EMBED_BUN=1 cargo build --release -p nomifun-web`,bun 会嵌入并在 data 目录自解压(需构建环境能取到 bun)。
>
> 校验:`sudo -u nomifun -s -- which bun` 必须返回路径,否则 agent 首次 spawn 才暴露失败、错误隐晦。
### 3) 安装并开机自启
```bash
sudo cp packaging/linux/nomifun-web.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now nomifun-web # 开机自启 + 立即启动 = 自动开 webui
sudo systemctl status nomifun-web
```
同样建议在前面摆一个 Caddy/nginx 做 TLS,并在 unit 里设 `Environment=NOMIFUN_HTTPS=true`
> ⚠️ unit 里 `Environment=NOMIFUN_DATA_DIR=/var/lib/nomifun` 必须与 `StateDirectory=nomifun` **保持一致**。若你改 unit(比如注释掉 `User`/`Group` 改用 root)时不慎删掉那行,数据会悄悄落到服务用户的按用户目录(`$XDG_DATA_HOME/NomiFun/Nomi`,通常是 `~nomifun/.local/share/NomiFun/Nomi`),与 systemd 托管的 `/var/lib/nomifun` 脱钩。
---
## 启动参数 / 环境变量
| 参数 | 环境变量 | 默认 | 说明 |
|---|---|---|---|
| `--host` | `NOMIFUN_WEB_HOST` | `127.0.0.1` | 监听地址。`0.0.0.0` 会对 LAN/VPN/公网可达;请先完成管理员设置或预置密码,公网必须放在 TLS 之后 |
| `--port` | `NOMIFUN_WEB_PORT` | `8787` | 同时服务 API、`/ws`、SPA 的端口 |
| `--data-dir` | `NOMIFUN_DATA_DIR` | 按用户目录 | 数据目录(db / 日志 / bun 缓存 / agent 状态)。默认是服务用户的按用户目录(Linux 为 `$XDG_DATA_HOME/NomiFun/Nomi`);生产请显式指定绝对路径 |
| `--dist` | `NOMIFUN_WEB_DIST` | `../../ui/dist` | SPA 静态目录。部署时**必须**显式指定 |
| — | `NOMIFUN_HTTPS` | `false` | 设 `true` 时会话/CSRF cookie 带 `Secure`(仅在 TLS 后用) |
| `--admin-user` | `NOMIFUN_ADMIN_USERNAME` | `admin` | 预置管理员用户名(仅预置时用) |
| `--admin-password` | `NOMIFUN_ADMIN_PASSWORD` | 无 | 设了就在启动时**预置**管理员、跳过交互首启 |
| `--insecure-no-auth` | `NOMIFUN_WEB_INSECURE_NO_AUTH` | `false` | ⚠️ 关闭认证(桌面式 no-auth)。只在仅 loopback / 受信私网可达时用 |
直接命令行示例:
```bash
nomifun-web --host 127.0.0.1 --port 8787 \
--data-dir /var/lib/nomifun --dist /opt/nomifun/web
```
---
## Linux 运行时依赖
| 依赖 | 必需性 | 说明 |
|---|---|---|
| glibc + ca-certificates | 必需 | sqlite 静态链接、TLS 用 rustls,**无需 openssl / libsqlite** |
| `bun` ≥ 1.3.13 | **必需** | agent 执行;1.1.38 有 stdin bug。Docker 镜像里已含 |
| `node` / `npm` / `npx` | 推荐 | 用户启用的 MCP stdio server(`npx -y …`) |
| `git` | 推荐 | skill 发现、部分内置工具 |
| `ripgrep`(`rg`) | 推荐 | 代码搜索后端(缺则退化到 `grep`) |
| DISPLAY / X11 / WebView | ❌ 不需要 | `nomifun-web` 零 GUI 依赖,纯 headless |
---
## 安全须知
- **务必走 TLS**(公网):cookie 与登录凭据明文传输会被嗅探。TLS 后记得 `NOMIFUN_HTTPS=true`
- **首启竞态**:从服务起来到你完成首启 setup 之间,理论上谁先访问谁就能占下管理员账号。要彻底关掉这个窗口,用 `NOMIFUN_ADMIN_PASSWORD` 预置;或先监听 `127.0.0.1` / 内网 / 隧道完成首启,再改为 `0.0.0.0`
- **`--insecure-no-auth` 谨慎**:它把认证完全关掉(任何能连端口的人 = 特权用户,可执行 shell / 读写文件)。只在纯 loopback 或完全受信的私网用。
- 后端有终端 / 文件 / agent 能力,等价于"远程运维 = 远程代码执行(对你自己)"——这正是产品本意,但也因此**认证 + TLS 是底线**。