Files
havline-native/app-src/docs/project-analysis-2026-09-27.md
T

984 lines
37 KiB
Markdown
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.
# Fonu 项目详细分析(v1.2.19)
> 分析日期:2026-09-27
> 分析对象:`F:\Fonu-main\Fonu-1.0.2`
> 代码版本:`VERSION = 1.2.19`,前端 `package.json = 1.2.19`,内置 Agent `0.12.7`
> 分析方式:源码静态阅读、目录与依赖统计、迁移与路由核对、构建级验证
> 验证结果:`go test ./... -count=1`、`go vet ./...`、`npm run typecheck`、`npm test` 均通过
---
## 一、结论摘要
Fonu 是面向飞牛 fnOS 和家庭 NAS 场景的「公网访问管理面板」。它把四类原本分散的运维能力收敛到一个 Go 单体和 Vue 管理后台中:
1. 本机 Nginx 反向代理与配置版本管理;
2. 多家 DNS 服务商的 DDNS 自动同步;
3. ACME 证书申请、续期、导入与下载;
4. 基于 FRP 的内网穿透,以及部署在公网 VPS 上的 `fonu-agent` 远程管理。
项目当前形态不是单纯 CRUD 面板,而是已经形成「本地控制面 + 公网 Agent + 定时任务 + SQLite 状态库 + Nginx 配置生成器」的完整运维系统。
主要优点:
- **模块边界较清楚**:`internal` 下按 `proxy`、`nginx`、`ddns`、`acme`、`frp`、`agent`、`monitor`、`backup` 等领域拆分,应用装配集中在 `internal/app/app.go`。
- **交付链路完整**:同一套源码可构建 Docker 镜像、Linux amd64/arm64 原生安装包,并内嵌双架构 `fonu-agent`。
- **关键配置具备回滚能力**:本机 Nginx 主配置、规则自定义配置,以及公网 Agent 上的 vhost 都有版本列表、差异和回滚入口。
- **自动化程度较高**:DDNS、证书续期、中国 IP 段更新、健康巡检、指标采样、自动备份均由调度器统一驱动。
- **测试基础设施已建立**:后端 90 个 Go 测试文件;前端已有 Vitest、TypeScript 严格模式与 CI 验证流程。
需要重点关注:
- **FRP 是系统复杂度中心**:`internal/frp` 约 6049 行,`internal/agent` 约 2079 行,二者共同承担 VPS 配置、进程、Nginx、证书、frps、诊断、版本与漂移检测,修改风险高于其他模块。
- **前端巨型组件明显**:`ProxyView.vue` 约 3150 行,`SettingsView.vue` 约 2144 行,`AgentView.vue` 约 1807 行,维护和样式隔离成本较高。
- **仍存在历史遗留接口引用**:前端保留 `/api/frp` 旧方法,`DdnsView.vue` 仍调用它,但后端当前只注册 `/api/frpmulti/*`;该调用落入 `catch`,会造成 DDNS 页的 FRP 提示状态始终为关闭。
- **初始管理员密码会进入系统日志**:`auth.EnsureDefaultAdmin` 将初始密码写入应用日志,日志中心的系统日志又会读取该文件,安全边界需要收紧。
- **仓库工作区有未提交改动**:当前有归档目录删除、内置 Agent 二进制更新、品牌图片更新等本地改动;分析时没有修改或回退这些内容。
---
## 二、仓库定位与目录边界
外层目录 `F:\Fonu-main` 不是项目源码仓库,而是工作区容器:
```text
F:\Fonu-main
├─ .breezell/ 工作区辅助目录
├─ .gocache-agent/ Agent 构建缓存
├─ docs/ 外层临时文档
├─ Fonu-1.0.2/ 真正的 Git 仓库与源码根目录
├─ agent-tsc.log
├─ fonu-image-build4.log
└─ frps-m1-tsc.log
```
真正的 Git 仓库位于 `F:\Fonu-main\Fonu-1.0.2`:
- 当前分支:`main`
- 远端:`origin/main`
- 最近提交:`70b891c feat(scripts): 新增 upgrade-hub.sh,仅编排文件的部署可一条命令升级`
- 当前未提交内容:
- 删除 `internal/frp-1.0.2/*`;
- 删除 `web/src/views/FrpView-1.0.2.vue`;
- 修改 `internal/frp/agentbin/fonu-agent-linux-amd64`;
- 修改 `internal/frp/agentbin/fonu-agent-linux-arm64`;
- 修改 `web/public/favicon.png`、`web/src/assets/brand/logo.png`;
- 新增 `web/public/111favicon.png`、`web/src/assets/brand/logo00.png`。
项目主体目录:
| 目录 | 作用 |
| --- | --- |
| `cmd/fonu` | 本地控制面主程序入口,嵌入前端静态资源 |
| `cmd/fonu-agent` | 公网 VPS Agent 入口 |
| `internal` | 全部业务实现,共 29 个领域包 |
| `migrations` | SQLite 迁移,共 27 个 SQL 文件 |
| `web` | Vue 3 + Vite 管理后台 |
| `deploy` | Nginx MIME 类型等部署资源 |
| `scripts` | 原生安装、升级、Agent 安装和 Linux 打包脚本 |
| `docs` | 设计、审查、部署与 FRP 文档 |
| `.github/workflows` | CI、Docker 发布、GitHub Release |
---
## 三、总体架构
```mermaid
flowchart LR
Browser[浏览器管理后台] -->|HTTP :6893 / Cookie / Bearer Token| App[fonu 控制面]
Public[公网访客] -->|HTTP/HTTPS| Nginx[本机 Nginx]
Nginx -->|反向代理| NAS[NAS 内网服务]
App --> DB[(SQLite /data/fonu.db)]
App --> FS[数据目录 /data]
App --> NginxCtl[Nginx 配置生成与重载]
App --> DDNS[Cloudflare / DNSPod / 阿里云 / 腾讯云 / 火山引擎]
App --> ACME[ACME CA]
App --> Scheduler[后台调度器]
App -->|frpc.toml / 进程管理| FRPC[本机 frpc]
FRPC -->|隧道| FRPS[公网 VPS frps]
FRPS -->|vhost| AgentNginx[VPS Nginx]
AgentNginx -->|隧道回源| Nginx
App -->|Bearer Token API :7700| Agent[fonu-agent]
Agent --> AgentNginx
Agent --> FRPS
Agent --> CertStore[VPS 证书目录]
```
运行角色:
| 角色 | 运行位置 | 职责 |
| --- | --- | --- |
| `fonu` | NAS / Docker / 原生 Linux | 管理后台、本机 Nginx、DDNS、证书、frpc、调度器、数据持久化 |
| `fonu-agent` | 公网 VPS | 管理 VPS 上的 Nginx vhost、证书副本、frps 进程与配置,并提供诊断只读接口 |
| Nginx | NAS 与 VPS 两端 | NAS 侧负责反代与流量入口;VPS 侧负责公网反代和 frps vhost 回源 |
| frpc / frps | NAS / VPS | 在无公网 IP 时建立 TCP、HTTP、HTTPS 等隧道 |
| SQLite | NAS 数据目录 | 保存账号、会话、代理规则、DDNS、证书、FRP、指标、健康、备份元数据等 |
---
## 四、技术栈与构建交付
### 4.1 后端
`go.mod` 声明 Go 1.23,模块路径为 `github.com/fonu/fonu`。本次验证环境使用 Go 1.26.2,可正常构建与测试。
关键依赖:
| 能力 | 依赖 |
| --- | --- |
| HTTP 服务 | Go 标准库 `net/http` 与 `http.ServeMux` |
| SQLite | `modernc.org/sqlite`,纯 Go,不依赖 CGO |
| ACME | `github.com/go-acme/lego/v4` |
| DNSPod | `github.com/nrdcg/dnspod-go` |
| 腾讯云 | `tencentcloud-sdk-go` |
| 火山引擎 | `volcengine-go-sdk` |
| SSH | `golang.org/x/crypto/ssh` |
| 密码哈希 | `golang.org/x/crypto/bcrypt` |
| 日志轮转 | `gopkg.in/natefinch/lumberjack.v2` |
后端有两个入口:
- `cmd/fonu/main.go`:加载配置、解析迁移目录、嵌入 `web/dist`、启动 `app.New`、监听退出信号。
- `cmd/fonu-agent/main.go`:解析监听地址、Token、Nginx 目录、证书目录、frps 路径,启动 Agent HTTP 服务。
### 4.2 前端
前端使用 Vue 3.5、TypeScript 5.7、Vite 6:
| 能力 | 依赖 |
| --- | --- |
| UI 组件 | Naive UI |
| 路由 | Vue Router |
| 图表 | ECharts + vue-echarts |
| 配置编辑器 | CodeMirror 6 |
| 拖拽排序 | SortableJS |
| 图标 | `@vicons/ionicons5` |
| 测试 | Vitest |
`web/src` 共 52 个 TypeScript / Vue 源文件。前端测试目前有 2 个 spec、13 条测试,主要集中在格式化与状态映射纯函数。
### 4.3 Docker 与原生安装
项目提供三种 Compose:
| 文件 | 场景 | 网络 |
| --- | --- | --- |
| `docker-compose.yml` | 普通 Docker、本地源码构建 | bridge,映射 6893/18080/9443 |
| `docker-compose.host.yml` | 飞牛 fnOS 等已占用 80/443 的环境 | host 网络,默认反代端口 18080/9443 |
| `docker-compose.hub.yml` | 干净机器只保留一个编排文件 | BuildKit 直接从 Git 仓库拉源码构建 |
Dockerfile 的主要流程:
1. 从根目录 `VERSION` 读取版本;
2. 构建前端;
3. 交叉编译 Linux amd64 / arm64 `fonu-agent`;
4. 将前端 `dist` 复制到 `cmd/fonu/web/dist` 供 `go:embed`;
5. 编译 `fonu` 主程序;
6. 运行时镜像安装 Nginx、CA 证书、tzdata;
7. 默认监听 6893,暴露 80/443,数据目录为 `/data`。
注意:当前 Dockerfile 不预装 `frpc`,需要用户在「应用配置」中按需下载。旧版 README 或部署文档中若仍表述为“镜像内置 frpc”,应按当前实现校正。
### 4.4 版本一致性
当前版本事实:
| 位置 | 值 |
| --- | --- |
| `VERSION` | `1.2.19` |
| `web/package.json` | `1.2.19` |
| `AgentVersion` | `0.12.7` |
| `internal/version.Version` | 构建时通过 `-ldflags` 注入,默认 `dev` |
前端构建时优先读取 `VITE_APP_VERSION`,否则回落到 `package.json`。Dockerfile 将 `VERSION` 注入前端和后端。
---
## 五、后端模块分析
### 5.1 规模概览
后端共有 235 个 `.go` 文件,其中 90 个是 `_test.go`。按非测试源码粗略统计,主要模块规模如下:
| 包 | 文件数 | 约行数 | 职责 |
| --- | ---: | ---: | --- |
| `frp` | 23 | 6049 | FRP 多服务端、frpc 运行、Agent 调用、诊断、健康、漂移、版本 |
| `api` | 22 | 3241 | HTTP 路由、处理器、中间件、响应封装 |
| `ddns` | 14 | 2379 | 多服务商 DDNS、记录同步、凭证、Zone 解析 |
| `nginx` | 23 | 2186 | Nginx 配置生成、校验、启动、重载、版本与回滚 |
| `agent` | 4 | 2079 | 公网 VPS Agent 的全部接口与配置写入 |
| `notify` | 7 | 1418 | 通知配置、加密存储、邮件、Webhook、Telegram 与国内 IM |
| `acme` | 8 | 1370 | ACME 申请、续期、导入、导出、任务日志 |
| `proxy` | 3 | 1015 | 反代规则模型、SQLite Store 与端口冲突校验 |
| `service` | 3 | 595 | 反代与 Nginx 配置的业务服务层 |
| `monitor` | 1 | 559 | 资源、隧道、服务端、Agent、配置漂移巡检 |
| `logstore` | 4 | 685 | 系统日志、访问日志、错误日志读取与显示转换 |
| `traffic` | 3 | 421 | Nginx access log 跟踪、流量聚合、在线连接 |
| `backup` | 3 | 401 | 导出、恢复、校验、自动备份与保留策略 |
| `certificate` | 2 | 353 | 证书元数据 Store 与导入 |
| `auth` | 2 | 334 | 管理员、会话、API Token 验证 |
| `chinacidr` | 1 | 301 | 中国 IP 段下载、生成与 Nginx 联动 |
| `metrics` | 1 | 231 | 指标历史写入、查询与分桶 |
| `config` | 1 | 201 | 环境变量、数据目录、会话密钥 |
| `settings` | 1 | 193 | 设置项读写、默认值与类型转换 |
| `fsutil` | 1 | 164 | 原子写、备份、版本列表与回滚基础 |
### 5.2 应用装配:`internal/app/app.go`
`app.New` 是理解整个系统的入口,按顺序完成:
1. 创建日志目录与 JSON 日志;
2. 创建数据、Nginx、日志、证书、FRP 目录;
3. 解析或自动生成会话密钥;
4. 打开 SQLite 并执行迁移;
5. 创建 AES-GCM 凭据加密盒;
6. 初始化管理员账号;
7. 创建 proxy、settings、ddns、certificate、nginx 等 Store / Service;
8. 创建 FRP 多服务端服务;
9. 创建 HTTP 路由;
10. 创建调度器与流量采集器;
11. 启动 DDNS、ACME、中国 IP、巡检、自动备份任务;
12. 初始化 Nginx 配置并恢复 frpc 运行状态;
13. 启动 HTTP 服务,退出时关闭 Nginx、frpc 和数据库。
这种集中装配方式让依赖关系清晰,但也使 `app.go` 成为高耦合入口。新增模块时应继续通过构造参数或小型接口注入,避免在 `app.New` 中堆积具体职责。
### 5.3 配置与数据目录:`internal/config`
关键环境变量:
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `FONU_LISTEN` | `:6893` | 管理后台监听地址 |
| `FONU_DATA_DIR` | `/data` 或当前目录 `.data` | 数据根目录 |
| `FONU_SESSION_SECRET` | 空 | 会话签名与凭据加密密钥;空时自动生成 |
| `FONU_NGINX_BIN` | `nginx` | Nginx 可执行文件 |
| `FONU_NGINX_PID` | `<DataDir>/nginx/nginx.pid` | Nginx PID |
| `FONU_NGINX_MIME_TYPES` | `/etc/nginx/mime.types` 或仓库资源 | MIME 类型文件 |
| `FONU_NGINX_HTTP_PORT` | `80` | 新建 HTTP 反代默认端口 |
| `FONU_NGINX_HTTPS_PORT` | `443` | 新建 HTTPS 反代默认端口 |
| `FONU_FRPC_BIN` | `frpc` | frpc 可执行文件 |
| `FONU_FRPC_PID` | `<DataDir>/frp/frpc.pid` | frpc PID 文件 |
会话密钥策略值得肯定:
- 显式配置时优先使用;
- 未配置时生成 32 字节随机值;
- 持久化为 `<DataDir>/session_secret`,权限 0600;
- 使用临时文件加改名写入,避免半文件;
- 已经使用历史默认值时只提示,不自动替换,避免历史密文全部无法解密。
数据目录典型结构:
```text
<DataDir>/
├─ fonu.db
├─ session_secret
├─ ssh_known_hosts
├─ nginx/
│ ├─ nginx.conf
│ ├─ rules/
│ ├─ custom/
│ ├─ backups/
│ ├─ errors/
│ └─ china_cidr.conf
├─ certs/
├─ frp/
│ ├─ frpc.toml
│ └─ frpc.pid
├─ logs/
│ ├─ app.log
│ ├─ access.log
│ ├─ error.log
│ └─ frpc.log
└─ backups/
```
### 5.4 数据库:`internal/db`
SQLite 初始化参数:
- `foreign_keys(1)`:启用外键;
- `busy_timeout(5000)`:锁等待 5 秒;
- `SetMaxOpenConns(1)`:单连接串行访问。
单连接是保守且适合 NAS 单机场景的选择,能避免多连接写冲突,但所有 API、调度任务和日志查询都共享一个数据库连接。当历史指标、健康事件和流量查询增大时,响应延迟可能互相影响。
迁移系统特点:
- 按文件名前缀数字排序;
- 每个迁移在独立事务中执行;
- 成功后写入 `schema_migrations`;
- 已应用版本跳过;
- 迁移文件不嵌入二进制,运行时通过 `ResolveMigrationsDir` 在多个候选路径查找。
### 5.5 认证与凭据
认证能力分三类:
1. 管理员账号:bcrypt 哈希;
2. 浏览器会话:32 字节随机 Token,数据库保存,默认 7 天;
3. API Token:随机明文只返回一次,数据库保存 SHA-256,支持 `read` / `write` 范围。
登录保护:
- 同一 IP 在 5 分钟内失败 5 次后返回 429;
- 登录失败会进入通知统计;
- 修改密码后删除该账号的其他会话,保留当前会话;
- Cookie 使用 `HttpOnly`、`SameSite=Lax`,并根据直连 TLS 或 `X-Forwarded-Proto` 设置 `Secure`。
凭据加密:
- 使用会话密钥经 SHA-256 派生 AES-256-GCM;
- 加密 DDNS、FRP、通知、SSH、Dashboard 和 OIDC 等敏感字段;
- 部分凭据 API 只返回 `has_token` / `has_password` 等状态,不回传明文。
---
## 六、数据库模型
### 6.1 表清单
| 表 | 迁移 | 作用 |
| --- | --- | --- |
| `schema_migrations` | 001 | 迁移版本 |
| `admins` | 001 | 管理员账号 |
| `sessions` | 001 | 浏览器会话 |
| `api_tokens` | 025 | API 脚本令牌 |
| `proxy_rules` | 001 / 006 / 010-014 | 本机反向代理规则 |
| `proxy_hosts` | 006 / 010 | 规则域名与监听端口 |
| `proxy_custom_endpoints` | 024 | 自定义 Nginx 规则解析出的域名端口占用 |
| `proxy_traffic` | 009 | 规则累计流量 |
| `ddns_configs` | 002-008 / 022 | DDNS 任务、记录、凭证、自定义 IP |
| `certificates` | 002 / 004 / 005 | 证书元数据、CA、多域名 |
| `settings` | 001 | 键值设置 |
| `frp_servers` | 015-016 / 020 / 023 | FRP 服务端、认证、Dashboard、Agent、SSH |
| `frp_proxies` | 015 / 018 / 021 | FRP 穿透规则与扩展选项 |
| `frp_runtime_status` | 015 / 019 | frpc 期望状态、进程状态、连接状态 |
| `frp_server_diagnostics` | 017 | 服务端延迟与可用性采样 |
| `route_health_events` | 026 | 公网规则状态变更事件 |
| `metrics_samples` | 027 | 本机与规则指标历史 |
### 6.2 数据设计亮点
- `proxy_hosts(hostname, listen_port)` 唯一,避免同一域名与端口被多条规则占用。
- `proxy_custom_endpoints` 单独记录自定义 Nginx 配置的监听端点,保证自定义模式也能参与冲突校验。
- `frp_proxies.type` 在 021 迁移中扩展到 8 种类型,并为 TCP / UDP 远程端口分别建立唯一索引。
- `frp_runtime_status` 将“用户期望状态”和“实际进程/连接状态”分开,有助于区分手动停止和异常掉线。
- `route_health_events` 只记录状态变化,不保存每轮原始采样,降低长期数据量。
- `metrics_samples` 采用 `(scope, metric, value, at)` 纵向结构,增加指标不需要改表。
### 6.3 数据生命周期
| 数据 | 清理方式 |
| --- | --- |
| 会话 | 校验过期时删除;修改密码时删除其他会话 |
| 健康事件 | 巡检按保留期清理 |
| 指标采样 | 巡检按保留期清理 |
| 备份归档 | 按设置中的保留份数剪枝 |
| Nginx 配置版本 | 默认保留最近 10 个有效变化 |
| 日志 | 由 `logstore` 与轮转策略处理 |
---
## 七、HTTP API 与认证面
### 7.1 路由规模
当前 `internal/api/router.go` 中:
- 受保护路由约 113 条:使用 `SessionMiddleware + RequireAuth`;
- 匿名路由 5 条:版本、登录状态、登录、退出、公开状态页;
- 静态资源与 SPA fallback:非 `/api/` 路由全部交给前端。
匿名接口:
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/api/version` | 版本与默认反代端口 |
| GET | `/api/auth/status` | 是否初始化、是否已登录 |
| POST | `/api/auth/login` | 登录 |
| POST | `/api/auth/logout` | 退出 |
| GET | `/api/status-page` | 默认关闭的只读状态页 |
### 7.2 API 分组
| 分组 | 代表路径 | 能力 |
| --- | --- | --- |
| 认证 | `/api/auth/*` | 登录、退出、改密 |
| API Token | `/api/settings/tokens` | 创建、列出、撤销 |
| 仪表盘 | `/api/status` | 公网 IP、DDNS、证书、反代、资源和请求趋势 |
| 本机反代 | `/api/proxies*` | 规则 CRUD、排序、流量、连接、实时日志 |
| Nginx 配置 | `/api/proxies/{id}/nginx`、`/api/settings/nginx/*` | 自动/自定义配置、校验、回滚、版本 |
| DDNS | `/api/ddns*` | 任务、测试、全部更新、单任务更新 |
| 证书 | `/api/certificates*` | CA 选项、申请、SSE 日志、导入、续期、下载、删除 |
| 设置 | `/api/settings` | 常规、通知、安全、高级设置 |
| 日志 | `/api/logs/*` | 访问、错误、系统、SSE |
| 指标 | `/api/metrics/history` | 分桶历史趋势 |
| 备份 | `/api/backup*` | 导出、恢复、归档、下载、删除 |
| 服务发现 | `/api/discovery/scan` | 扫描常见 NAS 服务端口 |
| FRP 多服务端 | `/api/frpmulti/*` | 服务端、frpc、规则、诊断、版本下载 |
| 公网 Agent | `/api/frpmulti/servers/{id}/agent/*` | 状态、Nginx、frps、证书、路由、漂移、指标 |
### 7.3 Agent API
`fonu-agent` 当前提供 20 个 `/api/v1/*` 接口,全部使用 Bearer Token:
| 类别 | 接口 |
| --- | --- |
| 基础 | `ping`、`status` |
| 路由 | 列表、写入、详情、删除、版本、回滚 |
| Nginx | 安装、重载、日志 |
| frps | 配置读取与写入、安装、启停重启 |
| 证书 | 写入与列表 |
| 中国 IP | `PUT /china-cidr` |
| 指标 | `GET /metrics` |
Agent 侧的重要安全措施:
- SSH 安装使用 TOFU 主机密钥校验,首次记录,后续变更拒绝;
- frps 下载只允许官方地址和固定加速前缀;
- frps 安装包下载后校验 SHA-256;
- Basic Auth 使用纯 Go apr1,不依赖 VPS 上的 `openssl`;
- 写 vhost 前保留版本,`nginx -t` 失败时恢复上一个可用配置。
---
## 八、前端结构
### 8.1 路由与页面
前端共 11 条路由定义:
| 路径 | 名称 | 是否公开 | 页面 |
| --- | --- | --- | --- |
| `/setup` | - | 重定向 | 跳转登录 |
| `/login` | `login` | 是 | 登录 |
| `/status` | `status` | 是 | 公开状态页 |
| `/dashboard` | `dashboard` | 否 | 仪表盘 |
| `/ddns` | `ddns` | 否 | DDNS |
| `/certificates` | `certificates` | 否 | 证书管理 |
| `/proxies` | `proxies` | 否 | 本机反向代理 |
| `/frp` | `frp` | 否 | 内网穿透 |
| `/frp-agent` | `frp-agent` | 否 | 公网服务端 |
| `/public-proxy` | `public-proxy` | 否 | 公网反代 |
| `/logs` | `logs` | 否 | 日志中心 |
| `/settings` | `settings` | 否 | 系统设置 |
路由守卫在每次受保护页面跳转前请求 `/api/auth/status`。公开状态页不做登录检查,避免匿名访问时产生无意义请求。
### 8.2 页面规模
| 文件 | 约行数 | 主要职责 |
| --- | ---: | --- |
| `ProxyView.vue` | 3150 | 本机反代规则、详情、日志、Nginx 配置 |
| `SettingsView.vue` | 2144 | 常规、通知、安全、高级、备份、Token |
| `AgentView.vue` | 1807 | 公网服务端、Agent、frps 生命周期、诊断 |
| `CertificatesView.vue` | 1685 | 证书列表、申请、续期、下载、导入 |
| `DashboardView.vue` | 1679 | 状态卡、趋势、健康、流量 |
| `DdnsView.vue` | 1568 | DDNS 任务、记录、服务商配置 |
| `FrpView.vue` | 1406 | frpc 服务端和穿透规则 |
| `ProxyRemoteView.vue` | 1150 | 公网反代部署、漂移、健康、版本 |
| `LogsView.vue` | 860 | 系统、访问、错误、FRP 日志 |
这些页面都由 Composition API 直接承载大量状态和逻辑。当前结构适合快速迭代,但需要继续拆分:
- `ProxyView.vue`:拆规则表单、Nginx 面板、客户端面板、日志面板;
- `AgentView.vue`:拆服务端表单、日志弹窗、详情弹窗、延迟图;
- `SettingsView.vue`:拆通知、安全、备份、Token、状态页卡片。
### 8.3 API 客户端
`web/src/api/client.ts` 统一处理:
- `credentials: include`;
- JSON 请求体;
- 30 秒普通请求超时;
- 10 分钟证书操作超时;
- 错误信息归一化;
- Blob 下载与文件名解析。
`web/src/api/types.ts` 约 923 行,集中定义后端响应结构。类型覆盖较全,但仍保留旧 FRP 单体接口类型,形成历史冗余。
### 8.4 前端测试
已有:
- `web/src/utils/status.spec.ts`:状态值到 UI 状态的映射回归;
- `web/src/utils/format.spec.ts`:格式化函数。
缺口:
- API 客户端无测试;
- 页面组件无测试;
- 轮询与可见性行为无测试;
- 配置差异、回滚、表单校验等高风险交互仍只能依赖类型检查与人工验证。
---
## 九、核心业务链路
### 9.1 本机反向代理
数据流:
```text
前端保存规则
→ proxy.Store 校验域名与端口唯一性
→ 写入 proxy_rules / proxy_hosts
→ 生成 Nginx 配置
→ nginx -t 校验
→ 热重载或重启
→ 写入流量采集与日志
```
要点:
- 规则支持多域名、多端口、IPv4/IPv6 监听、HTTPS 与 HTTP 跳转;
- 安全设置保存在 `security_json`,包括 IP 黑白名单、仅中国大陆、Basic Auth、限流、连接数限制、安全响应头等;
- 自动模式由模板生成,自定义模式允许编辑但会解析监听端点参与冲突校验;
- 配置写入前保留 `.bak` 与 `.versions`,失败不覆盖当前可用配置;
- Nginx 配置版本支持列表、读取、差异对比和回滚。
### 9.2 DDNS
支持的服务商:
- Cloudflare;
- DNSPod;
- 阿里云 DNS;
- 腾讯云 DNS;
- 火山引擎 DNS。
能力:
- 单任务多根域名 / 多记录;
- 最长后缀 Zone 匹配;
- IPv4 / IPv6;
- 自定义固定 IP;
- 手动测试与全部更新;
- 定时任务按设置间隔运行,默认 5 分钟;
- 凭证加密保存;
- IP 变化、失败等情况进入通知系统。
### 9.3 证书
证书链路:
```text
选择 CA 与域名
→ 复用 DDNS 凭证
→ DNS-01 挑战
→ lego 申请
→ 写入证书目录
→ 更新 certificates 表
→ 重载 Nginx
→ 前端通过 SSE 获取任务日志
```
当前 CA 相关能力:
- Let's Encrypt;
- ZeroSSL;
- LiteSSL;
- LiteSSL 强制 EAB;
- 可配置 DNS 递归解析器,避免容器内 `127.0.0.11` 无法解析权威 NS;
- 支持导入 PEM、下载 ZIP / 证书 / 私钥;
- 每天执行续期巡检,按到期阈值提醒。
### 9.4 FRP 与公网 Agent
FRP 模块当前支持:
- 多台公网 frps;
- 8 种穿透类型:tcp、udp、http、https、tcpmux、stcp、sudp、xtcp;
- frpc 多版本下载、启用与删除;
- frpc 配置生成与进程管理;
- frps.toml 同源生成;
- 服务端认证方式:token、none、OIDC;
- frpc 流量、连接数与速率;
- 服务端 TCP / 登录 / 代理 / DNS / 归属地诊断;
- SSH 一键安装、升级、卸载 `fonu-agent`;
- Agent 状态、Nginx、frps 配置、证书、日志、指标;
- 公网反代 vhost 部署、漂移检测、版本与回滚;
- 四段健康检查:DNS、证书、隧道、回源服务。
主要风险点:
- `internal/frp/service.go` 和 `internal/agent/server.go` 都是超大文件;
- 宿主机 Nginx 模板与 Agent Nginx 模板存在两份实现,需要人工保持同步;
- Agent 与主程序之间存在版本兼容契约,旧 Agent 通过 404/405 映射升级提示,但接口演进仍需谨慎;
- VPS 上的系统环境差异大,尤其是 Nginx 安装位置、systemd、宝塔环境与 PATH。
### 9.5 监控、健康与通知
调度器每 2 分钟执行 `monitor`:
- 本机 CPU、内存、磁盘;
- 资源阈值;
- 本机规则流量;
- 公网规则 DNS / 证书 / 隧道 / 服务四段健康;
- frps 服务端掉线;
- Agent 不可达;
- 公网 vhost 配置漂移。
通知渠道:
- SMTP 邮件;
- Webhook;
- Telegram;
- Bark;
- ntfy;
- Gotify;
- 企业微信;
- 钉钉;
- 飞书。
告警策略已具备:
- 连续失败阈值;
- 恢复通知;
- 资源告警冷却;
- 配置漂移冷却;
- 登录失败窗口统计。
### 9.6 指标、日志与流量
指标:
- 本机资源与每条公网规则流量写入 `metrics_samples`;
- 查询时按桶取平均,前端支持 1 小时、24 小时、7 天趋势;
- 避免每加一个指标就改表结构。
日志:
- `app.log`:应用系统日志;
- `access.log`:本机 Nginx 访问日志;
- `error.log`:本机 Nginx 错误日志;
- `frpc.log`:FRP 客户端日志;
- 日志中心支持分页、筛选和 SSE 实时访问/错误日志。
流量:
- `traffic.Collector` 跟踪 access log 增量;
- 汇总到规则和域名;
- 计算累计上下行、速率与当前连接数;
- 与监控巡检共用主机维度聚合算法,减少口径漂移。
### 9.7 备份与恢复
能力:
- 手动导出;
- 上传恢复;
- 定时自动备份;
- 服务端归档列表;
- 下载、恢复、删除;
- 恢复前校验归档;
- 按保留份数剪枝;
- 自动备份失败通知。
恢复会覆盖数据库与证书等关键数据,前端需要二次确认。由于 SQLite 正在运行,恢复后推荐重启 Fonu。
---
## 十、后台调度
`internal/app/app.go` 注册的任务:
| 任务 | 间隔 | 作用 |
| --- | --- | --- |
| DDNS | 默认 5 分钟,可在设置中调整 | 检测公网 IP 并同步 DNS |
| ACME | 24 小时 | 证书续期与到期提醒 |
| 中国 IP 段 | 默认 24 小时,可在设置中调整 | 下载并生成 Nginx geo 配置 |
| monitor | 2 分钟 | 资源、隧道、服务端、Agent、漂移巡检与指标采样 |
| backup | 24 小时 | 自动备份并按策略保留 |
调度器还负责:
- 任务 panic 恢复;
- 非正间隔跳过;
- 应用退出时通过 Context 取消。
---
## 十一、部署与运维
### 11.1 推荐部署形态
飞牛 fnOS 推荐:
```bash
docker run -d \
--name fonu \
--net=host \
-v /vol1/docker/fonu-data:/data \
--restart unless-stopped \
fonu:local
```
管理入口 `http://<NAS-IP>:6893`,默认反代端口 18080/9443,避免和系统 Nginx 抢占 80/443。
### 11.2 升级方式
三种方式:
1. 本地源码:`docker compose up -d --build`;
2. 单文件编排:保留 `docker-compose.hub.yml`,重新执行 `docker compose up -d --build`;
3. 原生安装:使用 `scripts/build-linux.ps1` 构建包,再由 `scripts/fonu-install.sh` 安装。
升级脚本:
- `scripts/upgrade.sh`:拉代码、构建、重建、健康检查,支持 `--no-pull` 和回滚;
- `scripts/upgrade-hub.sh`:仅使用编排文件的部署升级;
- `scripts/agent-install.sh`:安装 `fonu-agent`。
### 11.3 数据持久化
默认使用命名卷 `fonu-data`。如需绑定宿主机目录,在 `.env` 中设置:
```dotenv
FONU_DATA_PATH=/vol1/docker/fonu-data
```
Compose 约定以 `/` 或 `.` 开头的值解释为 bind mount,其余字符串仍是命名卷。
`FONU_SESSION_SECRET` 同时承担凭据解密,已有数据后不能随意替换,否则 DDNS、FRP、通知和 SSH 等密文可能无法解密。
---
## 十二、工程化、测试与质量门禁
### 12.1 CI
`.github/workflows/ci.yml` 已实现完整验证顺序:
1. Checkout;
2. 安装 Node 22;
3. `npm ci`;
4. `npm run build`;
5. `npm test`;
6. 将 `web/dist` 同步到 `cmd/fonu/web/dist`;
7. 安装 Go;
8. `go vet ./...`;
9. `go test ./... -count=1`;
10. `go build ./...`。
顺序不可颠倒,因为 `cmd/fonu/main.go` 使用 `//go:embed all:web/dist`,干净检出时前端产物必须先生成。
当前 CI 尚未开启 `gofmt -l` 卡口,原因是仓库仍有历史未格式化文件。建议在集中执行一次 `gofmt -w` 后开启。
### 12.2 发布工作流
- `docker-release.yml`:tag 或手动触发,构建 amd64/arm64 镜像并生成多架构 manifest;
- `github-release.yml`:根据 `CHANGELOG.md` 生成 GitHub Release。
### 12.3 测试覆盖
Go 测试分布较广,`nginx` 包测试最多,其次为 `frp`、`acme`、`api`。
当前没有测试文件的包:
- `internal/discovery`;
- `internal/secret`;
- `internal/service`;
- `internal/settings`;
- `internal/version`;
- `cmd/fonu`;
- `cmd/fonu-agent`。
优先级较高的补测:
1. `internal/service` 的规则保存、Nginx 自定义配置与回滚;
2. `internal/settings` 的类型转换、默认值和迁移;
3. `internal/secret` 的加解密与密钥错误提示;
4. 路由与中间件测试,确保新增受保护接口不会漏鉴权;
5. Agent `readLimitState`、`writeLimitState` 与配置回滚;
6. 前端 API 客户端和轮询组合式函数。
### 12.4 本次验证
本次分析实际执行:
```bash
go test ./... -count=1
go vet ./...
cd web && npm run typecheck
cd web && npm test
```
结果:
- Go:所有含测试包通过;
- Go vet:无输出,退出码 0;
- 前端类型检查:通过;
- 前端测试:2 个测试文件、13 条测试全部通过。
### 12.5 格式化
仓库目前仍存在历史未格式化文件。本次只做过读取和测试,没有批量执行 `gofmt -w`,以免把格式化噪音混入用户当前未提交的改动。
---
## 十三、当前风险与技术债
### 13.1 功能与兼容性
1. **旧 `/api/frp` 客户端残留**
- `web/src/api/client.ts` 仍定义 `getFRP`、`saveFRP`、`getFRPStatus`、`syncFRPDomains`、`getFRPLogs`;
- `web/src/views/DdnsView.vue` 在 `init()` 中调用 `api.getFRP()`;
- 后端当前只注册 `/api/frpmulti/*`,因此该请求失败后进入 `catch`,`frpEnabled` 被设为 `false`;
- 影响:DDNS 页可能无法显示与 FRP 相关的提示;
- 建议:删除旧类型与旧方法,或改为调用 `/api/frpmulti/status`。
2. **Agent 版本兼容需要长期维护**
- 主程序通过 404/405 识别旧 Agent,并提示升级;
- 新接口应继续沿用最低版本提示,避免前端只看到网络错误。
3. **宿主机与 Agent 的 Nginx 模板双份实现**
- 一边是 `internal/nginx`,一边是 `internal/agent`;
- 修改一处忘记另一处会造成本机与公网行为不一致;
- 建议抽共享模板包,至少共享安全指令、限流、geo、WebSocket、TLS 片段。
### 13.2 安全
1. **初始管理员密码写入应用日志**
- `auth.EnsureDefaultAdmin` 记录用户名和密码;
- `logstore.appendSystemAttrs` 会把密码原样拼进系统日志;
- `GET /api/logs/system` 可读取该系统日志;
- 建议只在进程首次启动时输出到终端一次,不写入持久日志;或生成一次性初始密码文件并设置 0600。
2. **Agent 默认明文 HTTP 监听**
- 默认 `:7700`,Bearer Token 直接在网络中传输;
- 如果直接暴露公网,需要 HTTPS、内网限制或前置反向代理;
- 建议默认绑定 `127.0.0.1`,由 Nginx 或隧道暴露,并把公网绑定作为显式选项。
3. **Agent Token 比较未使用常量时间**
- Agent 使用字符串比较;
- 建议使用 `crypto/subtle.ConstantTimeCompare`。
4. **Agent 限流状态反序列化失败会静默清空**
- `readLimitState` 忽略 JSON 错误;
- 后续写入可能删除 zone 文件;
- 建议解析失败返回错误并保留原文件。
5. **备份上传仍允许 64MB**
- 已有 `CreateTemp` 修复路径穿越;
- 仍建议降低上限并增加频率限制。
### 13.3 性能与并发
1. **SQLite 单连接**
- 当前实现简单可靠,但监控查询、日志、指标与 API 会竞争同一连接;
- 可通过缩短事务、减少大查询、为高频读增加轻量缓存来缓解。
2. **前端大页面与高频轮询**
- 多个页面存在 15 秒轮询,仪表盘流量轮询更快;
- 轮询请求应加入 in-flight 互斥,并在页面隐藏时暂停;
- 可以统一抽 `useVisibilityPolling`。
3. **巨型后端文件**
- `internal/frp/service.go` 约 1805 行;
- `internal/agent/server.go` 约 1551 行;
- 建议按能力拆分文件,例如 rate limit、nginx template、frps install、route versions、diagnostics。
### 13.4 维护性
1. **前端样式与弹窗外壳重复**
- 多个页面重复实现左列表、弹窗外壳和状态色;
- 建议抽 `SidebarList.vue`、`ModalShell.vue` 和统一状态令牌。
2. **硬编码颜色仍需收敛**
- `variables.css` 已有品牌令牌;
- 页面中仍有大量直接色值与重复色板。
3. **旧文档时效**
- `project-audit-2026-09-24.md` 基于 v1.2.6,其中很多 P0/P1 已实施;
- 阅读旧审查时应以 CHANGELOG、当前源码和本文档为准。
4. **归档目录删除尚未提交**
- `internal/frp-1.0.2` 与 `FrpView-1.0.2.vue` 在工作树中已删除但未提交;
- 分析时保持原样,没有回退或提交。
---
## 十四、建议改进路线
### P0:先处理明确问题
1. 修正 `DdnsView` 对旧 `/api/frp` 的调用,切到 `/api/frpmulti/status`。
2. 初始管理员密码不再写入持久应用日志。
3. Agent Token 比较改为常量时间。
4. `readLimitState` 解析失败时拒绝覆盖原状态。
5. 明确 Agent 默认监听策略,最好默认仅本机或要求 HTTPS。
### P1:降低核心模块风险
1. 抽共享 Nginx 模板包,统一本机与 Agent 的安全、限流、geo、TLS 逻辑。
2. 拆分 `internal/frp/service.go` 与 `internal/agent/server.go`。
3. 拆分 `ProxyView.vue`、`SettingsView.vue`、`AgentView.vue`。
4. 为路由鉴权、配置回滚、Agent 版本兼容补测试。
5. 前端轮询统一抽 composable,加入互斥与页面可见性控制。
### P2:工程质量提升
1. 全仓执行一次 `gofmt -w`,随后在 CI 中开启 `gofmt -l` 卡口。
2. 清理旧 FRP 类型、旧 API 方法和未使用样式。
3. 收敛颜色令牌与重复弹窗外壳。
4. 增加前端 API 客户端测试与关键页面组件测试。
5. 为 SQLite 高频查询建立基准,确认单连接是否满足长期指标数据。
### P3:能力增强
1. 加密迁移包:导出并导入含敏感凭据的完整实例。
2. 审计日志:记录谁在何时修改了规则、服务端、证书和设置。
3. 2FA / TOTP 与登录设备管理。
4. SMART 磁盘健康与 UPS 状态。
5. Prometheus `/metrics` 导出。
6. 状态页事故记录与服务入口聚合页。
---
## 十五、关键源码索引
| 目标 | 文件 |
| --- | --- |
| 应用装配 | `internal/app/app.go` |
| 配置 | `internal/config/config.go` |
| 数据库迁移 | `internal/db/db.go`、`migrations/*.sql` |
| HTTP 路由 | `internal/api/router.go` |
| 认证中间件 | `internal/api/middleware.go` |
| 登录与会话 | `internal/auth/auth.go`、`internal/api/auth_handler.go` |
| 本机反代 | `internal/proxy/proxy.go`、`internal/service/nginx_config.go` |
| Nginx 生成与重载 | `internal/nginx/*.go` |
| DDNS | `internal/ddns/*.go` |
| 证书 | `internal/acme/*.go`、`internal/certificate/*.go` |
| FRP 主服务 | `internal/frp/service.go` |
| FRP 模型 | `internal/frp/model.go` |
| Agent 客户端 | `internal/frp/agent_client.go` |
| 公网 Agent | `internal/agent/server.go`、`internal/agent/versions.go` |
| 监控巡检 | `internal/monitor/monitor.go` |
| 通知 | `internal/notify/*.go` |
| 日志 | `internal/logstore/*.go` |
| 流量 | `internal/traffic/collector.go` |
| 指标 | `internal/metrics/*.go` |
| 备份 | `internal/backup/*.go` |
| 前端路由 | `web/src/router/index.ts` |
| 前端 API | `web/src/api/client.ts`、`web/src/api/types.ts` |
| 前端布局 | `web/src/layouts/AppLayout.vue` |
| CI | `.github/workflows/ci.yml` |
| 容器构建 | `Dockerfile`、`docker-compose*.yml` |
---
## 十六、总结
Fonu 已经从一个 Nginx 反代面板发展为覆盖「本地入口、DNS、证书、内网穿透、公网 Agent、监控告警、备份恢复」的 NAS 公网访问控制面。
它的优势在于:功能完整、模块边界总体清楚、迁移与部署链路成熟、关键配置有版本和回滚、自动化任务覆盖主要运维场景。
当前最需要做的不是继续横向增加功能,而是先把 FRP / Agent 这条最复杂的链路拆小,把旧接口残留和日志泄密等明确问题清掉,再补上路由鉴权、配置回滚和 Agent 兼容测试。这样后续增加迁移包、审计、2FA、Prometheus 等能力时,才不会把复杂度继续堆进两个超大服务文件。