diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index aff01d4..74a7c88 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- # 顺序不能颠倒:cmd/fonu/main.go 用 //go:embed all:web/dist 嵌入前端,
+ # 顺序不能颠倒:cmd/havline/main.go 用 //go:embed all:web/dist 嵌入前端,
# 干净检出时该目录不存在,go vet / go build 会直接失败。
- name: Setup Node
uses: actions/setup-node@v4
@@ -39,8 +39,8 @@ jobs:
- name: Sync web dist into embed directory
run: |
- rm -rf cmd/fonu/web/dist
- cp -r web/dist cmd/fonu/web/dist
+ rm -rf cmd/havline/web/dist
+ cp -r web/dist cmd/havline/web/dist
- name: Setup Go
uses: actions/setup-go@v5
diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
index 553ee8e..c62c5e9 100644
--- a/.github/workflows/docker-release.yml
+++ b/.github/workflows/docker-release.yml
@@ -60,7 +60,7 @@ jobs:
push: true
build-args: |
VERSION=${{ steps.version.outputs.version }}
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/fonu:${{ steps.version.outputs.version }}-${{ matrix.arch }}
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/havline:${{ steps.version.outputs.version }}-${{ matrix.arch }}
labels: |
org.opencontainers.image.source=${{ github.repositoryUrl }}
org.opencontainers.image.version=${{ steps.version.outputs.version }}
@@ -91,7 +91,7 @@ jobs:
- name: Create multi-arch manifest
env:
- IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/fonu
+ IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/havline
VERSION: ${{ steps.version.outputs.version }}
run: |
if [ -z "${IMAGE}" ]; then
diff --git a/.gitignore b/.gitignore
index 2e7a2c9..1e225d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,7 @@ bin/
.data/
web/node_modules/
web/dist/
-cmd/fonu/web/dist/
+cmd/havline/web/dist/
dist/
*.db
/data/
@@ -11,11 +11,15 @@ dist/
.env.local
.DS_Store
.cursor/
+.playwright-cli/
+output/
+web/public/111favicon.png
+web/src/assets/brand/logo00.png
# scripts/ 下的本地辅助脚本不入库,但随仓库分发的 agent 安装脚本需要跟踪
scripts/*
!scripts/agent-install.sh
!scripts/build-linux.ps1
-!scripts/fonu-install.sh
+!scripts/havline-install.sh
!scripts/upgrade.sh
!scripts/upgrade-hub.sh
# 本地压缩包与导出产物不入库(如 web/src.rar)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8768a58..deb873a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [Unreleased]
+
+### 变更
+
+- **品牌全量切换为 Havline**:项目显示名、Go module、命令目录、二进制、环境变量、数据库、Cookie、Docker/Compose、Agent、Updater、脚本和当前文档统一切换到 `Havline / havline / HAVLINE_`
+- **AgentVersion 提升到 0.15.0**:Agent 安装路径、配置目录、数据目录、systemd unit、Nginx 托管标记和 API 服务标识全部切换到 `havline-agent`
+- **历史文档保留原品牌名**:`CHANGELOG.md` 历史版本、旧品牌评估文档和带日期的历史分析报告保留原始名称,作为项目沿革和 GPL 来源记录
+
## [v1.9.0] - 2026-10-01
### 新增
diff --git a/Dockerfile b/Dockerfile
index 3c27dd2..2e130e2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -37,13 +37,13 @@ COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
COPY --from=version /VERSION ./VERSION
-# 先交叉编译 fonu-agent 到 embed 目录(替换仓库中的占位文件),主程序构建时自动内嵌
-RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o internal/frp/agentbin/fonu-agent-linux-amd64 ./cmd/fonu-agent \
- && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -o internal/frp/agentbin/fonu-agent-linux-arm64 ./cmd/fonu-agent
-COPY --from=web-builder /src/web/dist ./cmd/fonu/web/dist
+# 先交叉编译 havline-agent 到 embed 目录(替换仓库中的占位文件),主程序构建时自动内嵌
+RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o internal/frp/agentbin/havline-agent-linux-amd64 ./cmd/havline-agent \
+ && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -o internal/frp/agentbin/havline-agent-linux-arm64 ./cmd/havline-agent
+COPY --from=web-builder /src/web/dist ./cmd/havline/web/dist
RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build \
- -ldflags "-X github.com/fonu/fonu/internal/version.Version=$(cat /src/VERSION | tr -d ' \t\n\r')" \
- -o /fonu ./cmd/fonu
+ -ldflags "-X github.com/havline/havline/internal/version.Version=$(cat /src/VERSION | tr -d ' \t\n\r')" \
+ -o /havline ./cmd/havline
# 一键升级 sidecar:独立容器持有 Docker Socket,主程序只通过共享 Unix Socket 调用它
FROM --platform=$BUILDPLATFORM ${GO_IMAGE} AS updater-builder
@@ -55,13 +55,13 @@ COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY cmd ./cmd
COPY internal ./internal
-RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -trimpath -o /fonu-updater ./cmd/fonu-updater
+RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -trimpath -o /havline-updater ./cmd/havline-updater
# updater 运行时:Docker CLI / Compose 与 Git 只在 sidecar 内可用
FROM ${UPDATER_IMAGE} AS updater
RUN apk add --no-cache git docker-cli-compose docker-cli-buildx ca-certificates
-COPY --from=updater-builder /fonu-updater /usr/local/bin/fonu-updater
-ENTRYPOINT ["/usr/local/bin/fonu-updater"]
+COPY --from=updater-builder /havline-updater /usr/local/bin/havline-updater
+ENTRYPOINT ["/usr/local/bin/havline-updater"]
# 运行时 stage
FROM ${RUNTIME_IMAGE}
@@ -72,18 +72,18 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
-COPY --from=go-builder /fonu /app/fonu
+COPY --from=go-builder /havline /app/havline
COPY --from=go-builder /src/web/assets/image /app/web/assets/image
COPY migrations /app/migrations
# 时区:Go 的 time.Local 取决于 TZ 环境变量,且需要镜像内存在 zoneinfo(故装上 tzdata)
ENV TZ=Asia/Shanghai \
- FONU_DATA_DIR=/data \
- FONU_LISTEN=:6893 \
- FONU_NGINX_MIME_TYPES=/etc/nginx/mime.types
+ HAVLINE_DATA_DIR=/data \
+ HAVLINE_LISTEN=:6893 \
+ HAVLINE_NGINX_MIME_TYPES=/etc/nginx/mime.types
EXPOSE 6893 80 443
VOLUME ["/data"]
-CMD ["/app/fonu"]
+CMD ["/app/havline"]
diff --git a/README.md b/README.md
index 42e87f2..559123a 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-
+
-Fonu
+Havline
让 NAS 访问更简单
@@ -24,7 +24,7 @@
## 简介
-**Fonu** 是一款主要为 **飞牛 fnOS** 编写的反代与管理工具,帮助你在 NAS 上快速搭建稳定的公网访问能力:通过 Web 界面管理 Nginx 反向代理、自动同步 DDNS、申请与续期 Let's Encrypt 证书,并在一个仪表盘里查看服务状态、访问日志与流量统计。
+**Havline** 是一款主要为 **飞牛 fnOS** 编写的反代与管理工具,帮助你在 NAS 上快速搭建稳定的公网访问能力:通过 Web 界面管理 Nginx 反向代理、自动同步 DDNS、申请与续期 Let's Encrypt 证书,并在一个仪表盘里查看服务状态、访问日志与流量统计。
无需手写 Nginx 配置,也无需在多个工具之间切换。当前开发与测试环境以飞牛 fnOS 为主,**群晖、威联通、自建 Linux 等其他环境尚未充分测试**,部署前请自行验证。
@@ -56,10 +56,10 @@
## 部署
-本地项目:三个 compose 文件均从源码构建镜像(`build: .`),不拉取任何仓库镜像,构建产物标签为 `fonu:local`。
+本地项目:三个 compose 文件均从源码构建镜像(`build: .`),不拉取任何仓库镜像,构建产物标签为 `havline:local`。
> **详细部署安装文档**:[`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md) —— 环境要求、镜像构建、三种部署方式、首次配置、FRP/DDNS/证书联动、升级回滚与故障排查。
-> **FRPS 服务端管理方案**:[`docs/frps服务端管理方案.md`](frps服务端管理方案.md) —— 公网 VPS 部署 `fonu-agent`,SSH 从本地 Fonu 安装/升级/卸载,日常通过本地管理面板调用 API 自动编排 Nginx、frps 与证书。
+> **FRPS 服务端管理方案**:[`docs/frps服务端管理方案.md`](frps服务端管理方案.md) —— 公网 VPS 部署 `havline-agent`,SSH 从本地 Havline 安装/升级/卸载,日常通过本地管理面板调用 API 自动编排 Nginx、frps 与证书。
### 快速部署(推荐 · 飞牛 fnOS · Host 模式)
@@ -67,11 +67,11 @@
```bash
docker run -d \
- --name fonu \
+ --name havline \
--net=host \
-v ./data:/data \
--restart unless-stopped \
- fonu:local
+ havline:local
```
| 入口 | 地址 |
@@ -89,7 +89,7 @@ docker run -d \
Docker Compose
```bash
-FONU_SESSION_SECRET=your-secret docker compose -f docker-compose.host.yml up -d
+HAVLINE_SESSION_SECRET=your-secret docker compose -f docker-compose.host.yml up -d
```
@@ -98,7 +98,7 @@ FONU_SESSION_SECRET=your-secret docker compose -f docker-compose.host.yml up -d
Bridge 网络(端口映射,非飞牛环境可自行尝试)
```bash
-FONU_SESSION_SECRET=your-secret docker compose -f docker-compose.hub.yml up -d --build
+HAVLINE_SESSION_SECRET=your-secret docker compose -f docker-compose.hub.yml up -d --build
```
@@ -114,7 +114,7 @@ docker compose up -d --build
### Web 一键升级
-`docker-compose.yml` 与 `docker-compose.hub.yml` 会同时启动受限的 `fonu-updater` sidecar。检测到新版本后,侧栏会出现「一键升级」入口,可在管理页面完成构建、重建与健康检查。
+`docker-compose.yml` 与 `docker-compose.hub.yml` 会同时启动受限的 `havline-updater` sidecar。检测到新版本后,侧栏会出现「一键升级」入口,可在管理页面完成构建、重建与健康检查。
> 从旧版本升级时,需要先手动更新 Compose 文件并执行一次 `docker compose up -d --build` 启动 sidecar。详见 [`docs/system-upgrade.md`](docs/system-upgrade.md)。
@@ -126,15 +126,15 @@ docker compose up -d --build
### FRP 内网穿透(无公网 IP)
-适用于无法端口映射、无公网 IPv4 的场景。Fonu 内置 **frpc**,支持**多台公网服务端**与全部 8 种穿透类型(tcp / udp / http / https / tcpmux / stcp / sudp / xtcp)及插件(static_file / socks5 / https2http 等)。
+适用于无法端口映射、无公网 IPv4 的场景。Havline 内置 **frpc**,支持**多台公网服务端**与全部 8 种穿透类型(tcp / udp / http / https / tcpmux / stcp / sudp / xtcp)及插件(static_file / socks5 / https2http 等)。
-**典型流量路径**(tcp 规则 + 本地反代):用户 → DNS(解析到 VPS)→ frps → frpc → Fonu Nginx → 内网服务
+**典型流量路径**(tcp 规则 + 本地反代):用户 → DNS(解析到 VPS)→ frps → frpc → Havline Nginx → 内网服务
#### 1. VPS 部署 frps
在服务端「详情」弹窗点 **「frps.toml」** 一键生成与 frpc 侧同源的服务端配置(bindPort / 认证 / webServer / 传输参数 / vhost 端口与防火墙清单全部对齐),复制到 VPS 保存后 `frps -c frps.toml` 启动,按文件末尾清单放行防火墙端口。
-#### 2. Fonu 配置 frpc
+#### 2. Havline 配置 frpc
在侧栏 **FRP 穿透** 页面:
@@ -153,25 +153,25 @@ docker compose up -d --build
| 变量 | 默认值 | 说明 |
| ----------------------- | ------------------------- | ------------------------------------------ |
-| `FONU_LISTEN` | `:6893` | 管理后台监听地址 |
-| `FONU_DATA_DIR` | `/data` | 数据目录(数据库、Nginx 配置、证书、日志) |
-| `FONU_SESSION_SECRET` | 空(自动生成) | 会话与凭据加密密钥;有数据后不要更换 |
-| `FONU_NGINX_HTTP_PORT` | `80` | 新建 HTTP 反代规则的默认监听端口 |
-| `FONU_NGINX_HTTPS_PORT` | `443` | 新建 HTTPS 反代规则的默认监听端口 |
-| `FONU_NGINX_BIN` | `nginx` | Nginx 可执行文件路径(本地开发可指定) |
-| `FONU_FRPC_BIN` | `frpc` | frpc 可执行文件路径(镜像不预装,用「应用配置」按需下载或自行放置) |
-| `FONU_FRP_PID` | `{DATA_DIR}/frp/frpc.pid` | frpc 进程 PID 文件路径 |
-| `FONU_UPDATER_SOCKET` | `/run/fonu-updater/updater.sock` | 一键升级 sidecar 的 Unix Socket;未挂载时自动禁用 |
+| `HAVLINE_LISTEN` | `:6893` | 管理后台监听地址 |
+| `HAVLINE_DATA_DIR` | `/data` | 数据目录(数据库、Nginx 配置、证书、日志) |
+| `HAVLINE_SESSION_SECRET` | 空(自动生成) | 会话与凭据加密密钥;有数据后不要更换 |
+| `HAVLINE_NGINX_HTTP_PORT` | `80` | 新建 HTTP 反代规则的默认监听端口 |
+| `HAVLINE_NGINX_HTTPS_PORT` | `443` | 新建 HTTPS 反代规则的默认监听端口 |
+| `HAVLINE_NGINX_BIN` | `nginx` | Nginx 可执行文件路径(本地开发可指定) |
+| `HAVLINE_FRPC_BIN` | `frpc` | frpc 可执行文件路径(镜像不预装,用「应用配置」按需下载或自行放置) |
+| `HAVLINE_FRP_PID` | `{DATA_DIR}/frp/frpc.pid` | frpc 进程 PID 文件路径 |
+| `HAVLINE_UPDATER_SOCKET` | `/run/havline-updater/updater.sock` | 一键升级 sidecar 的 Unix Socket;未挂载时自动禁用 |
## 注意事项
-1. **生产环境务必设置** `FONU_SESSION_SECRET`,不要使用默认值。
-2. **端口冲突**:宿主机已有 Nginx(如飞牛系统)占用 80/443 时,不要让 Fonu 反代规则监听 80/443;使用 `18080/9443` 或 host 模式 + 环境变量。
-3. **已有反代规则**:修改 `FONU_NGINX_HTTP_PORT` / `FONU_NGINX_HTTPS_PORT` 后,**不会自动更新**数据库中已有规则的端口,需在反代页手动修改并保存。
-4. **访问日志**:仅统计经 Fonu Nginx 反代的域名流量,不包含管理后台 `:6893` 的请求。
+1. **生产环境务必设置** `HAVLINE_SESSION_SECRET`,不要使用默认值。
+2. **端口冲突**:宿主机已有 Nginx(如飞牛系统)占用 80/443 时,不要让 Havline 反代规则监听 80/443;使用 `18080/9443` 或 host 模式 + 环境变量。
+3. **已有反代规则**:修改 `HAVLINE_NGINX_HTTP_PORT` / `HAVLINE_NGINX_HTTPS_PORT` 后,**不会自动更新**数据库中已有规则的端口,需在反代页手动修改并保存。
+4. **访问日志**:仅统计经 Havline Nginx 反代的域名流量,不包含管理后台 `:6893` 的请求。
5. **证书与 DNS**:HTTPS 申请前请确保域名 DNS 已解析到本机,且 DDNS 凭证能管理对应根域。
-6. **数据持久化**:Docker 部署请挂载 volume 到 `FONU_DATA_DIR`,避免容器重建后配置丢失。
-7. **备份**:可通过设置页导出配置;重要证书与数据库建议定期备份 `FONU_DATA_DIR` 目录。
+6. **数据持久化**:Docker 部署请挂载 volume 到 `HAVLINE_DATA_DIR`,避免容器重建后配置丢失。
+7. **备份**:可通过设置页导出配置;重要证书与数据库建议定期备份 `HAVLINE_DATA_DIR` 目录。
## 本地开发
@@ -179,9 +179,9 @@ docker compose up -d --build
# 前端热更新
cd web && npm ci && npm run dev
-# 后端(需先把 web/dist 复制到 cmd/fonu/web/dist)
+# 后端(需先把 web/dist 复制到 cmd/havline/web/dist)
cd web && npm run build
-go run ./cmd/fonu
+go run ./cmd/havline
# 测试
go test ./...
diff --git a/cmd/fonu-agent/main.go b/cmd/fonu-agent/main.go
deleted file mode 100644
index bb17fa7..0000000
--- a/cmd/fonu-agent/main.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package main
-
-import (
- "context"
- "flag"
- "fmt"
- "log"
- "os"
- "os/signal"
- "strings"
- "syscall"
-
- "github.com/fonu/fonu/internal/agent"
-)
-
-func main() {
- listen := flag.String("listen", envOr("FONU_AGENT_LISTEN", "127.0.0.1:7700"), "agent listen address")
- allowRemote := flag.Bool("allow-remote", envBool("FONU_AGENT_ALLOW_REMOTE"), "allow non-loopback listen address")
- token := flag.String("token", os.Getenv("FONU_AGENT_TOKEN"), "agent bearer token")
- nginxDir := flag.String("nginx-conf-dir", envOr("FONU_AGENT_NGINX_CONF_DIR", ""), "managed nginx config directory(留空自动探测 Nginx 实际加载的 vhost 目录)")
- certsDir := flag.String("certs-dir", envOr("FONU_AGENT_CERTS_DIR", "/var/lib/fonu-agent/certs"), "certificate directory")
- frpsConfig := flag.String("frps-config", envOr("FONU_AGENT_FRPS_CONFIG", "/etc/frp/frps.toml"), "frps config path")
- frpsBin := flag.String("frps-bin", envOr("FONU_AGENT_FRPS_BIN", "frps"), "frps binary")
- dataDir := flag.String("data-dir", envOr("FONU_AGENT_DATA_DIR", "/var/lib/fonu-agent"), "agent data directory")
- showVersion := flag.Bool("version", false, "print agent version and exit")
- flag.Parse()
- if *showVersion {
- fmt.Println("fonu-agent", agent.AgentVersion)
- return
- }
- if *token == "" {
- log.Fatal("FONU_AGENT_TOKEN or -token is required")
- }
- server, err := agent.NewServer(agent.Config{
- ListenAddr: *listen, AllowRemote: *allowRemote, Token: *token, NginxConfDir: *nginxDir,
- CertsDir: *certsDir, FrpsConfigPath: *frpsConfig, FrpsBinary: *frpsBin, DataDir: *dataDir,
- })
- if err != nil {
- log.Fatal(err)
- }
- ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
- defer stop()
- if err := server.ListenAndServe(ctx); err != nil && ctx.Err() == nil {
- log.Fatal(err)
- }
-}
-
-func envOr(key, fallback string) string {
- if value := os.Getenv(key); value != "" {
- return value
- }
- return fallback
-}
-
-func envBool(key string) bool {
- switch strings.ToLower(strings.TrimSpace(os.Getenv(key))) {
- case "1", "true", "yes", "on":
- return true
- default:
- return false
- }
-}
diff --git a/cmd/fonu-updater/main.go b/cmd/fonu-updater/main.go
deleted file mode 100644
index af84490..0000000
--- a/cmd/fonu-updater/main.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package main
-
-import (
- "context"
- "log"
- "os"
- "os/signal"
- "syscall"
-
- "github.com/fonu/fonu/internal/updater"
-)
-
-func main() {
- server, err := updater.NewServer(updater.Config{
- SocketPath: envOr("FONU_UPDATER_SOCKET", "/run/fonu-updater/updater.sock"),
- ProjectDir: envOr("FONU_UPDATE_PROJECT_DIR", "/workspace"),
- ComposeFile: envOr("FONU_UPDATE_COMPOSE_FILE", "docker-compose.yml"),
- Service: envOr("FONU_UPDATE_SERVICE", "fonu"),
- Mode: envOr("FONU_UPDATE_MODE", "local"),
- RepoURL: envOr("FONU_UPDATE_REPO", ""),
- Branch: envOr("FONU_UPDATE_BRANCH", "main"),
- AppURL: envOr("FONU_UPDATE_APP_URL", "http://fonu:6893"),
- HostProjectDir: envOr("FONU_UPDATE_HOST_DIR", ""),
- ProjectName: envOr("FONU_UPDATE_PROJECT_NAME", ""),
- SelfImage: envOr("FONU_UPDATE_SELF_IMAGE", ""),
- DockerSocket: envOr("FONU_UPDATE_DOCKER_SOCK", "/var/run/docker.sock"),
- }, nil)
- if err != nil {
- log.Fatal(err)
- }
-
- ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
- defer stop()
- if err := server.Serve(ctx); err != nil && ctx.Err() == nil {
- log.Fatal(err)
- }
-}
-
-func envOr(key, fallback string) string {
- if value := os.Getenv(key); value != "" {
- return value
- }
- return fallback
-}
diff --git a/cmd/havline-agent/main.go b/cmd/havline-agent/main.go
new file mode 100644
index 0000000..7f4322e
--- /dev/null
+++ b/cmd/havline-agent/main.go
@@ -0,0 +1,62 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "os/signal"
+ "strings"
+ "syscall"
+
+ "github.com/havline/havline/internal/agent"
+)
+
+func main() {
+ listen := flag.String("listen", envOr("HAVLINE_AGENT_LISTEN", "127.0.0.1:7700"), "agent listen address")
+ allowRemote := flag.Bool("allow-remote", envBool("HAVLINE_AGENT_ALLOW_REMOTE"), "allow non-loopback listen address")
+ token := flag.String("token", os.Getenv("HAVLINE_AGENT_TOKEN"), "agent bearer token")
+ nginxDir := flag.String("nginx-conf-dir", envOr("HAVLINE_AGENT_NGINX_CONF_DIR", ""), "managed nginx config directory(留空自动探测 Nginx 实际加载的 vhost 目录)")
+ certsDir := flag.String("certs-dir", envOr("HAVLINE_AGENT_CERTS_DIR", "/var/lib/havline-agent/certs"), "certificate directory")
+ frpsConfig := flag.String("frps-config", envOr("HAVLINE_AGENT_FRPS_CONFIG", "/etc/frp/frps.toml"), "frps config path")
+ frpsBin := flag.String("frps-bin", envOr("HAVLINE_AGENT_FRPS_BIN", "frps"), "frps binary")
+ dataDir := flag.String("data-dir", envOr("HAVLINE_AGENT_DATA_DIR", "/var/lib/havline-agent"), "agent data directory")
+ showVersion := flag.Bool("version", false, "print agent version and exit")
+ flag.Parse()
+ if *showVersion {
+ fmt.Println("havline-agent", agent.AgentVersion)
+ return
+ }
+ if *token == "" {
+ log.Fatal("HAVLINE_AGENT_TOKEN or -token is required")
+ }
+ server, err := agent.NewServer(agent.Config{
+ ListenAddr: *listen, AllowRemote: *allowRemote, Token: *token, NginxConfDir: *nginxDir,
+ CertsDir: *certsDir, FrpsConfigPath: *frpsConfig, FrpsBinary: *frpsBin, DataDir: *dataDir,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
+ defer stop()
+ if err := server.ListenAndServe(ctx); err != nil && ctx.Err() == nil {
+ log.Fatal(err)
+ }
+}
+
+func envOr(key, fallback string) string {
+ if value := os.Getenv(key); value != "" {
+ return value
+ }
+ return fallback
+}
+
+func envBool(key string) bool {
+ switch strings.ToLower(strings.TrimSpace(os.Getenv(key))) {
+ case "1", "true", "yes", "on":
+ return true
+ default:
+ return false
+ }
+}
diff --git a/cmd/havline-updater/main.go b/cmd/havline-updater/main.go
new file mode 100644
index 0000000..c462b69
--- /dev/null
+++ b/cmd/havline-updater/main.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "context"
+ "log"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "github.com/havline/havline/internal/updater"
+)
+
+func main() {
+ server, err := updater.NewServer(updater.Config{
+ SocketPath: envOr("HAVLINE_UPDATER_SOCKET", "/run/havline-updater/updater.sock"),
+ ProjectDir: envOr("HAVLINE_UPDATE_PROJECT_DIR", "/workspace"),
+ ComposeFile: envOr("HAVLINE_UPDATE_COMPOSE_FILE", "docker-compose.yml"),
+ Service: envOr("HAVLINE_UPDATE_SERVICE", "havline"),
+ Mode: envOr("HAVLINE_UPDATE_MODE", "local"),
+ RepoURL: envOr("HAVLINE_UPDATE_REPO", ""),
+ Branch: envOr("HAVLINE_UPDATE_BRANCH", "main"),
+ AppURL: envOr("HAVLINE_UPDATE_APP_URL", "http://havline:6893"),
+ HostProjectDir: envOr("HAVLINE_UPDATE_HOST_DIR", ""),
+ ProjectName: envOr("HAVLINE_UPDATE_PROJECT_NAME", ""),
+ SelfImage: envOr("HAVLINE_UPDATE_SELF_IMAGE", ""),
+ DockerSocket: envOr("HAVLINE_UPDATE_DOCKER_SOCK", "/var/run/docker.sock"),
+ }, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
+ defer stop()
+ if err := server.Serve(ctx); err != nil && ctx.Err() == nil {
+ log.Fatal(err)
+ }
+}
+
+func envOr(key, fallback string) string {
+ if value := os.Getenv(key); value != "" {
+ return value
+ }
+ return fallback
+}
diff --git a/cmd/fonu/main.go b/cmd/havline/main.go
similarity index 91%
rename from cmd/fonu/main.go
rename to cmd/havline/main.go
index ccd5c38..cf74858 100644
--- a/cmd/fonu/main.go
+++ b/cmd/havline/main.go
@@ -12,8 +12,8 @@ import (
"syscall"
"time"
- "github.com/fonu/fonu/internal/app"
- "github.com/fonu/fonu/internal/config"
+ "github.com/havline/havline/internal/app"
+ "github.com/havline/havline/internal/config"
)
//go:embed all:web/dist
diff --git a/docker-compose.host.yml b/docker-compose.host.yml
index be24451..310e5d5 100644
--- a/docker-compose.host.yml
+++ b/docker-compose.host.yml
@@ -1,8 +1,8 @@
# 飞牛 NAS 等已占用 80/443 的环境:使用 host 网络 + 高位默认反代端口
# 本地项目:直接用源码构建镜像,不从 Docker Hub 拉取
-name: fonu
+name: havline
services:
- fonu:
+ havline:
build:
context: .
args:
@@ -10,25 +10,25 @@ services:
GO_IMAGE: ${GO_IMAGE:-docker.m.daocloud.io/library/golang:1.23-bookworm}
RUNTIME_IMAGE: ${RUNTIME_IMAGE:-docker.m.daocloud.io/library/debian:bookworm-slim}
GOPROXY: ${GOPROXY:-https://goproxy.cn,direct}
- image: fonu:${FONU_VERSION:-local}
- container_name: fonu
+ image: havline:${HAVLINE_VERSION:-local}
+ container_name: havline
restart: unless-stopped
network_mode: host
volumes:
- # 数据目录:默认用命名卷 fonu-data(由 Docker 管理,位置随 Docker 数据根)。
- # 想落到指定目录就在同层 .env 里设 FONU_DATA_PATH=/vol1/docker/fonu-data ——
+ # 数据目录:默认用命名卷 havline-data(由 Docker 管理,位置随 Docker 数据根)。
+ # 想落到指定目录就在同层 .env 里设 HAVLINE_DATA_PATH=/vol1/docker/havline-data ——
# 以 / 或 . 开头的写法会被 compose 当成 bind mount,其余字符串仍是命名卷。
- - ${FONU_DATA_PATH:-fonu-data}:/data
+ - ${HAVLINE_DATA_PATH:-havline-data}:/data
environment:
- FONU_DATA_DIR: /data
- FONU_LISTEN: :6893
+ HAVLINE_DATA_DIR: /data
+ HAVLINE_LISTEN: :6893
# 留空即自动:应用会在数据目录里生成并持久化 /session_secret(0600)
# 想自己指定就设这个变量;注意它同时是凭据加密密钥,已有数据后不要随意更换
- FONU_SESSION_SECRET: ${FONU_SESSION_SECRET:-}
+ HAVLINE_SESSION_SECRET: ${HAVLINE_SESSION_SECRET:-}
# 时区:通知消息时间按此显示(默认东八区)
TZ: ${TZ:-Asia/Shanghai}
- FONU_NGINX_HTTP_PORT: ${FONU_NGINX_HTTP_PORT:-18080}
- FONU_NGINX_HTTPS_PORT: ${FONU_NGINX_HTTPS_PORT:-9443}
+ HAVLINE_NGINX_HTTP_PORT: ${HAVLINE_NGINX_HTTP_PORT:-18080}
+ HAVLINE_NGINX_HTTPS_PORT: ${HAVLINE_NGINX_HTTPS_PORT:-9443}
volumes:
- fonu-data:
+ havline-data:
diff --git a/docker-compose.hub.yml b/docker-compose.hub.yml
index 712b8c5..955c503 100644
--- a/docker-compose.hub.yml
+++ b/docker-compose.hub.yml
@@ -1,13 +1,13 @@
# 从仓库拉源码构建并启动(适合干净机器:只要这一个文件)
#
# 用法:
-# mkdir -p /opt/fonu && cd /opt/fonu
+# mkdir -p /opt/havline && cd /opt/havline
# # 取本文件(按你仓库页面上的「原始文件」链接下载,或 git clone 后复制过来):
# # curl -fsSLo docker-compose.hub.yml <仓库里这个文件的原始地址>
# docker compose up -d --build # 会话密钥由应用首次启动自动生成并持久化在数据卷里
#
# 想自己指定会话密钥(可选项):在 compose 文件同层建 .env 写入
-# FONU_SESSION_SECRET=<一串随机字符,openssl rand -hex 32 可生成>
+# HAVLINE_SESSION_SECRET=<一串随机字符,openssl rand -hex 32 可生成>
# 注意:它同时是凭据加密密钥,数据卷里已经有数据后不要随意更换,否则已存的凭据无法解密。
#
# 构建上下文用的是 Git 地址:BuildKit 会自己把仓库克隆下来,不需要先 git clone 到本地;
@@ -16,22 +16,22 @@
#
# 首次构建要拉前端与 Go 依赖,比较慢;之后靠 Dockerfile 里的 cache mount 复用缓存。
# 80/443 已被占用(如飞牛 NAS)请改用 docker-compose.host.yml。
-name: fonu
+name: havline
services:
- fonu:
+ havline:
build:
- context: http://45.207.223.190:10082/mrlee/-Fonu.git#main
+ context: http://45.207.223.190:10082/mrlee/Havline.git#main
args:
NODE_IMAGE: ${NODE_IMAGE:-docker.m.daocloud.io/library/node:22-alpine}
GO_IMAGE: ${GO_IMAGE:-docker.m.daocloud.io/library/golang:1.23-bookworm}
RUNTIME_IMAGE: ${RUNTIME_IMAGE:-docker.m.daocloud.io/library/debian:bookworm-slim}
UPDATER_IMAGE: ${UPDATER_IMAGE:-docker.m.daocloud.io/library/docker:27-cli}
GOPROXY: ${GOPROXY:-https://goproxy.cn,direct}
- image: fonu:${FONU_VERSION:-local}
- # 有 build 段时 compose 默认会先尝试拉同名镜像(日志里那条 "fonu Pulling" 就是),
+ image: havline:${HAVLINE_VERSION:-local}
+ # 有 build 段时 compose 默认会先尝试拉同名镜像(日志里那条 "havline Pulling" 就是),
# 国内直连 Docker Hub 会超时报警;强制走构建,跳过这一步。
pull_policy: build
- container_name: fonu
+ container_name: havline
restart: unless-stopped
depends_on:
- updater
@@ -40,53 +40,53 @@ services:
- "18080:80"
- "9443:443"
volumes:
- # 数据目录:默认用命名卷 fonu-data(由 Docker 管理,位置随 Docker 数据根)。
- # 想落到指定目录就在同层 .env 里设 FONU_DATA_PATH=/vol1/docker/fonu-data ——
+ # 数据目录:默认用命名卷 havline-data(由 Docker 管理,位置随 Docker 数据根)。
+ # 想落到指定目录就在同层 .env 里设 HAVLINE_DATA_PATH=/vol1/docker/havline-data ——
# 以 / 或 . 开头的写法会被 compose 当成 bind mount,其余字符串仍是命名卷。
- - ${FONU_DATA_PATH:-fonu-data}:/data
- - fonu-updater-sock:/run/fonu-updater
+ - ${HAVLINE_DATA_PATH:-havline-data}:/data
+ - havline-updater-sock:/run/havline-updater
environment:
- FONU_DATA_DIR: /data
- FONU_LISTEN: :6893
- FONU_UPDATER_SOCKET: /run/fonu-updater/updater.sock
+ HAVLINE_DATA_DIR: /data
+ HAVLINE_LISTEN: :6893
+ HAVLINE_UPDATER_SOCKET: /run/havline-updater/updater.sock
# 留空即自动:应用会在数据目录里生成并持久化 /session_secret(0600)
# 想自己指定就在同层 .env 里设这个变量;注意它同时是凭据加密密钥,已有数据后不要更换
- FONU_SESSION_SECRET: ${FONU_SESSION_SECRET:-}
+ HAVLINE_SESSION_SECRET: ${HAVLINE_SESSION_SECRET:-}
# 时区:通知消息时间按此显示(默认东八区)
TZ: ${TZ:-Asia/Shanghai}
# 受限升级 sidecar:只有它持有 Docker Socket,主程序通过共享 Unix Socket 触发
updater:
build:
- context: http://45.207.223.190:10082/mrlee/-Fonu.git#main
+ context: http://45.207.223.190:10082/mrlee/Havline.git#main
target: updater
args:
GO_IMAGE: ${GO_IMAGE:-docker.m.daocloud.io/library/golang:1.23-bookworm}
UPDATER_IMAGE: ${UPDATER_IMAGE:-docker.m.daocloud.io/library/docker:27-cli}
GOPROXY: ${GOPROXY:-https://goproxy.cn,direct}
- image: fonu-updater:${FONU_VERSION:-local}
+ image: havline-updater:${HAVLINE_VERSION:-local}
pull_policy: build
- container_name: fonu-updater
+ container_name: havline-updater
restart: unless-stopped
volumes:
- ${DOCKER_SOCK:-/var/run/docker.sock}:/var/run/docker.sock
- - ${FONU_UPDATE_HOST_DIR:-.}:/workspace
- - fonu-updater-sock:/run/fonu-updater
+ - ${HAVLINE_UPDATE_HOST_DIR:-.}:/workspace
+ - havline-updater-sock:/run/havline-updater
environment:
- FONU_UPDATER_SOCKET: /run/fonu-updater/updater.sock
- FONU_UPDATE_PROJECT_DIR: /workspace
- FONU_UPDATE_COMPOSE_FILE: docker-compose.hub.yml
- FONU_UPDATE_SERVICE: fonu
- FONU_UPDATE_MODE: git
- FONU_UPDATE_REPO: http://45.207.223.190:10082/mrlee/-Fonu.git
- FONU_UPDATE_BRANCH: main
- FONU_UPDATE_APP_URL: http://fonu:6893
- FONU_UPDATE_HOST_DIR: ${FONU_UPDATE_HOST_DIR:-}
- FONU_UPDATE_PROJECT_NAME: ${FONU_UPDATE_PROJECT_NAME:-fonu}
- FONU_UPDATE_SELF_IMAGE: fonu-updater:${FONU_VERSION:-local}
- FONU_UPDATE_DOCKER_SOCK: ${DOCKER_SOCK:-/var/run/docker.sock}
+ HAVLINE_UPDATER_SOCKET: /run/havline-updater/updater.sock
+ HAVLINE_UPDATE_PROJECT_DIR: /workspace
+ HAVLINE_UPDATE_COMPOSE_FILE: docker-compose.hub.yml
+ HAVLINE_UPDATE_SERVICE: havline
+ HAVLINE_UPDATE_MODE: git
+ HAVLINE_UPDATE_REPO: http://45.207.223.190:10082/mrlee/Havline.git
+ HAVLINE_UPDATE_BRANCH: main
+ HAVLINE_UPDATE_APP_URL: http://havline:6893
+ HAVLINE_UPDATE_HOST_DIR: ${HAVLINE_UPDATE_HOST_DIR:-}
+ HAVLINE_UPDATE_PROJECT_NAME: ${HAVLINE_UPDATE_PROJECT_NAME:-havline}
+ HAVLINE_UPDATE_SELF_IMAGE: havline-updater:${HAVLINE_VERSION:-local}
+ HAVLINE_UPDATE_DOCKER_SOCK: ${DOCKER_SOCK:-/var/run/docker.sock}
TZ: ${TZ:-Asia/Shanghai}
volumes:
- fonu-data:
- fonu-updater-sock:
+ havline-data:
+ havline-updater-sock:
diff --git a/docker-compose.yml b/docker-compose.yml
index 5d2ffd0..676d99b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,12 +1,12 @@
# 固定项目名:不改的话,改了目录名/项目名后 compose 会把它当成新项目,
-# fonu-data 会变成新空卷(看起来像「数据丢了」)。
-name: fonu
+# havline-data 会变成新空卷(看起来像「数据丢了」)。
+name: havline
# 本地项目:用源码构建镜像(镜像名随 VERSION 变,便于回滚;也便于导出 / 备份)
# 升级:docker compose up -d --build(或在部署机跑 scripts/upgrade.sh)
-# 回滚:FONU_VERSION=上一个版本号 docker compose up -d(镜像 tag 还在本地)
+# 回滚:HAVLINE_VERSION=上一个版本号 docker compose up -d(镜像 tag 还在本地)
services:
- fonu:
+ havline:
build:
context: .
args:
@@ -15,8 +15,8 @@ services:
RUNTIME_IMAGE: ${RUNTIME_IMAGE:-docker.m.daocloud.io/library/debian:bookworm-slim}
UPDATER_IMAGE: ${UPDATER_IMAGE:-docker.m.daocloud.io/library/docker:27-cli}
GOPROXY: ${GOPROXY:-https://goproxy.cn,direct}
- image: fonu:${FONU_VERSION:-local}
- container_name: fonu
+ image: havline:${HAVLINE_VERSION:-local}
+ container_name: havline
restart: unless-stopped
depends_on:
- updater
@@ -25,18 +25,18 @@ services:
- "18080:80"
- "9443:443"
volumes:
- # 数据目录:默认用命名卷 fonu-data(由 Docker 管理,位置随 Docker 数据根)。
- # 想落到指定目录就在同层 .env 里设 FONU_DATA_PATH=/vol1/docker/fonu-data ——
+ # 数据目录:默认用命名卷 havline-data(由 Docker 管理,位置随 Docker 数据根)。
+ # 想落到指定目录就在同层 .env 里设 HAVLINE_DATA_PATH=/vol1/docker/havline-data ——
# 以 / 或 . 开头的写法会被 compose 当成 bind mount,其余字符串仍是命名卷。
- - ${FONU_DATA_PATH:-fonu-data}:/data
- - fonu-updater-sock:/run/fonu-updater
+ - ${HAVLINE_DATA_PATH:-havline-data}:/data
+ - havline-updater-sock:/run/havline-updater
environment:
- FONU_DATA_DIR: /data
- FONU_LISTEN: :6893
- FONU_UPDATER_SOCKET: /run/fonu-updater/updater.sock
+ HAVLINE_DATA_DIR: /data
+ HAVLINE_LISTEN: :6893
+ HAVLINE_UPDATER_SOCKET: /run/havline-updater/updater.sock
# 留空即自动:应用会在数据目录里生成并持久化 /session_secret(0600)
# 想自己指定就设这个变量;注意它同时是凭据加密密钥,已有数据后不要随意更换
- FONU_SESSION_SECRET: ${FONU_SESSION_SECRET:-}
+ HAVLINE_SESSION_SECRET: ${HAVLINE_SESSION_SECRET:-}
# 时区:通知消息时间按此显示(默认东八区)
TZ: ${TZ:-Asia/Shanghai}
@@ -49,26 +49,26 @@ services:
GO_IMAGE: ${GO_IMAGE:-docker.m.daocloud.io/library/golang:1.23-bookworm}
UPDATER_IMAGE: ${UPDATER_IMAGE:-docker.m.daocloud.io/library/docker:27-cli}
GOPROXY: ${GOPROXY:-https://goproxy.cn,direct}
- image: fonu-updater:${FONU_VERSION:-local}
- container_name: fonu-updater
+ image: havline-updater:${HAVLINE_VERSION:-local}
+ container_name: havline-updater
restart: unless-stopped
volumes:
- ${DOCKER_SOCK:-/var/run/docker.sock}:/var/run/docker.sock
- - ${FONU_UPDATE_HOST_DIR:-.}:/workspace
- - fonu-updater-sock:/run/fonu-updater
+ - ${HAVLINE_UPDATE_HOST_DIR:-.}:/workspace
+ - havline-updater-sock:/run/havline-updater
environment:
- FONU_UPDATER_SOCKET: /run/fonu-updater/updater.sock
- FONU_UPDATE_PROJECT_DIR: /workspace
- FONU_UPDATE_COMPOSE_FILE: docker-compose.yml
- FONU_UPDATE_SERVICE: fonu
- FONU_UPDATE_MODE: local
- FONU_UPDATE_APP_URL: http://fonu:6893
- FONU_UPDATE_HOST_DIR: ${FONU_UPDATE_HOST_DIR:-}
- FONU_UPDATE_PROJECT_NAME: ${FONU_UPDATE_PROJECT_NAME:-fonu}
- FONU_UPDATE_SELF_IMAGE: fonu-updater:${FONU_VERSION:-local}
- FONU_UPDATE_DOCKER_SOCK: ${DOCKER_SOCK:-/var/run/docker.sock}
+ HAVLINE_UPDATER_SOCKET: /run/havline-updater/updater.sock
+ HAVLINE_UPDATE_PROJECT_DIR: /workspace
+ HAVLINE_UPDATE_COMPOSE_FILE: docker-compose.yml
+ HAVLINE_UPDATE_SERVICE: havline
+ HAVLINE_UPDATE_MODE: local
+ HAVLINE_UPDATE_APP_URL: http://havline:6893
+ HAVLINE_UPDATE_HOST_DIR: ${HAVLINE_UPDATE_HOST_DIR:-}
+ HAVLINE_UPDATE_PROJECT_NAME: ${HAVLINE_UPDATE_PROJECT_NAME:-havline}
+ HAVLINE_UPDATE_SELF_IMAGE: havline-updater:${HAVLINE_VERSION:-local}
+ HAVLINE_UPDATE_DOCKER_SOCK: ${DOCKER_SOCK:-/var/run/docker.sock}
TZ: ${TZ:-Asia/Shanghai}
volumes:
- fonu-data:
- fonu-updater-sock:
+ havline-data:
+ havline-updater-sock:
diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md
index 77631d0..35eac8d 100644
--- a/docs/DEPLOYMENT.md
+++ b/docs/DEPLOYMENT.md
@@ -1,15 +1,15 @@
-# Fonu 部署安装文档
+# Havline 部署安装文档
-> 适用版本:v1.1.0(源码包 `fonu-1.1.0-src.zip`)。本文覆盖从拿到安装包到服务上线的完整流程:环境要求 → 打包 → 三种部署方式 → 首次配置 → FRP/DDNS/证书联动 → 升级与回滚 → 故障排查。
+> 适用版本:v1.1.0(源码包 `havline-1.1.0-src.zip`)。本文覆盖从拿到安装包到服务上线的完整流程:环境要求 → 打包 → 三种部署方式 → 首次配置 → FRP/DDNS/证书联动 → 升级与回滚 → 故障排查。
> 功能细节与组合场景请配合项目内两份文档:`README.md`(功能总览)与 FRP 页面右上「使用说明」(内置十个组合场景详解)。
## 0. 交付物清单与安装路径速查
| 交付物 | 内容 | 适用 | 安装方式 |
| --- | --- | --- | --- |
-| `fonu-1.1.0-linux-amd64.tar.gz`(14MB) | 编译好的 Linux 二进制(前端已内嵌)+ migrations + systemd + 一键脚本 | **裸机 Linux 服务器**,目标机只需系统 nginx | 解压 → `sudo bash install.sh`(见 3.4) |
-| `fonu-1.1.0-image.tar.gz`(约 100-150MB) | 构建好的 Docker 镜像 `fonu:local`(含 nginx + frpc) | 目标机有 Docker、要离线安装 | `docker load` → `docker run`(见 3.3 方式 C) |
-| `fonu-1.1.0-src.zip`(11MB) | 完整源码 + Dockerfile/compose + 部署文档 | 要自行构建 / 二次开发 / 交叉编译 | `docker compose build` 后按方式 A/B 部署(见第 2 节) |
+| `havline-1.1.0-linux-amd64.tar.gz`(14MB) | 编译好的 Linux 二进制(前端已内嵌)+ migrations + systemd + 一键脚本 | **裸机 Linux 服务器**,目标机只需系统 nginx | 解压 → `sudo bash install.sh`(见 3.4) |
+| `havline-1.1.0-image.tar.gz`(约 100-150MB) | 构建好的 Docker 镜像 `havline:local`(含 nginx + frpc) | 目标机有 Docker、要离线安装 | `docker load` → `docker run`(见 3.3 方式 C) |
+| `havline-1.1.0-src.zip`(11MB) | 完整源码 + Dockerfile/compose + 部署文档 | 要自行构建 / 二次开发 / 交叉编译 | `docker compose build` 后按方式 A/B 部署(见第 2 节) |
**怎么选**:飞牛 fnOS 首选镜像包导入(图形界面,见第 3 节末「飞牛 fnOS 部署详解」),要在 NAS 上构建则用源码包 + host 模式 compose;普通 Linux 服务器要省事用 Linux 运行包(免 Docker);目标机不方便联网构建用镜像包离线导入。三者的功能完全一致——容器里跑的也是同一份二进制 + 系统 nginx。
@@ -17,7 +17,7 @@
### 1.1 源码包内容
-`fonu-1.1.0-src.zip`(约 11MB,304 项)为完整可构建源码:
+`havline-1.1.0-src.zip`(约 11MB,304 项)为完整可构建源码:
| 目录/文件 | 说明 |
| --- | --- |
@@ -50,7 +50,7 @@
docker compose build
```
-一次构建同时产出:前端 `dist`、Go 后端二进制与运行镜像(frpc 不预装,运行期用「应用配置」按需下载),标签为 `fonu:local`。
+一次构建同时产出:前端 `dist`、Go 后端二进制与运行镜像(frpc 不预装,运行期用「应用配置」按需下载),标签为 `havline:local`。
**版本号**:读取根目录 `VERSION` 文件(当前 `1.1.0`)自动注入前后端显示;要改版本号只需编辑该文件后重新 build。
@@ -61,49 +61,49 @@ docker build \
--build-arg NODE_IMAGE=docker.m.daocloud.io/library/node:22-alpine \
--build-arg GO_IMAGE=docker.m.daocloud.io/library/golang:1.23-bookworm \
--build-arg RUNTIME_IMAGE=docker.m.daocloud.io/library/debian:bookworm-slim \
- -t fonu:local .
+ -t havline:local .
```
## 3. 三种部署方式(按环境二选一)
### 方式 A:飞牛 fnOS / 已占用 80/443 的环境 —— host 模式(推荐)
-飞牛系统自带 Nginx 占用 80/443,Fonu 使用 host 网络 + 高位反代端口避免冲突。
+飞牛系统自带 Nginx 占用 80/443,Havline 使用 host 网络 + 高位反代端口避免冲突。
```bash
-FONU_SESSION_SECRET=请改成一串随机字符 docker compose -f docker-compose.host.yml up -d --build
+HAVLINE_SESSION_SECRET=请改成一串随机字符 docker compose -f docker-compose.host.yml up -d --build
```
- 无端口映射,容器直接使用宿主机网络栈;
-- 反代端口由环境变量 `FONU_NGINX_HTTP_PORT=18080`、`FONU_NGINX_HTTPS_PORT=9443` 决定(文件内已内置);
+- 反代端口由环境变量 `HAVLINE_NGINX_HTTP_PORT=18080`、`HAVLINE_NGINX_HTTPS_PORT=9443` 决定(文件内已内置);
- 入口:管理后台 `http://:6893`,HTTP 反代 `http://:18080`,HTTPS 反代 `https://:9443`;
-- 数据持久化在命名卷 `fonu-data`(挂载到容器 `/data`)。
+- 数据持久化在命名卷 `havline-data`(挂载到容器 `/data`)。
### 方式 B:其他环境 / 空闲 80/443 —— Bridge 端口映射
```bash
-FONU_SESSION_SECRET=请改成一串随机字符 docker compose -f docker-compose.hub.yml up -d --build
+HAVLINE_SESSION_SECRET=请改成一串随机字符 docker compose -f docker-compose.hub.yml up -d --build
```
- 端口映射 `6893:6893`、`18080:80`、`9443:443`(宿主机 18080/9443 → 容器 80/443);
-- 数据同样在命名卷 `fonu-data`。
+- 数据同样在命名卷 `havline-data`。
### 方式 C:docker run(不想用 compose)
```bash
-docker run -d --name fonu \
+docker run -d --name havline \
--net=host \
-v ./data:/data \
- -e FONU_SESSION_SECRET=请改成一串随机字符 \
+ -e HAVLINE_SESSION_SECRET=请改成一串随机字符 \
--restart unless-stopped \
- fonu:local
+ havline:local
```
-host 模式下反代端口用环境变量控制:`-e FONU_NGINX_HTTP_PORT=18080 -e FONU_NGINX_HTTPS_PORT=9443`。Bridge 环境改为 `-p 6893:6893 -p 18080:80 -p 9443:443`(去掉 `--net=host`)。
+host 模式下反代端口用环境变量控制:`-e HAVLINE_NGINX_HTTP_PORT=18080 -e HAVLINE_NGINX_HTTPS_PORT=9443`。Bridge 环境改为 `-p 6893:6893 -p 18080:80 -p 9443:443`(去掉 `--net=host`)。
**生成随机 SESSION_SECRET**:`openssl rand -hex 32`(Linux)或 PowerShell:`-join ((48..57)+(97..122) | Get-Random -Count 32 | % {[char]$_})`。
-> **`FONU_SESSION_SECRET` 是什么**:Fonu 的主密钥,两个用途——① 签名/加密登录会话 Cookie(决定登录态是否可被伪造);② 作为 `secret.Box` 加密存储中敏感凭据(DDNS API Token、FRP Token、SMTP 密码等)。默认值 `change-me-in-production` 是公开的:拿到它即可伪造管理员会话、解密数据库里的全部凭据,**生产环境必须替换**。⚠️ 换密钥的时机:首次启动前设置;若之后更换,旧密钥加密的凭据将无法解密(应用会提示重新保存),需到对应页面重录 Token。
+> **`HAVLINE_SESSION_SECRET` 是什么**:Havline 的主密钥,两个用途——① 签名/加密登录会话 Cookie(决定登录态是否可被伪造);② 作为 `secret.Box` 加密存储中敏感凭据(DDNS API Token、FRP Token、SMTP 密码等)。默认值 `change-me-in-production` 是公开的:拿到它即可伪造管理员会话、解密数据库里的全部凭据,**生产环境必须替换**。⚠️ 换密钥的时机:首次启动前设置;若之后更换,旧密钥加密的凭据将无法解密(应用会提示重新保存),需到对应页面重录 Token。
### Linux 服务器完整部署步骤(Ubuntu / Debian / CentOS)
@@ -113,13 +113,13 @@ curl -fsSL https://get.docker.com | sh
sudo systemctl enable --now docker
# 1. 解压源码包并进入目录
-unzip fonu-1.1.0-src.zip -d fonu && cd fonu
+unzip havline-1.1.0-src.zip -d havline && cd havline
# 2. 构建镜像(约 3-10 分钟,取决于网络与机器性能)
docker compose build
# 3. 生成密钥并启动(Bridge 模式,映射 6893/18080/9443)
-echo "FONU_SESSION_SECRET=$(openssl rand -hex 32)" > .env
+echo "HAVLINE_SESSION_SECRET=$(openssl rand -hex 32)" > .env
docker compose -f docker-compose.hub.yml up -d
# 4. 防火墙放行(按需)
@@ -128,48 +128,48 @@ sudo ufw allow 80/tcp 443/tcp # 反代(Bridge 模式容器内监听 80/443
# firewalld 用: firewall-cmd --permanent --add-port=6893/tcp && firewall-cmd --reload
# 5. 验证
-docker logs fonu | grep -i password # 取 admin 初始密码
+docker logs havline | grep -i password # 取 admin 初始密码
curl -I http://127.0.0.1:6893/login # 应返回 200
```
- **开机自启**:compose 内已含 `restart: unless-stopped`,且第 0 步已 `enable docker`,重启服务器自动拉起;
- **80/443 被系统 Nginx 占用**:改用 `docker-compose.host.yml`(host 网络 + 18080/9443 高位端口),或改 `docker-compose.hub.yml` 里的宿主端口映射;
-- **离线部署**(目标机不方便联网构建):在有网的机器上 `docker compose build` 后 `docker save fonu:local | gzip > fonu.tar.gz`,拷贝到目标机 `docker load < fonu.tar.gz`,再用方式 C 的 `docker run` 启动(无需再构建);
-- **数据位置**:命名卷 `fonu-data`(`docker volume inspect fonu-data` 查看宿主机实际路径),备份该卷即备份全部配置与证书。
+- **离线部署**(目标机不方便联网构建):在有网的机器上 `docker compose build` 后 `docker save havline:local | gzip > havline.tar.gz`,拷贝到目标机 `docker load < havline.tar.gz`,再用方式 C 的 `docker run` 启动(无需再构建);
+- **数据位置**:命名卷 `havline-data`(`docker volume inspect havline-data` 查看宿主机实际路径),备份该卷即备份全部配置与证书。
### Linux 运行包安装(免 Docker,裸机部署)
-适用:不想装 Docker 的 Linux 服务器。前提:系统已装 nginx(Fonu 复用它做反代),root 权限。
+适用:不想装 Docker 的 Linux 服务器。前提:系统已装 nginx(Havline 复用它做反代),root 权限。
```bash
# 1. 解压
-tar xzf fonu-1.1.0-linux-amd64.tar.gz -C fonu && cd fonu
+tar xzf havline-1.1.0-linux-amd64.tar.gz -C havline && cd havline
-# 2. 一键安装(自动建 fonu 系统用户、装到 /opt/fonu、生成随机密钥、注册 systemd 开机自启)
+# 2. 一键安装(自动建 havline 系统用户、装到 /opt/havline、生成随机密钥、注册 systemd 开机自启)
sudo bash install.sh
# 3. 验证
-systemctl status fonu
+systemctl status havline
curl -I http://127.0.0.1:6893/login
-journalctl -u fonu | grep -i password # 取 admin 初始密码
+journalctl -u havline | grep -i password # 取 admin 初始密码
```
-包内容:`fonu` 二进制(前端已 `go:embed` 内嵌,无需单独部署 dist)、`migrations/`(从二进制同级目录自动查找)、`deploy/nginx/mime.types`、`fonu.service`(systemd 模板)、`install.sh`、`DEPLOYMENT.md`。
+包内容:`havline` 二进制(前端已 `go:embed` 内嵌,无需单独部署 dist)、`migrations/`(从二进制同级目录自动查找)、`deploy/nginx/mime.types`、`havline.service`(systemd 模板)、`install.sh`、`DEPLOYMENT.md`。
关键路径(install.sh 自动配置):
| 项 | 值 |
| --- | --- |
-| 二进制 | `/opt/fonu/fonu` |
-| 数据目录 | `/var/lib/fonu`(数据库、证书、日志全在这) |
-| 会话密钥 | `/etc/default/fonu`(重复安装保留原值) |
-| 服务 | `systemctl restart fonu` 生效配置改动 |
+| 二进制 | `/opt/havline/havline` |
+| 数据目录 | `/var/lib/havline`(数据库、证书、日志全在这) |
+| 会话密钥 | `/etc/default/havline`(重复安装保留原值) |
+| 服务 | `systemctl restart havline` 生效配置改动 |
-**备份/迁移**:停服务后打包 `/var/lib/fonu` 即可(`tar czf fonu-data.tar.gz /var/lib/fonu`),新机器解压到同路径 + 装同版本二进制即完成迁移。
+**备份/迁移**:停服务后打包 `/var/lib/havline` 即可(`tar czf havline-data.tar.gz /var/lib/havline`),新机器解压到同路径 + 装同版本二进制即完成迁移。
-**手动安装**(不用脚本):按包内 `README.txt` 要点——建 `fonu` 系统用户、复制二进制与 migrations 到 `/opt/fonu`、`EnvironmentFile` 写入随机密钥、`systemctl enable --now fonu`。
+**手动安装**(不用脚本):按包内 `README.txt` 要点——建 `havline` 系统用户、复制二进制与 migrations 到 `/opt/havline`、`EnvironmentFile` 写入随机密钥、`systemctl enable --now havline`。
-> 镜像包(`fonu-1.1.0-image.tar.gz`)来源:在任意装了 Docker 的机器上对源码包执行 `docker compose build` 后 `docker save fonu:local | gzip > fonu-1.1.0-image.tar.gz`;目标机 `docker load < fonu-1.1.0-image.tar.gz` 后按方式 C 启动。
+> 镜像包(`havline-1.1.0-image.tar.gz`)来源:在任意装了 Docker 的机器上对源码包执行 `docker compose build` 后 `docker save havline:local | gzip > havline-1.1.0-image.tar.gz`;目标机 `docker load < havline-1.1.0-image.tar.gz` 后按方式 C 启动。
### 飞牛 fnOS 部署详解(图形界面,不敲命令)
@@ -177,17 +177,17 @@ journalctl -u fonu | grep -i password # 取 admin 初始密码
**方式一:导入现成镜像(推荐,5 分钟)**
-1. **上传文件**:把 `fonu-1.1.0-image.tar.gz` 通过文件管理 / SMB 上传到 NAS,如共享目录 `docker/fonu/`;
-2. **导入镜像**:打开 Docker 应用 → 镜像 → 导入(从文件加载)→ 选择该 tar.gz → 等待镜像列表出现 `fonu:local`(若界面只接受 .tar,先在电脑上解压出内层的 .tar 再导入);
+1. **上传文件**:把 `havline-1.1.0-image.tar.gz` 通过文件管理 / SMB 上传到 NAS,如共享目录 `docker/havline/`;
+2. **导入镜像**:打开 Docker 应用 → 镜像 → 导入(从文件加载)→ 选择该 tar.gz → 等待镜像列表出现 `havline:local`(若界面只接受 .tar,先在电脑上解压出内层的 .tar 再导入);
3. **创建容器**:容器 → 创建:
| 配置项 | 填写 |
| --- | --- |
-| 名称 | `fonu` |
-| 镜像 | `fonu:local` |
+| 名称 | `havline` |
+| 镜像 | `havline:local` |
| 网络 | **host**(飞牛 80/443 被系统占用,host + 高位端口最省事;UI 不支持 host 时选 Bridge 并映射 6893/18080/9443) |
-| 存储挂载 | NAS 建目录(如 `/vol1/…/docker/fonu/data`)→ 映射到容器 `/data`(读写) |
-| 环境变量 | `FONU_SESSION_SECRET=随机串`、`FONU_DATA_DIR=/data`、`FONU_LISTEN=:6893`、`FONU_NGINX_HTTP_PORT=18080`、`FONU_NGINX_HTTPS_PORT=9443` |
+| 存储挂载 | NAS 建目录(如 `/vol1/…/docker/havline/data`)→ 映射到容器 `/data`(读写) |
+| 环境变量 | `HAVLINE_SESSION_SECRET=随机串`、`HAVLINE_DATA_DIR=/data`、`HAVLINE_LISTEN=:6893`、`HAVLINE_NGINX_HTTP_PORT=18080`、`HAVLINE_NGINX_HTTPS_PORT=9443` |
| 重启策略 | 自动重启 / unless-stopped(开机自启) |
4. **启动并取初始密码**:容器详情 → 日志,搜 `password`;浏览器打开 `http://:6893` 登录。
@@ -196,14 +196,14 @@ journalctl -u fonu | grep -i password # 取 admin 初始密码
1. 把源码包 zip 上传到 NAS 并解压(文件管理支持解压,或电脑解压后整目录上传);
2. Docker 应用 → 项目(Compose)→ 新建:指定项目目录为解压后的源码目录、使用 `docker-compose.host.yml`(或直接把该文件内容粘贴到 Compose 编辑器);
-3. 在源码目录同层放一个 `.env` 文件,内容一行:`FONU_SESSION_SECRET=随机串`;
+3. 在源码目录同层放一个 `.env` 文件,内容一行:`HAVLINE_SESSION_SECRET=随机串`;
4. 构建并启动。⚠️ NAS 上构建耗时较长(前端编译),低配机器建议改用方式一;
5. 后续升级:上传新源码覆盖 → 项目重新构建即可,数据在卷里不受影响。
**Compose 构建报 401(docker.fnnas.com)**:fnOS 的 Docker 设置里默认配了自家加速地址,但它对 `library/` 官方镜像拒绝匿名拉取。三种解法任选:
-1. **改用镜像包(最省事)**:跳过构建,按方式一导入 `fonu-1.1.0-image.tar.gz`;
-2. **覆盖基础镜像源**:在源码目录的 `.env` 里加三行(已有 FONU_SESSION_SECRET 就追加在下面):
+1. **改用镜像包(最省事)**:跳过构建,按方式一导入 `havline-1.1.0-image.tar.gz`;
+2. **覆盖基础镜像源**:在源码目录的 `.env` 里加三行(已有 HAVLINE_SESSION_SECRET 就追加在下面):
```
NODE_IMAGE=docker.m.daocloud.io/library/node:22-alpine
GO_IMAGE=docker.m.daocloud.io/library/golang:1.23-bookworm
@@ -224,10 +224,10 @@ journalctl -u fonu | grep -i password # 取 admin 初始密码
## 4. 首次登录与安全配置
-1. 启动后查看初始密码:`docker logs fonu | grep -i password`(自动生成的 admin 初始密码会打印在日志里);
+1. 启动后查看初始密码:`docker logs havline | grep -i password`(自动生成的 admin 初始密码会打印在日志里);
2. 浏览器打开 `http://:6893/login`,用 `admin` + 初始密码登录;
3. **立即**在「设置 → 安全」修改管理员密码;
-4. 确认「设置 → 常规」中 `FONU_SESSION_SECRET` 已改为非默认值(compose 文件中已传入则跳过);
+4. 确认「设置 → 常规」中 `HAVLINE_SESSION_SECRET` 已改为非默认值(compose 文件中已传入则跳过);
5. 建议在「设置 → 通知」配置一种告警方式(邮件 / Webhook / Telegram),DDNS 失败、证书续签失败、登录异常等事件会推送。
## 5. 反向代理与 HTTPS 证书
@@ -254,7 +254,7 @@ DDNS 页 → 添加任务:选服务商 → 填 API 凭证(可「测试连接
前置:一台有公网 IP 的 VPS。
-1. **Fonu 添加服务端**:FRP 穿透页 → 添加服务端 → 填 VPS 地址、bindPort(默认 7000)、认证方式与 Token(与 frps 一致);
+1. **Havline 添加服务端**:FRP 穿透页 → 添加服务端 → 填 VPS 地址、bindPort(默认 7000)、认证方式与 Token(与 frps 一致);
2. **生成 frps 配置**:服务端「详情」→「frps.toml」→ 复制生成内容到 VPS 保存为 `frps.toml`,执行 `frps -c frps.toml`;按文件末尾的防火墙清单在 VPS 安全组放行端口;
3. **添加穿透规则**:8 种类型按需选——tcp/udp 填远程端口;http/https/tcpmux 填自定义域名(走 frps vhost 端口);stcp/sudp/xtcp 凭 secretKey(无公网监听);
4. **应用并验证**:「应用配置」→「校验并应用配置」;服务端「详情」看五项诊断(TCP 可达 / frpc 登录 / 代理可达 / DNS 解析 / 归属地)与独立日志。
@@ -265,12 +265,12 @@ DDNS 页 → 添加任务:选服务商 → 填 API 凭证(可「测试连接
```bash
# 1. 备份数据(最重要)
-docker run --rm -v fonu-data:/data -v ${PWD}:/backup alpine tar czf /backup/fonu-data-backup.tar.gz /data
+docker run --rm -v havline-data:/data -v ${PWD}:/backup alpine tar czf /backup/havline-data-backup.tar.gz /data
# 2. 解压新版源码包覆盖项目目录(VERSION 与 migrations 会更新)
# 3. 重建并启动(迁移 015–022 会在启动时自动应用)
docker compose -f docker-compose.host.yml up -d --build
# 4. 看启动日志确认迁移与版本
-docker logs -f fonu
+docker logs -f havline
```
### 回滚
@@ -288,18 +288,18 @@ docker compose -f docker-compose.host.yml up -d --build
| 现象 | 处理 |
| --- | --- |
-| 容器起来但 6893 打不开 | `docker logs fonu` 看启动报错;`docker port fonu`(Bridge 模式)确认映射 |
-| 提示「会话加密密钥」警告 | 未设置 `FONU_SESSION_SECRET`,compose 传入即可 |
+| 容器起来但 6893 打不开 | `docker logs havline` 看启动报错;`docker port havline`(Bridge 模式)确认映射 |
+| 提示「会话加密密钥」警告 | 未设置 `HAVLINE_SESSION_SECRET`,compose 传入即可 |
| 反代 502 | 目标内网应用未启动或地址填错;日志中心看 Nginx error 日志 |
| 证书申请失败 | DDNS 凭证是否有效、域名 DNS 是否已生效、ACME 邮箱是否已设置 |
| frpc 连不上 frps | 两端 Token/端口不一致;用服务端「详情 → frps.toml」重新生成对齐 |
-| 穿透流量显示 0 | frps 未配置 webServer 或 Fonu 服务端表单未填管理地址(非必须功能) |
+| 穿透流量显示 0 | frps 未配置 webServer 或 Havline 服务端表单未填管理地址(非必须功能) |
| 页面改动未生效 | 确认访问的是容器端口而非开发服务器;`docker compose up -d --build` 重建 |
## 10. 卸载
```bash
docker compose down # 停止并删除容器(数据卷保留)
-docker volume rm fonu-data # 彻底删除数据(不可逆,先备份)
-docker rmi fonu:local # 删除镜像
+docker volume rm havline-data # 彻底删除数据(不可逆,先备份)
+docker rmi havline:local # 删除镜像
```
diff --git a/docs/FRP公网暴露操作手册.md b/docs/FRP公网暴露操作手册.md
index 56a93c1..1ea87fe 100644
--- a/docs/FRP公网暴露操作手册.md
+++ b/docs/FRP公网暴露操作手册.md
@@ -1,7 +1,7 @@
# 内网应用经 FRP 暴露公网:NAS 侧完整操作手册
-> 场景:把 NAS 内网应用(本例 `192.168.31.152:7023`)通过一台公网 VPS(`38.95.74.87`)以域名(`tv.36800.cc`)暴露出去,URL 不带端口号,且后续域名 / HTTPS / 访问控制全部在 NAS 的 Fonu 网页里管理,VPS 只做纯流量转发。
-> 适用版本:Fonu v1.1.0+(FRP 多服务端模块)。VPS 上 **不需要也不应该** 装 Nginx 反代。
+> 场景:把 NAS 内网应用(本例 `192.168.31.152:7023`)通过一台公网 VPS(`38.95.74.87`)以域名(`tv.36800.cc`)暴露出去,URL 不带端口号,且后续域名 / HTTPS / 访问控制全部在 NAS 的 Havline 网页里管理,VPS 只做纯流量转发。
+> 适用版本:Havline v1.1.0+(FRP 多服务端模块)。VPS 上 **不需要也不应该** 装 Nginx 反代。
## 0. 最终架构与访问地址
@@ -12,7 +12,7 @@ VPS frps(监听 80 —— 由 frpc 动态注册的 tcp 隧道,VPS 上无需
↓ FRP 隧道(bindPort 7000,TLS)
NAS frpc
↓ 127.0.0.1:18080
-Fonu Nginx(按域名分流,后续可加 HTTPS/黑白名单)
+Havline Nginx(按域名分流,后续可加 HTTPS/黑白名单)
↓ http://192.168.31.152:7023
内网应用
```
@@ -23,17 +23,17 @@ Fonu Nginx(按域名分流,后续可加 HTTPS/黑白名单)
| --- | --- | --- |
| 公网 VPS | `38.95.74.87`(tv.36800.cc) | 只跑 frps,Nginx 停用 |
| NAS 内网应用 | `192.168.31.152:7023` | 真正的 Web 服务 |
-| Fonu 管理后台 | `http://:6893` | 全部配置入口 |
+| Havline 管理后台 | `http://:6893` | 全部配置入口 |
## 1. 前置条件
-- VPS:已部署 frps(bindPort 7000、token 认证;参考 Fonu 服务端详情「frps.toml」一键生成);
+- VPS:已部署 frps(bindPort 7000、token 认证;参考 Havline 服务端详情「frps.toml」一键生成);
- VPS:frps 的 Nginx **未**占用 80(占用见第 5 节排障);
- 云厂商安全组已放行:7000/tcp(frps 控制)、80/tcp(对外)、7500/tcp(frps 管理接口,可选,用于流量统计);
-- NAS:Fonu 已部署并能打开管理后台;
-- 域名:`tv.36800.cc` 的 DNS A 记录指向 VPS IP(可用 Fonu DDNS 页「自定义 IP」固定,或由 DNS 服务商直接管理)。
+- NAS:Havline 已部署并能打开管理后台;
+- 域名:`tv.36800.cc` 的 DNS A 记录指向 VPS IP(可用 Havline DDNS 页「自定义 IP」固定,或由 DNS 服务商直接管理)。
-## 2. Fonu 网页配置(三处)
+## 2. Havline 网页配置(三处)
### 2.1 FRP 服务端
@@ -57,9 +57,9 @@ Fonu Nginx(按域名分流,后续可加 HTTPS/黑白名单)
| --- | --- | --- |
| 所属服务端 | 上一步的服务端 | |
| 名称 | `tv`(frps 全局唯一,出现在日志里) | |
-| 类型 | **tcp** | ⚠️ 不选 http——80 直接由 frps 转发,域名分流交给 Fonu 反代 |
+| 类型 | **tcp** | ⚠️ 不选 http——80 直接由 frps 转发,域名分流交给 Havline 反代 |
| 远程端口 | **80** | 选 tcp 类型后出现此字段;frpc 登录时向 frps 动态注册 |
-| 内网目标 | **127.0.0.1:18080** | 指向 Fonu 自己的 Nginx HTTP 监听(fnOS host 模式默认 18080) |
+| 内网目标 | **127.0.0.1:18080** | 指向 Havline 自己的 Nginx HTTP 监听(fnOS host 模式默认 18080) |
保存 → 右上 **「应用配置」→「校验并应用配置」**,或对该服务端「停止→启动」。
@@ -72,25 +72,25 @@ Fonu Nginx(按域名分流,后续可加 HTTPS/黑白名单)
| 前端域名 | `tv.36800.cc` |
| 监听端口 | **18080**(与 2.2 的内网目标端口一致;fnOS 下不要用 80,系统 Nginx 已占用) |
| 目标地址 | `http://192.168.31.152:7023`(⚠️ 指向真正的内网应用,完整 URL 带协议与端口) |
-| HTTPS | 按需;fnOS 下 Fonu Nginx 的 HTTPS 监听默认 9443 |
+| HTTPS | 按需;fnOS 下 Havline Nginx 的 HTTPS 监听默认 9443 |
> ⚠️ **回环警告**:目标地址千万不要填 `http://tv.36800.cc:xxxx`——那会让请求从 NAS 出公网再回 NAS(NAS→VPS→NAS 绕圈)。反代目标是**内网应用本身**。
> ⚠️ **「目标地址缺少主机名」报错**:目标地址格式必须完整 `http://IP:端口`(协议+主机+端口缺一不可,单斜杠 `http:/` 或中文冒号都会报 400)。
## 3. VPS 侧配置(一次性的两步)
-### 3.1 frps.toml(Fonu 一键生成)
+### 3.1 frps.toml(Havline 一键生成)
-Fonu 服务端「详情」→「frps.toml」→ 复制内容到 VPS 保存为 `frps.toml`:
+Havline 服务端「详情」→「frps.toml」→ 复制内容到 VPS 保存为 `frps.toml`:
```toml
bindAddr = "0.0.0.0"
bindPort = 7000
auth.method = "token"
-auth.token = "<与 Fonu 一致>"
+auth.token = "<与 Havline 一致>"
-webServer.addr = "0.0.0.0" # 可选:Fonu 拉取穿透流量
+webServer.addr = "0.0.0.0" # 可选:Havline 拉取穿透流量
webServer.port = 7500
webServer.user = "admin"
webServer.password = "<自定义>"
@@ -130,8 +130,8 @@ frpc 约每 33 秒自动重试注册,释放后无需任何手动操作,注
| 步骤 | 命令 / 操作 | 预期 |
| --- | --- | --- |
| VPS 80 已监听 | `ss -tlnp \| grep ':80 '` | 进程为 frps |
-| frpc 注册成功 | Fonu 服务端「日志」 | 无 `port unavailable`,有 proxy 启动记录 |
-| 隧道通 | Fonu 服务端「测速」/「详情」诊断 | TCP 可达、frpc 登录 ok、延迟正常 |
+| frpc 注册成功 | Havline 服务端「日志」 | 无 `port unavailable`,有 proxy 启动记录 |
+| 隧道通 | Havline 服务端「测速」/「详情」诊断 | TCP 可达、frpc 登录 ok、延迟正常 |
| 分流通 | 服务端「详情」→ 诊断 `proxy_reachable` | ok |
| 端到端 | 外网浏览器 `http://tv.36800.cc` | 直达内网应用页面 |
@@ -142,10 +142,10 @@ frpc 约每 33 秒自动重试注册,释放后无需任何手动操作,注
| `Connection refused`(外部 curl 80) | **VPS 侧**:frps 没绑上 80 | ① `ss -tlnp \| grep ':80 '` 看 80 被谁占(多为 Nginx 没停)→ 停用;② `systemctl status frps` 确认在跑且用的新配置(nohup 手动跑的必须 `pkill frps` 重启);③ 重启后等 frpc 自动重试 |
| frpc 日志 `start error: port unavailable` | 同上 | VPS 80 被占;见 3.2 |
| 诊断「frpc 登录」失败 | Token/端口不一致 | 两端 token 对齐;端口=frps bindPort |
-| 登录成功但 `proxy_reachable` 失败 | Fonu 反代 | 反代规则监听端口与 FRP 内网目标端口一致(18080);反代已启用 |
+| 登录成功但 `proxy_reachable` 失败 | Havline 反代 | 反代规则监听端口与 FRP 内网目标端口一致(18080);反代已启用 |
| 打开域名 404 | 域名分流失败 | 反代规则的前端域名与访问域名完全一致;frps 无需 vhost(tcp 方案) |
| 域名解析不对 | DNS | `nslookup tv.36800.cc` 应返回 VPS IP;用 DDNS 自定义 IP 固定 |
-| 流量统计全 0 | frps webServer 未配 | 非必须;要在 Fonu 看流量就配 webServer 并在服务端表单填管理接口 |
+| 流量统计全 0 | frps webServer 未配 | 非必须;要在 Havline 看流量就配 webServer 并在服务端表单填管理接口 |
## 6. 两种方案对比(VPS 80 的归属)
@@ -154,9 +154,9 @@ frpc 约每 33 秒自动重试注册,释放后无需任何手动操作,注
| FRP 规则类型 | tcp,远程端口 80 → 127.0.0.1:18080 | http 类型 + frps `vhostHTTPPort=8080` |
| http 类型去端口 | —(tcp 方案直接占 80) | `vhostHTTPPort` 从 8080 改 **80**:URL 变 `http://域名`(无端口);前提同样是 VPS 停 Nginx 释放 80,改后 `pkill frps` 重启生效;NAS 侧规则无需动 |
| VPS Nginx | 不需要(停用) | 必须:listen 80 → proxy_pass 127.0.0.1:8080(**必须带** `proxy_set_header Host $host`) |
-| 加新域名 | Fonu 反代加一条 + FRP 加一条 tcp 规则(远程端口仍 80,内网目标同 18080) | VPS 上加 server 块 |
-| 上 HTTPS | Fonu 证书页(DNS-01)+ 反代开 HTTPS + FRP 加 tcp 443→127.0.0.1:9443 | VPS 上装证书 |
-| 访问控制(IP/BasicAuth) | Fonu 反代页直接配 | VPS 上手写 |
+| 加新域名 | Havline 反代加一条 + FRP 加一条 tcp 规则(远程端口仍 80,内网目标同 18080) | VPS 上加 server 块 |
+| 上 HTTPS | Havline 证书页(DNS-01)+ 反代开 HTTPS + FRP 加 tcp 443→127.0.0.1:9443 | VPS 上装证书 |
+| 访问控制(IP/BasicAuth) | Havline 反代页直接配 | VPS 上手写 |
| 限制 | VPS 80/443 不能再跑别的站点 | VPS 可共存多站点 |
| URL | `http://tv.36800.cc`(无端口) | `http://tv.36800.cc`(80)或带 vhost 端口 |
@@ -164,7 +164,7 @@ frpc 约每 33 秒自动重试注册,释放后无需任何手动操作,注
## 7. HTTPS 升级路径(方案 A 延伸)
-1. **Fonu 证书页**:申请 `tv.36800.cc` 或 `*.36800.cc` 证书(ACME DNS-01,复用 DDNS 页服务商凭证);
+1. **Havline 证书页**:申请 `tv.36800.cc` 或 `*.36800.cc` 证书(ACME DNS-01,复用 DDNS 页服务商凭证);
2. **反代规则**:开 HTTPS、绑定证书、(可选)HTTP 强制跳转;
-3. **FRP 加一条 tcp 规则**:远程端口 443 → 内网目标 `127.0.0.1:9443`(Fonu Nginx HTTPS 监听);
+3. **FRP 加一条 tcp 规则**:远程端口 443 → 内网目标 `127.0.0.1:9443`(Havline Nginx HTTPS 监听);
4. 安全组放行 443;之后 `https://tv.36800.cc` 可用,TLS 全程在 NAS 侧终止。
diff --git a/docs/README.md b/docs/README.md
index 6cb5e32..4b6a1a2 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,4 +1,4 @@
-# Fonu 文档索引
+# Havline 文档索引
> 维护约定:新增方案 / 审查文档时在下方登记;方案落地后在「状态」列标注实施结果,并给已被推翻的描述加时效说明。
> 最近更新:2026-10-01(v1.9.0)
@@ -11,14 +11,15 @@
| `improvement-roadmap-2026-09-27.md` | P0-P3 改进路线实施方案:逐项改法、测试、验收、依赖、发布与回滚 | P0-1、P0-3、P0-4 已完成,其余待实施 |
| `system-upgrade.md` | Docker Compose 一键升级:sidecar 架构、首次部署、安全边界、回滚、故障排查与验证页 | 已实施(v1.3.8) |
| `agent-listen-strategy-plan.md` | Agent 默认监听策略:回环监听、SSH 隧道优先、HTTPS pin 可选与自动迁移方案 | 阶段 1-4 已实施(v1.4.0);2.0.0 兼容清理待后续 |
-| `cloudflared-integration-plan.md` | Cloudflare Tunnel(cloudflared)与 Fonu 的融合方案:托管模型、复用面、分阶段落地与 v2 优化 | 阶段 1-2 已实施(v1.6.0);后续按 `cloudflared-fonu-fit-plan.md` 推进 |
-| `cloudflared-feature-plan.md` | Cloudflare 隧道功能全集:官方最新文档、参考项目功能、Fonu 现状差距与 P0-P3 路线 | 查漏用;实施以 `cloudflared-fonu-fit-plan.md` 为准 |
-| `cloudflared-fonu-fit-plan.md` | Cloudflare 隧道 Fonu 适配版计划:出口抽象、规则同源、体系整合、明确不做项与阶段实施 | 阶段 1-3 主体已实施;剩余项见 `cloudflared-current-features.md` |
+| `cloudflared-integration-plan.md` | Cloudflare Tunnel(cloudflared)与 Havline 的融合方案:托管模型、复用面、分阶段落地与 v2 优化 | 阶段 1-2 已实施(v1.6.0);后续按 `cloudflared-havline-fit-plan.md` 推进 |
+| `cloudflared-feature-plan.md` | Cloudflare 隧道功能全集:官方最新文档、参考项目功能、Havline 现状差距与 P0-P3 路线 | 查漏用;实施以 `cloudflared-havline-fit-plan.md` 为准 |
+| `cloudflared-havline-fit-plan.md` | Cloudflare 隧道 Havline 适配版计划:出口抽象、规则同源、体系整合、明确不做项与阶段实施 | 阶段 1-3 主体已实施;剩余项见 `cloudflared-current-features.md` |
| `cloudflared-current-features.md` | Cloudflare 隧道当前已实现功能说明:页面、出口、路由、DNS、监控、备份、诊断、API 与限制 | 当前版本功能基线 |
| `cloudflared-github-ux-research.md` | GitHub 同类项目调研:Cloudflare 管理面板、反向代理、访问平台、监控与流量检查器的可借鉴体验 | 调研与优化路线 |
+| `fnos-app-packaging-plan.md` | 飞牛 fnOS 应用打包方案:官方文档依据、Docker 模板、向导、权限、数据迁移、升级与发布流程 | 方案待实施 |
| `project-audit-2026-09-24.md` | 全项目审查:P0/P1/P2 分级,含精确行号与逐条修法 | **进行中**——P0 已随 v1.2.7 完成,P1 实施中 |
| `frp-server-list-merge-plan.md` | FRP 服务端列表并入「公网服务端」页(方案 A,四步) | 步 1/2 已完成(v1.2.6);步 3 文案收尾、步 4 死样式清理未做 |
-| `agent-capability-plan.md` | fonu-agent 能力扩展(P0/P1/P2/P3) | 已实施(agent 0.11.0);P3 三项不做 |
+| `agent-capability-plan.md` | havline-agent 能力扩展(P0/P1/P2/P3) | 已实施(agent 0.11.0);P3 三项不做 |
| `agent-view-design.md` | 公网 Agent 页设计方案(Pass 1/2 流程) | 部分过期:顶部下拉已被左列表 + 右详情取代 |
| `agent-view-design-v2.md` | 公网 Agent 页设计方案 v2 | 部分过期:同上 |
| `agent-view-refactor-plan.md` | 公网 Agent 页四段式(状态 → 控制 → 生命周期 → 输出)整改 | 已实施 |
@@ -26,11 +27,11 @@
| `agent-proxy-settings-plan.md` | 公网反代「部署设置」对齐本地反代的语义迁移 | 已实施 |
| `agent-proxy-roadmap.md` | 公网反代功能增强规划(含部署踩坑史) | 部分已实施(漂移检测、全量重部署),其余待排期 |
| `frps-features.md` | frps 服务端功能实现清单(对照官方 frps_full_example.toml) | 参考 |
-| `frps服务端管理方案.md` | FRPS 服务端管理方案(fonu-agent 部署在公网服务器) | 已实施(Agent 管理面板) |
+| `frps服务端管理方案.md` | FRPS 服务端管理方案(havline-agent 部署在公网服务器) | 已实施(Agent 管理面板) |
| `frps管理方案.md` | FRPS 管理方案(早期版本) | 疑似已被 `frps服务端管理方案.md` 取代,保留供追溯(待确认) |
| `upstream-sync-plan.md` | 上游 1.0.2 → 1.0.5 对照与可借鉴项 | 部分过期:对 `/frp` 现状的描述基于 2026-09-23 |
| `FRP公网暴露操作手册.md` | FRP 公网暴露操作手册(面向使用) | 参考 |
-| `DEPLOYMENT.md` | 部署安装:裸机 / Docker / 飞牛 host 模式 | 参考;适用 `fonu-1.1.0` 安装包 |
+| `DEPLOYMENT.md` | 部署安装:裸机 / Docker / 飞牛 host 模式 | 参考;适用 `havline-1.1.0` 安装包 |
## 其它位置
diff --git a/docs/agent-capability-plan.md b/docs/agent-capability-plan.md
index 8bbc989..0a8454d 100644
--- a/docs/agent-capability-plan.md
+++ b/docs/agent-capability-plan.md
@@ -1,8 +1,8 @@
-# fonu-agent 能力扩展方案
+# havline-agent 能力扩展方案
> **实施状态(2026-09-24)**:P0/P1/P2 已全部落地(agent 0.11.0);P3 三项按本文约定不做。剩余为真机验收项,见 `docs/project-audit-2026-09-24.md`。
-> 本文盘点 VPS 侧 `fonu-agent` 现有的接口面与数据面,给出「能直接做」「需要新增 agent 端点」「不做」三类功能的详细设计。
+> 本文盘点 VPS 侧 `havline-agent` 现有的接口面与数据面,给出「能直接做」「需要新增 agent 端点」「不做」三类功能的详细设计。
>
> 依据:本仓库 `internal/agent/`、`internal/frp/agent_client.go`、`internal/frp/agent_installer.go`、`internal/frp/ssh_installer.go`、`internal/api/frpmulti_handler.go`、`web/src/views/AgentView.vue`、`web/src/views/ProxyRemoteView.vue`(均为读代码确认,非推测)。
> 当前 `AgentVersion = "0.9.2"`(`internal/agent/server.go:34`)。
@@ -20,7 +20,7 @@
| --- | --- | --- |
| 1 | `GET /api/v1/ping` | 存活探测(安装/升级后确认 token 与网络) |
| 2 | `GET /api/v1/status` | 一次性返回 Nginx / frps / 端口 / 路由名的快照 |
-| 3 | `GET /api/v1/routes` | 每条 `fonu-*.conf` 的真实状态:`file_exists` / `syntax_ok` / `loaded` |
+| 3 | `GET /api/v1/routes` | 每条 `havline-*.conf` 的真实状态:`file_exists` / `syntax_ok` / `loaded` |
| 4 | `PUT /api/v1/routes` | 写入(或覆盖)一条域名的 Nginx 片段 |
| 5 | `GET /api/v1/routes/{domain}/conf` | 回读某域名实际生效的配置文件内容 |
| 6 | `DELETE /api/v1/routes/{domain}` | 删除某域名的片段 |
@@ -31,7 +31,7 @@
| 11 | `POST /api/v1/frps/action` | frps 的 start / stop / restart |
| 12 | `POST /api/v1/certs` | 推送证书(fullchain + privatekey) |
-鉴权:全部走 `s.auth(...)` 包装的 token 校验;agent **只监听 VPS 本地**,由 Fonu 主动发起请求,**不做任何主动上报**。
+鉴权:全部走 `s.auth(...)` 包装的 token 校验;agent **只监听 VPS 本地**,由 Havline 主动发起请求,**不做任何主动上报**。
### 1.2 agent 的数据面(`status` 已返回、但应用侧只用了一部分)
@@ -44,7 +44,7 @@
| `nginx_binary` / `nginx_conf_dir` | 配置的二进制与 vhost 目录 | 已用 |
| `ports` | 80 / 443 / 7000 / 8080 等端口是否监听 | **只用到了 8080 一项**(`AgentView.vue:359`) |
| `frps_active` / `frps_enabled` | systemd 单元是否运行 / 是否开机自启 | 已用 |
-| `frps_binary` / `frps_config_path` | Fonu 侧配置的路径 | 已用 |
+| `frps_binary` / `frps_config_path` | Havline 侧配置的路径 | 已用 |
| `frps_unit_exec` | systemd 实际执行的启动命令 | 已展示,**未做一致性核对** |
| `frps_version` | 探测到的 frps 版本 | 已展示 |
| `frps_log_tail` | frps 日志尾部 6 行 | 已展示 |
@@ -59,7 +59,7 @@
### 1.4 安装与升级链路(已闭环)
-- agent 二进制**内嵌在 Fonu 里**(`internal/frp/agentbin`,按 arch 取),通过 **SSH** 上传到 VPS 并注册/重启 systemd 单元(`agent_installer.go` + `ssh_installer.go`)。SSH 连接信息就是迁移 023 加的 `ssh_host / ssh_port / ssh_user / ssh_auth / ssh_secret_enc`。
+- agent 二进制**内嵌在 Havline 里**(`internal/frp/agentbin`,按 arch 取),通过 **SSH** 上传到 VPS 并注册/重启 systemd 单元(`agent_installer.go` + `ssh_installer.go`)。SSH 连接信息就是迁移 023 加的 `ssh_host / ssh_port / ssh_user / ssh_auth / ssh_secret_enc`。
- 因此「升级 Agent」不需要 agent 自己有下载能力——**自升级不是必须项**。
- 版本约定:`AgentVersion` 上方按「每项行为变更一行」的注释累积,改动后手动递增(当前 0.9.2)。
@@ -67,7 +67,7 @@
1. **没有资源采集**:VPS 的 CPU / 内存 / 磁盘 / 负载完全不可见。
2. **没有日志读取**:只有 frps 的 6 行 tail,Nginx 的 access / error 日志读不到(排障必须 SSH)。
-3. **frps 配置只写不读**:`PUT /api/v1/frps/config` 存在,没有对应的 GET;用户手改 VPS 上的 frps.toml 后 Fonu 无从知晓,下次下发直接覆盖。
+3. **frps 配置只写不读**:`PUT /api/v1/frps/config` 存在,没有对应的 GET;用户手改 VPS 上的 frps.toml 后 Havline 无从知晓,下次下发直接覆盖。
4. **没有证书清单**:只能推送,无法列出 VPS 上现有的证书与到期时间(宝塔自己申请的证书看不到)。
5. **部署是逐条的**:`SyncRouteSpec` 按单条规则调用,没有「全量重部署」入口——agent 重装/换机后只能一条条点。
6. **写入结果不含 reload 细节**:片段写入成功但 Nginx reload 失败时,用户拿不到失败原因。
@@ -89,7 +89,7 @@
| **P2-2** | reload 失败原因回传 | agent + 应用 | 同上 | 1h |
| **P3** | 不做:防火墙读写、任意命令执行、agent 自升级 | — | — | — |
-**贯穿约束(所有新增 agent 端点都适用)**:老版本 agent 没有新端点,应用侧调用必须**按「端点不存在」优雅降级**(收到 404/405 时显示「VPS 上的 agent 版本过低,请升级 Agent」而不是报错),否则用户升级了 Fonu 但没升级 agent 时页面会直接报错。
+**贯穿约束(所有新增 agent 端点都适用)**:老版本 agent 没有新端点,应用侧调用必须**按「端点不存在」优雅降级**(收到 404/405 时显示「VPS 上的 agent 版本过低,请升级 Agent」而不是报错),否则用户升级了 Havline 但没升级 agent 时页面会直接报错。
---
@@ -109,7 +109,7 @@
| 类别 | 定义 | 含义 |
| --- | --- | --- |
| 未部署 | `A - B` | 库里要部署但 VPS 上没有(agent 重装、换机、手工删过) |
-| 孤儿配置 | `B - A` | VPS 有但库里没有(手工加的 `fonu-*.conf`、已删规则的残留) |
+| 孤儿配置 | `B - A` | VPS 有但库里没有(手工加的 `havline-*.conf`、已删规则的残留) |
| 未生效 | `A ∩ (B - C)` | 文件在但未被 Nginx 加载(`syntax_ok=false`,或 include 未覆盖) |
| 内容不一致 | `RouteConf(domain)` 与「按当前部署设置生成的内容」规范化比对 | 手工改过上游 / 证书路径 / 安全项 |
@@ -117,18 +117,18 @@
**前端落点**:公网反代页顶部提示条(有差异时才出现)+「重新部署全部」按钮(二次确认,因为会覆盖手工改动)。
-**验收**:手工删一个 `fonu-*.conf` → 清单出现「未部署」,重部署后消失;手工改一条 conf 的上游 → 出现「内容不一致」;语法错误时归入「未生效」并给出原因。
+**验收**:手工删一个 `havline-*.conf` → 清单出现「未部署」,重部署后消失;手工改一条 conf 的上游 → 出现「内容不一致」;语法错误时归入「未生效」并给出原因。
### 3.3 frps 运行一致性核对
-- **比对三项**:`frps_unit_exec`(systemd 实际执行)vs Fonu 配置的 `frps_binary`;`frps_version` vs 期望版本;`frps_config_path` vs Fonu 生成路径。
+- **比对三项**:`frps_unit_exec`(systemd 实际执行)vs Havline 配置的 `frps_binary`;`frps_version` vs 期望版本;`frps_config_path` vs Havline 生成路径。
- **呈现**:AgentView「frps 控制」段加一行「启动路径一致性」,不一致时并列显示两个路径。
- **验收**:手工改过 systemd unit 的 VPS 上显示不一致;一致的机器显示绿色。
### 3.4 证书推送核对
- **数据**:已有 `RouteHealth` 的证书段(`cert_ok` / `cert_detail`)+ 推送结果。
-- **呈现**:公网反代页健康列已展示证书段;补一行「证书来源」(Fonu 推送 / 待推送 / VPS 本地已有)。
+- **呈现**:公网反代页健康列已展示证书段;补一行「证书来源」(Havline 推送 / 待推送 / VPS 本地已有)。
- **验收**:证书过期时健康列给出证书段失败与到期天数。
---
@@ -155,7 +155,7 @@
}
```
-- **实现要点**:**直接复用仓库里已有的 `internal/sysinfo`**(同一 module,`cmd/fonu-agent` 可直接引用)——`/proc/stat`、`/proc/meminfo`、`Statfs` 以及 Windows 的 kernel32 路径都已写好;agent 跑在 VPS 上,采到的天然是 VPS 自身数值。
+- **实现要点**:**直接复用仓库里已有的 `internal/sysinfo`**(同一 module,`cmd/havline-agent` 可直接引用)——`/proc/stat`、`/proc/meminfo`、`Statfs` 以及 Windows 的 kernel32 路径都已写好;agent 跑在 VPS 上,采到的天然是 VPS 自身数值。
- **CPU 差值**:`sysinfo.Read(prev)` 需要上次采样;agent 进程内保存上次 `CPUSample`,并对间隔 < 1 秒的重复调用直接返回上次结果(避免连续刷新时数值抖动)。
- **`load_avg` / `uptime_seconds`**:Linux 读 `/proc/loadavg`、`/proc/uptime`;非 Linux 省略这两个字段(前端按缺失处理)。
- **应用侧链路**:`AgentClient.Metrics(ctx)` → `GET /api/frpmulti/servers/{id}/agent/metrics` → AgentView 新增「VPS 资源」卡(复用本地仪表盘「系统资源」的行样式:名称 / 用量明细 / 4px 进度条 / 百分比,≥75% 转橙、≥90% 转红)。
@@ -181,7 +181,7 @@
- **路径来源(白名单,不接受任意 path 参数)**:
1. 优先从 `nginx -T` 的 dump 里解析 `access_log` / `error_log` 指令(agent 已有 `nginx -T` 解析与缓存经验,见 `resolveNginxConfDir`);
2. 解析不到时回落默认路径 `/var/log/nginx/{access,error}.log`;
- 3. 支持环境变量 `FONU_AGENT_NGINX_LOG_DIR` 覆盖(与现有 `FONU_AGENT_NGINX_CONF_DIR` 的回退风格一致)。
+ 3. 支持环境变量 `HAVLINE_AGENT_NGINX_LOG_DIR` 覆盖(与现有 `HAVLINE_AGENT_NGINX_CONF_DIR` 的回退风格一致)。
- **只读尾部**:实现 `tailFile(path, n)`(与现有 `journalTail` 同思路),从文件末尾按块倒读,避免把整个日志读进内存;单行超长(如 4KB)截断。
- **错误处理**:文件不存在 → 200 + `lines: []` + 原样返回 `path`(前端显示「未找到日志文件」);无权限 → 403 并给出明确原因。
- **应用侧链路**:`AgentClient.NginxLogs(ctx, serverID, type, lines)` → `GET /api/frpmulti/servers/{id}/agent/logs/nginx?...`;日志中心新增「公网 Nginx」页签,复用现有 `LogToolbar` / `LogPagination` / `EmptyState` 与 `logTimeCell`,与「FRP 日志」页签同一套呈现。
@@ -192,7 +192,7 @@
响应:`{ "path": "/etc/frp/frps.toml", "exists": true, "content": "...", "mod_time": "2026-09-23T18:00:00Z", "checked_at": "..." }`
- **实现要点**:读 `s.cfg.FrpsConfigPath`,限制文件大小(如 256KB),超限返回 `truncated: true`;文件不存在返回 `exists: false` 而不是错误。
-- **应用侧**:在「公网服务端」的 frps 同源配置弹窗里加「与 VPS 实际文件比对」——把「Fonu 即将下发的内容」与回读内容做规范化 diff(去注释与空白),不一致时提示「VPS 上的 frps.toml 已被手工修改」,并给「重新下发」按钮(复用已有 `PushFRPSConfig`)。
+- **应用侧**:在「公网服务端」的 frps 同源配置弹窗里加「与 VPS 实际文件比对」——把「Havline 即将下发的内容」与回读内容做规范化 diff(去注释与空白),不一致时提示「VPS 上的 frps.toml 已被手工修改」,并给「重新下发」按钮(复用已有 `PushFRPSConfig`)。
- **验收**:在 VPS 上手工改一行 frps.toml → 弹窗提示不一致并高亮差异行;重新下发后恢复一致。
---
@@ -204,8 +204,8 @@
响应:`{ "certs": [{ "domain": "7023.36800.cc", "cert_path": "...", "key_path": "...", "not_after": "2026-12-01T00:00:00Z", "days_left": 69 }], "warnings": [], "checked_at": "..." }`
- **实现要点**:遍历 `s.cfg.CertsDir` 下的域名目录,用标准库 `crypto/x509` 解析 `fullchain.pem` 的 `NotAfter`;解析失败的单条跳过并记入 `warnings`。
-- **应用侧**:与 Fonu 的证书列表交叉核对,输出「Fonu 有 / VPS 有 / 两边都有但到期时间不同」三类;宝塔自行申请的证书也能被看到。
-- **验收**:过期证书 `days_left < 0` 并标红;Fonu 未推送过的域名出现在「VPS 本地已有」分组。
+- **应用侧**:与 Havline 的证书列表交叉核对,输出「Havline 有 / VPS 有 / 两边都有但到期时间不同」三类;宝塔自行申请的证书也能被看到。
+- **验收**:过期证书 `days_left < 0` 并标红;Havline 未推送过的域名出现在「VPS 本地已有」分组。
### 5.2 reload 失败原因回传
@@ -221,7 +221,7 @@
1. **VPS 防火墙规则读写**(ufw / firewalld / 宝塔面板差异大、需要 root,误操作会直接断网)——最多做只读展示。
2. **任意命令执行端点**——agent 目前只有 token 鉴权,开放 shell 等于把 VPS 交出去。
-3. **agent 自升级**——现有链路(Fonu 内嵌二进制 + SSH 上传 + 重启 systemd)已经闭环,自升级只省掉存 SSH 凭据,收益有限,还要额外处理下载源与签名校验。
+3. **agent 自升级**——现有链路(Havline 内嵌二进制 + SSH 上传 + 重启 systemd)已经闭环,自升级只省掉存 SSH 凭据,收益有限,还要额外处理下载源与签名校验。
---
@@ -230,18 +230,18 @@
### 7.1 agent 侧(每新增一个端点都要走完)
1. `internal/agent/server.go`:加 handler + `mux.HandleFunc(...)`,并在 `AgentVersion` 上方按现有约定补一行版本说明(例如 `// 0.10.0:新增 /api/v1/metrics 资源采集与 /api/v1/logs/nginx 日志读取。`),把常量递增到 **`0.10.0`**。
-2. 重编两个架构的内嵌二进制(Fonu 编译期嵌入,必须是真实二进制而不是占位文件):
+2. 重编两个架构的内嵌二进制(Havline 编译期嵌入,必须是真实二进制而不是占位文件):
```powershell
$env:GOOS='linux'; $env:CGO_ENABLED='0'; $env:GOARCH='amd64'
-go build -trimpath -o internal/frp/agentbin/fonu-agent-linux-amd64.new ./cmd/fonu-agent
+go build -trimpath -o internal/frp/agentbin/havline-agent-linux-amd64.new ./cmd/havline-agent
$env:GOARCH='arm64'
-go build -trimpath -o internal/frp/agentbin/fonu-agent-linux-arm64.new ./cmd/fonu-agent
-Move-Item -Force internal/frp/agentbin/fonu-agent-linux-amd64.new internal/frp/agentbin/fonu-agent-linux-amd64
-Move-Item -Force internal/frp/agentbin/fonu-agent-linux-arm64.new internal/frp/agentbin/fonu-agent-linux-arm64
+go build -trimpath -o internal/frp/agentbin/havline-agent-linux-arm64.new ./cmd/havline-agent
+Move-Item -Force internal/frp/agentbin/havline-agent-linux-amd64.new internal/frp/agentbin/havline-agent-linux-amd64
+Move-Item -Force internal/frp/agentbin/havline-agent-linux-arm64.new internal/frp/agentbin/havline-agent-linux-arm64
```
-3. 重启本机 Fonu(加载新内嵌二进制)→ VPS 上「升级 Agent」→ 用 `GET /api/v1/status` 的 `agent_version` 确认已是 `0.10.0`。
+3. 重启本机 Havline(加载新内嵌二进制)→ VPS 上「升级 Agent」→ 用 `GET /api/v1/status` 的 `agent_version` 确认已是 `0.10.0`。
### 7.2 应用侧(每个端点对应一条链路)
@@ -260,10 +260,10 @@ Move-Item -Force internal/frp/agentbin/fonu-agent-linux-arm64.new internal/frp/a
| 风险 | 说明 | 应对 |
| --- | --- | --- |
-| **新老版本错配** | Fonu 已升级、VPS 上的 agent 仍是旧版 | 应用侧对新端点统一做 404/405 降级,前端显示「请升级 Agent」;不改动已有端点的行为 |
+| **新老版本错配** | Havline 已升级、VPS 上的 agent 仍是旧版 | 应用侧对新端点统一做 404/405 降级,前端显示「请升级 Agent」;不改动已有端点的行为 |
| **只读端点的权限** | `/var/log/nginx` 与证书目录可能不允许 agent 运行用户读取 | 端点返回明确的 403 + 路径,前端提示「agent 无读取权限」;安装脚本里补充读权限说明 |
| **`metrics` 采样抖动** | 高频调用时 CPU 差值失真 | 间隔 < 1 秒直接返回上次结果;前端按 15 秒轮询(与本地仪表盘一致) |
-| **全量重部署覆盖手工改动** | 会覆盖 VPS 上手工编辑过的 `fonu-*.conf` | 按钮带二次确认 + 差异预览;`RouteConf` 回读内容先展示再覆盖 |
-| **回滚** | agent 行为变更后需要退回旧版本 | agent 二进制随 Fonu 版本走:用旧版 Fonu 的「升级 Agent」即可把旧二进制重新上传;新增端点均为**纯增量**,不改变已有端点语义,应用侧回滚也无需数据迁移 |
+| **全量重部署覆盖手工改动** | 会覆盖 VPS 上手工编辑过的 `havline-*.conf` | 按钮带二次确认 + 差异预览;`RouteConf` 回读内容先展示再覆盖 |
+| **回滚** | agent 行为变更后需要退回旧版本 | agent 二进制随 Havline 版本走:用旧版 Havline 的「升级 Agent」即可把旧二进制重新上传;新增端点均为**纯增量**,不改变已有端点语义,应用侧回滚也无需数据迁移 |
-**总原则**:新增端点一律只读或纯增量、不改变已有端点语义;所有 agent 交互都保持「Fonu 主动拉取、agent 被动应答」的现有模型,不引入 agent 主动上报。
+**总原则**:新增端点一律只读或纯增量、不改变已有端点语义;所有 agent 交互都保持「Havline 主动拉取、agent 被动应答」的现有模型,不引入 agent 主动上报。
diff --git a/docs/agent-listen-strategy-plan.md b/docs/agent-listen-strategy-plan.md
index 2b45856..470cc05 100644
--- a/docs/agent-listen-strategy-plan.md
+++ b/docs/agent-listen-strategy-plan.md
@@ -14,14 +14,14 @@
推荐采用**自动为主、安全优先、旧模式显式兼容**的三层策略:
1. **默认模式:SSH 隧道 + 回环监听**
- - `fonu-agent` 默认只监听 `127.0.0.1:7700`。
- - 中央 Fonu 使用现有 SSH 凭据建立本地转发:
+ - `havline-agent` 默认只监听 `127.0.0.1:7700`。
+ - 中央 Havline 使用现有 SSH 凭据建立本地转发:
```text
- 中央 Fonu 127.0.0.1:<随机本地端口>
+ 中央 Havline 127.0.0.1:<随机本地端口>
→ SSH 加密连接
→ VPS 127.0.0.1:7700
- → fonu-agent
+ → havline-agent
```
- 不开放公网 Agent 端口,不需要域名、证书、云安全组或防火墙修改。
@@ -30,7 +30,7 @@
- Agent 仍只监听回环地址。
- HTTPS 不作为首次安装选项;Agent 安全安装完成后,用户可在「公网 Agent」页切换到该高级模式。
- 切换时自动配置 Nginx HTTPS 反代,并使用自签名证书。
- - 中央 Fonu 首次安装时记录证书指纹,后续按指纹校验。
+ - 中央 Havline 首次安装时记录证书指纹,后续按指纹校验。
- 适用于希望免维护常驻 SSH 隧道、且愿意开放一个 HTTPS 端口的用户。
3. **兼容模式:显式公网明文 HTTP**
@@ -38,7 +38,7 @@
- UI 持续显示高危警告。
- 不允许自动降级到该模式。
-默认安装与升级过程不需要用户设置 `FONU_AGENT_LISTEN`、TLS 证书或防火墙规则。
+默认安装与升级过程不需要用户设置 `HAVLINE_AGENT_LISTEN`、TLS 证书或防火墙规则。
---
@@ -49,7 +49,7 @@
当前入口:
```go
-listenv := flag.String("listen", envOr("FONU_AGENT_LISTEN", ":7700"), ...)
+listenv := flag.String("listen", envOr("HAVLINE_AGENT_LISTEN", ":7700"), ...)
```
`internal/agent.NewServer` 空配置时同样回落到:
@@ -73,7 +73,7 @@ cfg.ListenAddr = ":7700"
API: http://:7700
```
-### 2.3 中央 Fonu 连接
+### 2.3 中央 Havline 连接
安装完成后,`AgentInstall` 写入:
@@ -81,7 +81,7 @@ cfg.ListenAddr = ":7700"
AgentURL = fmt.Sprintf("http://%s:%d", server.SSHHost, 7700)
```
-中央 Fonu 后续直接访问 VPS 公网 IP 的 7700 端口。
+中央 Havline 后续直接访问 VPS 公网 IP 的 7700 端口。
### 2.4 当前问题
@@ -99,13 +99,13 @@ AgentURL = fmt.Sprintf("http://%s:%d", server.SSHHost, 7700)
### 3.1 必须实现
1. 新安装默认不监听公网网卡。
-2. 安装过程自动建立中央 Fonu 到 Agent 的安全连接。
+2. 安装过程自动建立中央 Havline 到 Agent 的安全连接。
3. 不需要用户手工配置网络或证书。
4. Agent 升级时可自动迁移旧监听配置。
5. 旧部署仍可显式使用公网 HTTP。
6. 传输失败时给出可执行的诊断信息。
-7. 中央 Fonu 重启后可自动恢复连接。
-8. Agent 与中央 Fonu 的版本兼容策略保持清晰。
+7. 中央 Havline 重启后可自动恢复连接。
+8. Agent 与中央 Havline 的版本兼容策略保持清晰。
### 3.2 非目标
@@ -155,7 +155,7 @@ AgentURL = fmt.Sprintf("http://%s:%d", server.SSHHost, 7700)
| 防火墙 | 需要放行一个 HTTPS 端口 |
| 证书 | 自动生成和固定,不需要公网 CA |
| 复杂度 | 中 |
-| 主要优势 | 中央 Fonu 可直接连接,不依赖常驻 SSH |
+| 主要优势 | 中央 Havline 可直接连接,不依赖常驻 SSH |
| 主要限制 | 云安全组仍需用户放行端口 |
结论:作为可选直连模式。
@@ -181,26 +181,26 @@ AgentURL = fmt.Sprintf("http://%s:%d", server.SSHHost, 7700)
```mermaid
flowchart LR
- Web[Fonu Web] --> API[Fonu 后端]
+ Web[Havline Web] --> API[Havline 后端]
API --> Tunnel[SSH 隧道管理器]
Tunnel -->|127.0.0.1:自动分配端口| SSH[SSH 加密连接]
SSH --> VPSLoopback[VPS 127.0.0.1:7700]
- VPSLoopback --> Agent[fonu-agent]
+ VPSLoopback --> Agent[havline-agent]
```
### 5.2 角色职责
| 组件 | 职责 |
| --- | --- |
-| Fonu 后端 | 决定 transport、启动隧道、调用 AgentClient |
+| Havline 后端 | 决定 transport、启动隧道、调用 AgentClient |
| 隧道管理器 | 建立 SSH 本地转发、保持连接、重连、健康检查 |
-| fonu-agent | 默认只监听回环,不再负责公网暴露 |
-| 安装脚本 | 写入 `FONU_AGENT_LISTEN=127.0.0.1:7700`,禁止默认可公开 |
+| havline-agent | 默认只监听回环,不再负责公网暴露 |
+| 安装脚本 | 写入 `HAVLINE_AGENT_LISTEN=127.0.0.1:7700`,禁止默认可公开 |
| UI | 显示安全模式、连接状态、可选迁移和显式旧模式警告 |
### 5.3 连接抽象
-中央 Fonu 不再把“公网 IP + 7700”当作 Agent 的唯一定义。新增 transport 概念:
+中央 Havline 不再把“公网 IP + 7700”当作 Agent 的唯一定义。新增 transport 概念:
```text
tunnel SSH 本地转发,默认
@@ -310,7 +310,7 @@ const defaultListenAddr = "127.0.0.1:7700"
以下位置同步:
-- `cmd/fonu-agent/main.go`
+- `cmd/havline-agent/main.go`
- `internal/agent.NewServer`
- `scripts/agent-install.sh`
- `internal/frp/agent_installer.go` 内嵌安装脚本
@@ -329,7 +329,7 @@ VPS公网IP:7700
则必须显式设置:
```dotenv
-FONU_AGENT_ALLOW_REMOTE=1
+HAVLINE_AGENT_ALLOW_REMOTE=1
```
否则 Agent 拒绝启动并输出:
@@ -337,21 +337,21 @@ FONU_AGENT_ALLOW_REMOTE=1
```text
检测到 Agent 将监听非回环地址,但未显式允许远程访问。
默认安全模式只监听 127.0.0.1:7700。
-如确认需要公网明文访问,请设置 FONU_AGENT_ALLOW_REMOTE=1。
+如确认需要公网明文访问,请设置 HAVLINE_AGENT_ALLOW_REMOTE=1。
```
### 7.3 旧配置迁移
-旧版本 `agent.env` 可能没有 `FONU_AGENT_LISTEN`,此时:
+旧版本 `agent.env` 可能没有 `HAVLINE_AGENT_LISTEN`,此时:
1. 新 Agent 默认监听回环;
2. 安装脚本显式写入:
```dotenv
- FONU_AGENT_LISTEN=127.0.0.1:7700
+ HAVLINE_AGENT_LISTEN=127.0.0.1:7700
```
-3. 中央 Fonu 在执行「升级 Agent」时同步建立隧道;
+3. 中央 Havline 在执行「升级 Agent」时同步建立隧道;
4. 升级完成后切换 transport;
5. 旧公网地址从 `agent_url` 中移除;
6. 不停留手工维护窗口。
@@ -376,12 +376,12 @@ Agent `/api/v1/status` 新增:
### 8.1 新安装
1. 用户填写 VPS SSH 信息。
-2. 中央 Fonu 通过 SSH 探测系统与 Nginx。
+2. 中央 Havline 通过 SSH 探测系统与 Nginx。
3. 分配 `agent_local_port`。
4. 上传 Agent 二进制。
5. 安装脚本写入:
- Token;
- - `FONU_AGENT_LISTEN=127.0.0.1:7700`;
+ - `HAVLINE_AGENT_LISTEN=127.0.0.1:7700`;
- systemd 服务。
6. 启动 Agent。
7. 通过 SSH 本地转发建立隧道。
@@ -413,7 +413,7 @@ Agent `/api/v1/status` 新增:
1. 读取启用管理且 transport=tunnel 的服务端。
2. 逐个调用 `Tunnel.Ensure`。
-3. 启动失败不阻塞整个 Fonu 启动。
+3. 启动失败不阻塞整个 Havline 启动。
4. UI 显示重连状态。
5. 后台按指数退避继续重试。
@@ -426,7 +426,7 @@ Agent `/api/v1/status` 新增:
- 不希望长期维护 SSH 隧道;
- 可以接受开放一个 HTTPS 端口;
- 没有域名或不想申请公网证书;
-- 希望 Agent API 可被中央 Fonu直接访问。
+- 希望 Agent API 可被中央 Havline直接访问。
### 9.2 安装后切换流程
@@ -436,8 +436,8 @@ Agent `/api/v1/status` 新增:
2. 切换流程生成自签名证书:
```text
- /etc/fonu-agent/tls/agent.crt
- /etc/fonu-agent/tls/agent.key
+ /etc/havline-agent/tls/agent.crt
+ /etc/havline-agent/tls/agent.key
```
3. 自动寻找可用端口:
@@ -449,8 +449,8 @@ Agent `/api/v1/status` 新增:
```nginx
server {
listen 7443 ssl;
- ssl_certificate /etc/fonu-agent/tls/agent.crt;
- ssl_certificate_key /etc/fonu-agent/tls/agent.key;
+ ssl_certificate /etc/havline-agent/tls/agent.crt;
+ ssl_certificate_key /etc/havline-agent/tls/agent.key;
location /api/v1/ {
proxy_pass http://127.0.0.1:7700;
@@ -459,7 +459,7 @@ Agent `/api/v1/status` 新增:
```
5. 返回证书 SHA-256 指纹。
-6. 中央 Fonu 保存 pin,并使用自定义校验。
+6. 中央 Havline 保存 pin,并使用自定义校验。
### 9.3 TLS 固定
@@ -494,7 +494,7 @@ VerifyPeerCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
云厂商安全组始终不能自动修改。UI 必须提示:
```text
-请在云控制台放行 TCP 7443。主机防火墙需要你确认后由 Fonu 执行,Fonu 不会修改云安全组。
+请在云控制台放行 TCP 7443。主机防火墙需要你确认后由 Havline 执行,Havline 不会修改云安全组。
```
---
@@ -504,11 +504,11 @@ VerifyPeerCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
仅用于旧部署迁移或用户明确要求:
```dotenv
-FONU_AGENT_LISTEN=:7700
-FONU_AGENT_ALLOW_REMOTE=1
+HAVLINE_AGENT_LISTEN=:7700
+HAVLINE_AGENT_ALLOW_REMOTE=1
```
-中央 Fonu UI 必须:
+中央 Havline UI 必须:
- 显示“公网明文传输”红色状态;
- 不在新安装中默认选择;
@@ -638,7 +638,7 @@ Agent 监听:127.0.0.1:7700
### 14.1 单元测试
- 默认监听地址是回环;
-- 非回环监听缺少 `FONU_AGENT_ALLOW_REMOTE` 时拒绝启动;
+- 非回环监听缺少 `HAVLINE_AGENT_ALLOW_REMOTE` 时拒绝启动;
- 回环监听无需额外变量;
- `agent_transport` 序列化与旧数据兼容;
- 隧道本地端口分配、冲突与释放;
@@ -661,7 +661,7 @@ Agent 监听:127.0.0.1:7700
至少覆盖:
- Debian / Ubuntu / CentOS;
-- fnOS 中的 Fonu;
+- fnOS 中的 Havline;
- 宝塔 Nginx;
- 已存在 80 / 443 / 7443 占用的 VPS;
- 云安全组只开放 SSH 的 VPS;
@@ -673,7 +673,7 @@ Agent 监听:127.0.0.1:7700
- 公网扫描不到 7700;
- 无需新增云安全组规则;
- UI 显示隧道已连接;
-- Fonu 重启后自动恢复;
+- Havline 重启后自动恢复;
- 「升级 Agent」可以自动迁移旧配置;
- 失败时不会丢失旧连接。
@@ -684,7 +684,7 @@ Agent 监听:127.0.0.1:7700
### 阶段 1:默认回环与防误开
- Agent 默认监听改为 `127.0.0.1:7700`;
-- 非回环监听要求 `FONU_AGENT_ALLOW_REMOTE=1`;
+- 非回环监听要求 `HAVLINE_AGENT_ALLOW_REMOTE=1`;
- 安装脚本显式写入监听地址;
- UI 显示监听范围。
@@ -729,7 +729,7 @@ Agent 监听:127.0.0.1:7700
| --- | --- | --- | --- |
| 1 | SSH 隧道作为唯一默认模式 | 首次安装不提供模式选择,统一安装为回环监听 + SSH 隧道;HTTPS 直连只作为安装后的高级切换项 | 现有安装流程已经依赖 SSH;默认路径必须最少输入、最少网络要求 |
| 2 | 隧道本地端口使用固定范围 | 固定为 `17700-18700`,绑定 `127.0.0.1`;分配结果持久化;启动时先复用持久化端口,再在范围内顺序查找空闲端口;范围耗尽时明确报错,不使用临时端口 | 固定范围便于排查和审计;持久化避免每次重启变化;不回退临时端口避免地址漂移 |
-| 3 | 隧道跟随主应用生命周期 | 使用应用内 `agent_tunnel` 管理器,在 Fonu 启动后按服务端恢复;应用退出时统一关闭;断线后指数退避重连;首版不新增独立 systemd 服务 | 当前项目是单进程控制面,调度器、FRP、监控均在同一生命周期内,新增守护进程会扩大部署复杂度 |
+| 3 | 隧道跟随主应用生命周期 | 使用应用内 `agent_tunnel` 管理器,在 Havline 启动后按服务端恢复;应用退出时统一关闭;断线后指数退避重连;首版不新增独立 systemd 服务 | 当前项目是单进程控制面,调度器、FRP、监控均在同一生命周期内,新增守护进程会扩大部署复杂度 |
| 4 | 公网 HTTP 保留整个 1.x 兼容期 | 自 `1.4.0` 起不再允许新安装选择 HTTP;存量连接继续可用但显示高危警告并提供迁移;计划在 `2.0.0` 移除 | 需要给旧 Agent 和环境留出迁移窗口,同时从版本边界上明确技术债的截止点 |
| 5 | HTTPS 自签名证书使用短期自动轮换 | ECDSA P-256,有效期 365 天;剩余 30 天时自动轮换;轮换期间同时接受旧 pin 和新 pin,双 pin 窗口 7 天;优先通过现有 SSH 通道获取新指纹后再切换 | 避免长期证书失管;双 pin 防止轮换瞬间把所有中央连接锁死 |
| 6 | 首版不自动改主机防火墙 | 首先只检测、展示和提示;SSH 隧道模式本身不需要放行新端口;HTTPS 高级模式后续再提供显式的“一键放行主机防火墙”操作,并始终不修改云厂商安全组 | 防火墙和云安全组属于高风险外部状态;首版先把安全默认值落地,避免自动变更导致 SSH 或现有服务失联 |
diff --git a/docs/agent-proxy-roadmap.md b/docs/agent-proxy-roadmap.md
index 6d749e3..c88838b 100644
--- a/docs/agent-proxy-roadmap.md
+++ b/docs/agent-proxy-roadmap.md
@@ -33,7 +33,7 @@
- 手动部署 / 移除单条规则的反代(部署时自动推证书、自动读最新 vhost 端口);
- VPS 当前已加载路由展示(`agentStatus.routes`);
-- 反代文件按 `fonu-<域名>.conf` 写入 Nginx 实际加载目录(`-T` 试探-验证)。
+- 反代文件按 `havline-<域名>.conf` 写入 Nginx 实际加载目录(`-T` 试探-验证)。
---
@@ -103,7 +103,7 @@ curl -s -o /dev/null -w "%{http_code}" -H "Host: <域名>" http://127.0.0.1:.conf.bak-<时间戳>`(保留最近 N 份),
+每次部署在 agent 侧留 `havline-<域名>.conf.bak-<时间戳>`(保留最近 N 份),
页面提供「查看历史 / 回滚到此版本」。
- 误操作(比如改坏了 upstream)30 秒恢复;
@@ -112,7 +112,7 @@ curl -s -o /dev/null -w "%{http_code}" -H "Host: <域名>" http://127.0.0.1:" http://127.0.0.1: 目标:把本地反向代理(`ProxyView.vue` 规则弹窗)中真正有价值的安全与运维设置,按公网反代的语义迁移到
-> 「公网反代」页(`/public-proxy`,`ProxyRemoteView.vue`)的「部署设置」中,由 fonu-agent 生成对应的 Nginx vhost 配置。
+> 「公网反代」页(`/public-proxy`,`ProxyRemoteView.vue`)的「部署设置」中,由 havline-agent 生成对应的 Nginx vhost 配置。
>
> 原则:**公网反代只管「公网入口」这一层**。凡是会破坏 frps vhost 路由、或本质属于「本机内网分流」的选项,
> 一律不迁移(见「明确不做」一节)。
@@ -22,7 +22,7 @@
-
**高级**:仅 TLS 1.3、安全响应头、限流、连接数限制
-第一梯队 7 项都是 agent 模板内改动 + Fonu 参数透传,风险低、见效快;第二梯队 3 项需要 agent 多写一个 http 上下文文件。说一声我从第一梯队开始做,或者指定要哪几项。
+第一梯队 7 项都是 agent 模板内改动 + Havline 参数透传,风险低、见效快;第二梯队 3 项需要 agent 多写一个 http 上下文文件。说一声我从第一梯队开始做,或者指定要哪几项。
@@ -50,7 +50,7 @@
- `basic_auth_user`(密码由 agent 自动生成并落盘 htpasswd);
- 默认头:Host / X-Real-IP / X-Forwarded-For。
-Fonu 侧对应链路:`ProxyOptions`(`internal/frp/model.go`)→ `DeployAgentRoute` → `AgentClient.SyncRouteEx`
+Havline 侧对应链路:`ProxyOptions`(`internal/frp/model.go`)→ `DeployAgentRoute` → `AgentClient.SyncRouteEx`
→ `PUT /api/v1/routes`。
---
@@ -73,9 +73,9 @@ Fonu 侧对应链路:`ProxyOptions`(`internal/frp/model.go`)→ `DeployAge
| # | 功能 | 关键约束 |
| --- | --- | --- |
-| 8 | **请求限流** | `limit_req_zone` 只能出现在 `http` 块。agent 需额外生成 `fonu-limits.conf`(宝塔将 vhost 目录 include 在 `http{}` 内,因此该文件顶层指令即 http 上下文),各域名 server 内 `limit_req zone=fonu_<域名>_req burst=N nodelay;` |
-| 9 | **连接数限制** | 同上,`limit_conn_zone` + `limit_conn fonu_<域名>_conn N;` |
-| 10 | **仅中国大陆 IP** | 需 `geo` 模块 + 中国 IP 段下发到 VPS(Fonu 已有 china cidr 模块,7456 条 v4),下发体积最大、成本最高,建议最后做 |
+| 8 | **请求限流** | `limit_req_zone` 只能出现在 `http` 块。agent 需额外生成 `havline-limits.conf`(宝塔将 vhost 目录 include 在 `http{}` 内,因此该文件顶层指令即 http 上下文),各域名 server 内 `limit_req zone=havline_<域名>_req burst=N nodelay;` |
+| 9 | **连接数限制** | 同上,`limit_conn_zone` + `limit_conn havline_<域名>_conn N;` |
+| 10 | **仅中国大陆 IP** | 需 `geo` 模块 + 中国 IP 段下发到 VPS(Havline 已有 china cidr 模块,7456 条 v4),下发体积最大、成本最高,建议最后做 |
### 明确不做(字段 / 语义不匹配)
@@ -109,13 +109,13 @@ Fonu 侧对应链路:`ProxyOptions`(`internal/frp/model.go`)→ `DeployAge
- 这些字段**只作用于公网 Nginx 模板,绝不写入 frpc.toml**(与现有 `websocket` / `redirect_https` 等一致,在 `ProxyOptions` 上以注释标明);
- 「启用 HTTPS」采用**反向字段 `https_disabled`**:字段缺省时保持现有行为(证书推送成功即启用 TLS),
- 保证新 Fonu + 旧 agent、旧数据不出现行为突变。
+ 保证新 Havline + 旧 agent、旧数据不出现行为突变。
---
## 四、实施步骤
-### 阶段 1:第一梯队后端(agent + Fonu)
+### 阶段 1:第一梯队后端(agent + Havline)
1. `internal/agent/server.go`
- `routeRequest` 增加第一梯队字段;
@@ -149,8 +149,8 @@ Fonu 侧对应链路:`ProxyOptions`(`internal/frp/model.go`)→ `DeployAge
### 阶段 3:第二梯队(按需)
-7. agent 增加 `fonu-limits.conf` 生成器(http 上下文 zone 定义)+ 每域名 server 引用;
-8. 中国 IP:Fonu 将 china cidr 列表推送到 agent(`/var/lib/fonu-agent/geo/china.conf`),agent 生成 `geo` 块。
+7. agent 增加 `havline-limits.conf` 生成器(http 上下文 zone 定义)+ 每域名 server 引用;
+8. 中国 IP:Havline 将 china cidr 列表推送到 agent(`/var/lib/havline-agent/geo/china.conf`),agent 生成 `geo` 块。
---
@@ -159,7 +159,7 @@ Fonu 侧对应链路:`ProxyOptions`(`internal/frp/model.go`)→ `DeployAge
每次改动后全量跑(项目既有闭环):
```powershell
-cd f:\Fonu-main\Fonu-1.0.2
+cd f:\Havline-main\Havline-1.0.2
gofmt -w internal/agent/server.go internal/frp/model.go internal/frp/agent_client.go internal/frp/service.go
go build ./... ; go vet ./internal/frp ./internal/agent ./internal/api
go test ./internal/frp ./internal/api
@@ -170,7 +170,7 @@ agent 二进制需按既有流程交叉编译(amd64 + arm64)并升级:
```powershell
$env:GOOS='linux'; $env:GOARCH='amd64'; $env:CGO_ENABLED='0'
-go build -trimpath -o internal/frp/agentbin/fonu-agent-linux-amd64.new ./cmd/fonu-agent
+go build -trimpath -o internal/frp/agentbin/havline-agent-linux-amd64.new ./cmd/havline-agent
# arm64 同理;Move-Item 覆盖后,在「公网 Agent」页执行「升级 Agent」
```
@@ -178,7 +178,7 @@ VPS 侧实测(部署 7023 规则后):
```bash
nginx -t # 语法
-cat /www/server/panel/vhost/nginx/fonu-7023.36800.cc.conf # 配置内容(或页面「查看配置」)
+cat /www/server/panel/vhost/nginx/havline-7023.36800.cc.conf # 配置内容(或页面「查看配置」)
curl -I http://7023.36800.cc # 强制跳转 → 301
curl -sI -H 'Host: 7023.36800.cc' http://127.0.0.1:9080 # 回源自检
curl -k -sI https://7023.36800.cc # 响应头(HSTS / X-Frame-Options)
@@ -196,8 +196,8 @@ for i in $(seq 1 50); do curl -s -o /dev/null -w '%{http_code} ' http://7023.368
| 场景 | 期望行为 |
| --- | --- |
-| 新 Fonu + 旧 agent | 旧 agent 忽略未知 JSON 字段(Go 默认行为),新选项静默不生效 → **部署前必须先「升级 Agent」**,页面提示 |
-| 旧 Fonu + 新 agent | 新字段缺省,行为与现状一致(`https_disabled` 缺省 = 启用 TLS) |
+| 新 Havline + 旧 agent | 旧 agent 忽略未知 JSON 字段(Go 默认行为),新选项静默不生效 → **部署前必须先「升级 Agent」**,页面提示 |
+| 旧 Havline + 新 agent | 新字段缺省,行为与现状一致(`https_disabled` 缺省 = 启用 TLS) |
| frps v0.68.0 / v0.71.0 | 与本方案无关(作用于 Nginx vhost,不依赖 dashboard API) |
| 回滚 | 关闭对应开关后重新部署即可恢复;agent 侧每域名配置为整文件覆盖写,无残留 |
diff --git a/docs/agent-view-design-v2.md b/docs/agent-view-design-v2.md
index c17c6b0..2d16831 100644
--- a/docs/agent-view-design-v2.md
+++ b/docs/agent-view-design-v2.md
@@ -17,11 +17,11 @@
| 框架 | Vue 3 + `
+