feat: rename project from Fonu to Havline
CI / verify (push) Canceled after 0s

This commit is contained in:
2026-10-01 16:54:35 +08:00
parent c377405696
commit 8fa5d922ac
250 changed files with 6724 additions and 5070 deletions
+34 -34
View File
@@ -5,10 +5,10 @@ import (
"fmt"
"strings"
"github.com/fonu/fonu/internal/frp/agentbin"
"github.com/havline/havline/internal/frp/agentbin"
)
// AgentInstaller 负责把内嵌的 fonu-agent 通过 SSH 装到 VPS 上,并收集安装结果。
// AgentInstaller 负责把内嵌的 havline-agent 通过 SSH 装到 VPS 上,并收集安装结果。
type AgentInstaller struct {
SSH SSHInstaller
}
@@ -28,12 +28,12 @@ func (i AgentInstaller) Probe(ctx context.Context) (map[string]any, error) {
return map[string]any{
"arch": arch,
"nginx_conf": out(`conf=$(nginx -V 2>&1 | grep -o '\-\-conf-path=[^ ]*' | head -1 | cut -d= -f2-); [ -z "$conf" ] && conf=/etc/nginx/nginx.conf; grep '^[[:space:]]*include' "$conf" 2>/dev/null | grep -m1 -E 'conf\.d|sites-enabled|vhost/nginx' || ls -d /etc/nginx/conf.d 2>/dev/null`),
"agent_exist": out("test -x /usr/local/bin/fonu-agent && echo yes || echo no") == "yes",
"agent_running": out("systemctl is-active fonu-agent 2>/dev/null || true"),
"agent_version": out("/usr/local/bin/fonu-agent -version 2>/dev/null || echo 未知"),
"agent_exist": out("test -x /usr/local/bin/havline-agent && echo yes || echo no") == "yes",
"agent_running": out("systemctl is-active havline-agent 2>/dev/null || true"),
"agent_version": out("/usr/local/bin/havline-agent -version 2>/dev/null || echo 未知"),
// Token 只回「是否已配置 + 末 4 位」:明文不经过 HTTP 返回前端(库里同一字段也是掩码)
"agent_token_configured": out("test -s /etc/fonu-agent/agent.env && grep -q '^FONU_AGENT_TOKEN=' /etc/fonu-agent/agent.env && echo yes || echo no") == "yes",
"agent_token_hint": out("sed -n 's/^FONU_AGENT_TOKEN=//p' /etc/fonu-agent/agent.env 2>/dev/null | tr -d '\\n' | tail -c 4"),
"agent_token_configured": out("test -s /etc/havline-agent/agent.env && grep -q '^HAVLINE_AGENT_TOKEN=' /etc/havline-agent/agent.env && echo yes || echo no") == "yes",
"agent_token_hint": out("sed -n 's/^HAVLINE_AGENT_TOKEN=//p' /etc/havline-agent/agent.env 2>/dev/null | tr -d '\\n' | tail -c 4"),
"frps_alive": out("systemctl is-active frps 2>/dev/null; pgrep -x frps >/dev/null && echo active || true"),
"frps_version": out("/usr/local/bin/frps --version 2>/dev/null || echo 未安装"),
"frps_config": out("test -f /etc/frp/frps.toml && echo /etc/frp/frps.toml || echo 配置文件不存在"),
@@ -57,7 +57,7 @@ func (i AgentInstaller) Install(ctx context.Context) (string, error) {
}
// 开发环境(go run / 非 Docker 构建)里内嵌的是占位文件;提前报错而不是让 VPS 上 systemd 反复 Exec format error
if !isELFBinary(binary) {
return "", fmt.Errorf("内嵌 agent 二进制无效(开发构建占位文件)。请用 Docker 构建或先执行构建脚本生成真实 agent:go build -o internal/frp/agentbin/fonu-agent-linux-amd64 ./cmd/fonu-agent(GOOS=linux GOARCH=amd64)")
return "", fmt.Errorf("内嵌 agent 二进制无效(开发构建占位文件)。请用 Docker 构建或先执行构建脚本生成真实 agent:go build -o internal/frp/agentbin/havline-agent-linux-amd64 ./cmd/havline-agent(GOOS=linux GOARCH=amd64)")
}
script, err := agentInstallScript()
if err != nil {
@@ -76,7 +76,7 @@ func (i AgentInstaller) Install(ctx context.Context) (string, error) {
// Uninstall 停止并移除 agent 服务;--keep-data 保留路由与证书。
func (i AgentInstaller) Uninstall(ctx context.Context, keepData bool) (string, error) {
command := "/usr/local/bin/fonu-agent-uninstall"
command := "/usr/local/bin/havline-agent-uninstall"
if keepData {
command += " --keep-data"
}
@@ -86,14 +86,14 @@ func (i AgentInstaller) Uninstall(ctx context.Context, keepData bool) (string, e
// RestorePreviousConfig 在安全迁移验证失败时恢复安装前的 agent.env,并重启旧监听配置。
func (i AgentInstaller) RestorePreviousConfig(ctx context.Context) error {
command := `if [ -f /etc/fonu-agent/agent.env.previous ]; then cp /etc/fonu-agent/agent.env.previous /etc/fonu-agent/agent.env && chmod 600 /etc/fonu-agent/agent.env && systemctl restart fonu-agent; fi`
command := `if [ -f /etc/havline-agent/agent.env.previous ]; then cp /etc/havline-agent/agent.env.previous /etc/havline-agent/agent.env && chmod 600 /etc/havline-agent/agent.env && systemctl restart havline-agent; fi`
_, err := i.SSH.Run(ctx, command)
return err
}
// Diagnose agent 失联时走 SSH 急救:进程、端口、服务日志尾部。
func (i AgentInstaller) Diagnose(ctx context.Context) (string, error) {
return i.SSH.Run(ctx, "systemctl status fonu-agent --no-pager -l 2>/dev/null | head -20; echo '---'; journalctl -u fonu-agent -n 15 --no-pager 2>/dev/null; echo '---'; ss -tlnp 2>/dev/null | grep ':7700 ' || echo '7700 not listening'")
return i.SSH.Run(ctx, "systemctl status havline-agent --no-pager -l 2>/dev/null | head -20; echo '---'; journalctl -u havline-agent -n 15 --no-pager 2>/dev/null; echo '---'; ss -tlnp 2>/dev/null | grep ':7700 ' || echo '7700 not listening'")
}
func extractToken(output string) string {
@@ -110,25 +110,25 @@ func agentInstallScript() ([]byte, error) {
return []byte(`#!/usr/bin/env bash
set -eu
if [ "$(id -u)" -ne 0 ]; then echo "请使用 root 或 sudo 运行" >&2; exit 1; fi
AGENT_BINARY=${1:-/tmp/fonu-agent}
PREFIX=/usr/local/bin/fonu-agent
CONF_DIR=/etc/fonu-agent
AGENT_BINARY=${1:-/tmp/havline-agent}
PREFIX=/usr/local/bin/havline-agent
CONF_DIR=/etc/havline-agent
CONF_FILE="$CONF_DIR/agent.env"
UNIT=/etc/systemd/system/fonu-agent.service
UNIT=/etc/systemd/system/havline-agent.service
if [ "${1:-}" = "--uninstall" ]; then
systemctl stop fonu-agent 2>/dev/null || true
systemctl disable fonu-agent 2>/dev/null || true
rm -f "$UNIT" "$PREFIX" /usr/local/bin/fonu-agent-uninstall
systemctl stop havline-agent 2>/dev/null || true
systemctl disable havline-agent 2>/dev/null || true
rm -f "$UNIT" "$PREFIX" /usr/local/bin/havline-agent-uninstall
systemctl daemon-reload
if [ "${2:-}" != "--keep-data" ]; then rm -rf /var/lib/fonu-agent; fi
echo "fonu-agent 已卸载"
if [ "${2:-}" != "--keep-data" ]; then rm -rf /var/lib/havline-agent; fi
echo "havline-agent 已卸载"
exit 0
fi
mkdir -p "$CONF_DIR" /var/lib/fonu-agent/certs
mkdir -p "$CONF_DIR" /var/lib/havline-agent/certs
# 先停服务,再原子替换;安装后必须确认目标存在、可执行且能输出版本
systemctl stop fonu-agent 2>/dev/null || true
systemctl stop havline-agent 2>/dev/null || true
TMP_PREFIX="${PREFIX}.new"
# 迁移旧版 Agent 留在 nginx include 范围内的 <配置>.versions 目录(保留历史版本内容)
for NGINX_DIR in /etc/nginx/sites-enabled /etc/nginx/conf.d; do
@@ -157,23 +157,23 @@ fi
printf 'agent version: %s\n' "$AGENT_VERSION"
if [ ! -f "$CONF_FILE" ]; then
umask 077
# 用 /dev/urandom 生成 64 位 hex token,不依赖 openssl(缺失时会静默写入空 token,导致 Fonu 无法认证 agent)
printf 'FONU_AGENT_TOKEN=%s\n' "$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')" > "$CONF_FILE"
# 用 /dev/urandom 生成 64 位 hex token,不依赖 openssl(缺失时会静默写入空 token,导致 Havline 无法认证 agent)
printf 'HAVLINE_AGENT_TOKEN=%s\n' "$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')" > "$CONF_FILE"
fi
cp "$CONF_FILE" "$CONF_FILE.previous" 2>/dev/null || true
touch "$CONF_FILE"
sed -i '/^FONU_AGENT_LISTEN=/d; /^FONU_AGENT_ALLOW_REMOTE=/d' "$CONF_FILE"
printf 'FONU_AGENT_LISTEN=127.0.0.1:7700\nFONU_AGENT_ALLOW_REMOTE=0\n' >> "$CONF_FILE"
sed -i '/^HAVLINE_AGENT_LISTEN=/d; /^HAVLINE_AGENT_ALLOW_REMOTE=/d' "$CONF_FILE"
printf 'HAVLINE_AGENT_LISTEN=127.0.0.1:7700\nHAVLINE_AGENT_ALLOW_REMOTE=0\n' >> "$CONF_FILE"
cat > "$UNIT" <<'UNIT'
[Unit]
Description=Fonu FRPS management agent
Description=Havline FRPS management agent
After=network-online.target nginx.service
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/fonu-agent/agent.env
ExecStart=/usr/local/bin/fonu-agent
EnvironmentFile=/etc/havline-agent/agent.env
ExecStart=/usr/local/bin/havline-agent
Restart=on-failure
RestartSec=3
@@ -181,11 +181,11 @@ RestartSec=3
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl restart fonu-agent
systemctl enable fonu-agent
cp "$0" /usr/local/bin/fonu-agent-uninstall
echo "fonu-agent installed"
echo "Token: $(sed -n 's/^FONU_AGENT_TOKEN=//p' "$CONF_FILE")"
systemctl restart havline-agent
systemctl enable havline-agent
cp "$0" /usr/local/bin/havline-agent-uninstall
echo "havline-agent installed"
echo "Token: $(sed -n 's/^HAVLINE_AGENT_TOKEN=//p' "$CONF_FILE")"
`), nil
}