From 36ffad99335544e0ef94eac35fe665c2833b342b Mon Sep 17 00:00:00 2001 From: engigu Date: Wed, 14 Jan 2026 10:04:29 +0800 Subject: [PATCH 01/34] fix: remote agent exec url_path_prefix error --- .gitignore | 1 + agent/agent.go | 12 ++++++++++- agent/config.example.ini | 4 ++++ internal/controllers/agent_controller.go | 24 +++++++++++++++++++++- internal/router/router.go | 26 ++++++++++++------------ 5 files changed, 52 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index ca83406..8f6ede5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ baihu # Data & Logs data/ +agent/logs/ !data/agent/ data/agent/* !data/agent/version.txt diff --git a/agent/agent.go b/agent/agent.go index 4b1d11e..13db1c5 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -202,9 +202,19 @@ func (a *Agent) connectWS() error { wsURL = strings.Replace(wsURL, "https://", "wss://", 1) wsURL = fmt.Sprintf("%s/api/agent/ws?token=%s&machine_id=%s", wsURL, url.QueryEscape(a.config.Token), url.QueryEscape(a.machineID)) + log.Infof("正在连接 WebSocket: %s", wsURL) + log.Infof("Token: %s..., MachineID: %s...", a.config.Token[:8], a.machineID[:16]) + dialer := websocket.Dialer{HandshakeTimeout: 10 * time.Second} - conn, _, err := dialer.Dial(wsURL, nil) + conn, resp, err := dialer.Dial(wsURL, nil) if err != nil { + if resp != nil { + bodyBytes, _ := io.ReadAll(resp.Body) + log.Errorf("WebSocket 握手失败: HTTP %d, Body: %s", resp.StatusCode, string(bodyBytes)) + resp.Body.Close() + } else { + log.Errorf("WebSocket 连接失败: %v", err) + } return err } diff --git a/agent/config.example.ini b/agent/config.example.ini index 98ebf18..8016dd1 100644 --- a/agent/config.example.ini +++ b/agent/config.example.ini @@ -1,6 +1,10 @@ [agent] # 主服务器地址(http/https,Agent 会自动转换为 WebSocket 连接) +# 如果主服务配置了url_prefix, 这里要也要加上路径 server_url = http://192.168.1.100:8052 +# 比如 url_prefix=/baihu +; server_url = http://192.168.1.100:8052/baihu + # Agent 名称(留空则使用主机名) name = # 注册令牌(首次注册时填写,注册成功后会自动替换为认证 Token) diff --git a/internal/controllers/agent_controller.go b/internal/controllers/agent_controller.go index de1bff4..abefcd8 100644 --- a/internal/controllers/agent_controller.go +++ b/internal/controllers/agent_controller.go @@ -337,8 +337,19 @@ func (c *AgentController) ForceUpdate(ctx *gin.Context) { // WSConnect Agent WebSocket 连接 func (c *AgentController) WSConnect(ctx *gin.Context) { + // 添加 panic 恢复 + defer func() { + if r := recover(); r != nil { + logger.Errorf("[AgentWS] WSConnect panic: %v", r) + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"}) + } + }() + ip := ctx.ClientIP() + // 打印请求信息用于调试 + logger.Infof("[AgentWS] 收到连接请求: IP=%s, URL=%s", ip, ctx.Request.URL.String()) + // 检查 IP 限流 if allowed, reason := c.wsManager.CheckRateLimit(ip); !allowed { logger.Warnf("[AgentWS] IP %s 被限流: %s", ip, reason) @@ -349,36 +360,45 @@ func (c *AgentController) WSConnect(ctx *gin.Context) { token := ctx.Query("token") if token == "" { c.wsManager.RecordConnectFail(ip) + logger.Warnf("[AgentWS] 连接失败: 缺少 token, IP=%s", ip) ctx.JSON(http.StatusUnauthorized, gin.H{"error": "缺少 token"}) return } machineID := ctx.Query("machine_id") + logger.Infof("[AgentWS] Token: %s..., MachineID: %s...", token[:8], machineID[:16]) + isNewAgent := false // 先尝试用 token 查找已有 Agent agent := c.agentService.GetByToken(token) + logger.Infof("[AgentWS] GetByToken 结果: agent=%v", agent != nil) // 如果没找到,尝试用令牌注册(会检查 machine_id 是否已存在) if agent == nil { + logger.Infof("[AgentWS] 尝试注册新 Agent") var err error agent, isNewAgent, err = c.agentService.RegisterByToken(token, machineID, ip) if err != nil { c.wsManager.RecordConnectFail(ip) + logger.Warnf("[AgentWS] 注册失败: %v, IP=%s, token=%s", err, ip, token[:8]+"...") ctx.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) return } + logger.Infof("[AgentWS] 注册成功: Agent #%d, isNew=%v", agent.ID, isNewAgent) } if !agent.Enabled { c.wsManager.RecordConnectFail(ip) + logger.Warnf("[AgentWS] Agent #%d 已禁用, IP=%s", agent.ID, ip) ctx.JSON(http.StatusForbidden, gin.H{"error": "Agent 已禁用"}) return } + logger.Infof("[AgentWS] 准备升级连接: Agent #%d, IP=%s", agent.ID, ip) conn, err := agentUpgrader.Upgrade(ctx.Writer, ctx.Request, nil) if err != nil { - logger.Errorf("[AgentWS] 升级连接失败: %v", err) + logger.Errorf("[AgentWS] 升级连接失败: %v, Agent #%d, IP=%s", err, agent.ID, ip) return } @@ -399,6 +419,8 @@ func (c *AgentController) WSConnect(ctx *gin.Context) { "machine_id": machineID, }) + logger.Infof("[AgentWS] Agent #%d 连接成功", agent.ID) + // 启动读写协程 go c.wsWritePump(ac) go c.wsReadPump(ac, agent) diff --git a/internal/router/router.go b/internal/router/router.go index 4a36f45..2cc8f01 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -217,16 +217,16 @@ func Setup(c *Controllers) *gin.Engine { agents.DELETE("/tokens/:id", c.Agent.DeleteToken) } } + } - // Agent API(供远程 Agent 调用) - agentAPI := api.Group("/agent") - { - agentAPI.POST("/heartbeat", c.Agent.Heartbeat) - agentAPI.GET("/tasks", c.Agent.GetTasks) - agentAPI.POST("/report", c.Agent.ReportResult) - agentAPI.GET("/download", c.Agent.Download) - agentAPI.GET("/ws", c.Agent.WSConnect) // WebSocket 连接 - } + // Agent API(供远程 Agent 调用,不使用 /v1 版本号) + agentAPI := root.Group("/api/agent") + { + agentAPI.POST("/heartbeat", c.Agent.Heartbeat) + agentAPI.GET("/tasks", c.Agent.GetTasks) + agentAPI.POST("/report", c.Agent.ReportResult) + agentAPI.GET("/download", c.Agent.Download) + agentAPI.GET("/ws", c.Agent.WSConnect) // WebSocket 连接 } // SPA fallback - serve index.html (no cache for HTML) @@ -237,19 +237,19 @@ func Setup(c *Controllers) *gin.Engine { ctx.Status(404) return } - + data, err := static.ReadFile("index.html") if err != nil { ctx.String(500, "index.html not found") return } - + html := string(data) - + // 注入配置变量供前端使用(API 调用和路由) configScript := `` html = strings.Replace(html, "", configScript+"", 1) - + ctx.Header("Cache-Control", "no-cache, no-store, must-revalidate") ctx.Data(200, "text/html; charset=utf-8", []byte(html)) }) From 7fad8d3536024b81696b9fba0d9aaead4163d73f Mon Sep 17 00:00:00 2001 From: engigu Date: Tue, 13 Jan 2026 22:36:37 +0800 Subject: [PATCH 02/34] Fix URL example for reverse proxy deployment --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 176d759..0ad9f32 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ docker run -d \ > ```bash > -e BH_SERVER_URL_PREFIX=/baihu > ``` -> 配置后访问地址为 `http://your-domain.com/baihu`,详见下方「URL 前缀配置」说明。 +> 配置后访问地址为 `http://your-domain.com/baihu/`,详见下方「URL 前缀配置」说明。 **Docker Compose(SQLite):** From b1d36f3c00a26198f675635a3c9695c794b6d767 Mon Sep 17 00:00:00 2001 From: engigu Date: Tue, 13 Jan 2026 22:42:00 +0800 Subject: [PATCH 03/34] fix: home chat render error --- web/src/views/dashboard/Dashboard.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/src/views/dashboard/Dashboard.vue b/web/src/views/dashboard/Dashboard.vue index 666c746..6fa6395 100644 --- a/web/src/views/dashboard/Dashboard.vue +++ b/web/src/views/dashboard/Dashboard.vue @@ -464,8 +464,8 @@ onUnmounted(() => { 执行统计 最近{{ chartDays }}天任务执行情况 - -
+ +
加载中...
@@ -477,8 +477,8 @@ onUnmounted(() => { 任务占比 最近{{ chartDays }}天任务执行分布 - -
+ +
加载中...
From 91883975e5e63d5295b90655d97901bf56619e34 Mon Sep 17 00:00:00 2001 From: engigu Date: Tue, 13 Jan 2026 22:52:14 +0800 Subject: [PATCH 04/34] fix: home chat render error --- web/src/views/dashboard/Dashboard.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/views/dashboard/Dashboard.vue b/web/src/views/dashboard/Dashboard.vue index 6fa6395..672a62d 100644 --- a/web/src/views/dashboard/Dashboard.vue +++ b/web/src/views/dashboard/Dashboard.vue @@ -193,7 +193,8 @@ const renderLineChart = () => { categories: sendStats.value.map(item => item.day.slice(5)), axisBorder: { show: false }, axisTicks: { show: false }, - labels: { style: { colors: textColor, fontSize: '12px', fontFamily: 'Inter, sans-serif' } } + labels: { style: { colors: textColor, fontSize: '12px', fontFamily: 'Inter, sans-serif' } }, + tooltip: { enabled: false } }, yaxis: { labels: { From 115010a3eed05f061271c3a44a258bd0d3ea8ed8 Mon Sep 17 00:00:00 2001 From: engigu Date: Wed, 14 Jan 2026 08:25:51 +0800 Subject: [PATCH 05/34] Add url_prefix to server configuration Add url_prefix configuration option to server settings --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0ad9f32..73ab024 100644 --- a/README.md +++ b/README.md @@ -518,6 +518,7 @@ nginx -t && nginx -s reload [server] port = 8052 host = 0.0.0.0 +url_prefix = [database] type = sqlite From 0208a21dd9876db75382c10d74bada8b80e1394d Mon Sep 17 00:00:00 2001 From: engigu Date: Thu, 15 Jan 2026 19:18:50 +0800 Subject: [PATCH 06/34] fix: task remove env error --- README.md | 1 - web/src/views/tasks/TaskDialog.vue | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4700a4f..cfadb99 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # 白虎面板 [![Hits](https://hits.sh/github.com/engigu/baihu-panel.svg?view=today-total)](https://hits.sh/github.com/engigu/baihu-panel/) -[![Image Tags](https://ghcr-badge.egpl.dev/engigu/baihu/tags?color=%2344cc11&ignore=latest%2Cmain*%2Cdev&n=3&label=docker+tags&trim=)](https://ghcr-badge.egpl.dev/engigu/baihu/tags) ![Latest Version](https://ghcr-badge.egpl.dev/engigu/baihu/latest_tag?color=%2344cc11&ignore=latest%2Cmain*&label=docker+version&trim=) ![Image Size](https://ghcr-badge.egpl.dev/engigu/baihu/size?color=%2344cc11&tag=latest&label=docker+image&trim=) ![Image pulls](https://img.shields.io/badge/dynamic/json?url=https://ghcr-badge.elias.eu.org/api/engigu/baihu-panel/baihu&query=downloadCount&style=flat&label=docker%20pulls&color=44cc11) diff --git a/web/src/views/tasks/TaskDialog.vue b/web/src/views/tasks/TaskDialog.vue index 790ad5e..177ae89 100644 --- a/web/src/views/tasks/TaskDialog.vue +++ b/web/src/views/tasks/TaskDialog.vue @@ -132,8 +132,7 @@ function addEnv(id: number) { } function removeEnv(id: number) { - const idx = selectedEnvIds.value.indexOf(id) - if (idx !== -1) selectedEnvIds.value.splice(idx, 1) + selectedEnvIds.value = selectedEnvIds.value.filter(envId => envId !== id) } async function save() { @@ -266,10 +265,10 @@ async function save() {
- - {{ env.name }} - - +
+ {{ env.name }} + +
From 77283c1e885cc7e96ebf4a056853887a0efa6a90 Mon Sep 17 00:00:00 2001 From: engigu Date: Thu, 15 Jan 2026 19:22:33 +0800 Subject: [PATCH 07/34] fix: task remove env error --- web/src/views/tasks/TaskDialog.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/views/tasks/TaskDialog.vue b/web/src/views/tasks/TaskDialog.vue index 177ae89..974e6df 100644 --- a/web/src/views/tasks/TaskDialog.vue +++ b/web/src/views/tasks/TaskDialog.vue @@ -6,7 +6,6 @@ import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' -import { Badge } from '@/components/ui/badge' import DirTreeSelect from '@/components/DirTreeSelect.vue' import { Plus, ChevronDown, X } from 'lucide-vue-next' import { api, type Task, type EnvVar, type Agent } from '@/api' From 6bb7cd14bb36cd5d7bee6a3d3395a6f83bc39066 Mon Sep 17 00:00:00 2001 From: engigu Date: Fri, 16 Jan 2026 08:14:25 +0800 Subject: [PATCH 08/34] Encourage users to star the project Added a request for users to star the project. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cfadb99..9d6f460 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ ![baihu.gif](https://f.pz.al/pzal/2025/12/24/f0d171f9a686d.gif) +如果项目有用,请帮忙点个star。 + ## 特色 - **轻量级:** docker/compose部署,无需复杂配置,开箱即用 From 82a42fbfc17390bdab9b63053482a829a5e01994 Mon Sep 17 00:00:00 2001 From: engigu Date: Wed, 14 Jan 2026 10:17:19 +0800 Subject: [PATCH 09/34] fix: remote agent exec url_path_prefix error --- internal/router/router.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/router/router.go b/internal/router/router.go index 2cc8f01..26996b7 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -216,6 +216,12 @@ func Setup(c *Controllers) *gin.Engine { agents.POST("/tokens", c.Agent.CreateToken) agents.DELETE("/tokens/:id", c.Agent.DeleteToken) } + + // Agent API(供前端调用,保持在 v1 下) + agentAPIv1 := authorized.Group("/agent") + { + agentAPIv1.GET("/download", c.Agent.Download) + } } } @@ -225,8 +231,8 @@ func Setup(c *Controllers) *gin.Engine { agentAPI.POST("/heartbeat", c.Agent.Heartbeat) agentAPI.GET("/tasks", c.Agent.GetTasks) agentAPI.POST("/report", c.Agent.ReportResult) - agentAPI.GET("/download", c.Agent.Download) - agentAPI.GET("/ws", c.Agent.WSConnect) // WebSocket 连接 + agentAPI.GET("/download", c.Agent.Download) // 也在这里注册,兼容 Agent 调用 + agentAPI.GET("/ws", c.Agent.WSConnect) // WebSocket 连接 } // SPA fallback - serve index.html (no cache for HTML) From 42cf3f6a971753500135ae8c0fe8b1823b996d8c Mon Sep 17 00:00:00 2001 From: engigu Date: Wed, 14 Jan 2026 10:29:17 +0800 Subject: [PATCH 10/34] feat: fix docs --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 49cd8ef..c3cfb94 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -139,3 +139,4 @@ jobs: with: tag_name: ${{ github.ref_name }} body_path: CHANGELOG.md + append_body: true # 追加模式:保留已有说明,在后面添加 CHANGELOG.md From b7dc53b91ef2a59fff6db9b091133f2ccd83fc67 Mon Sep 17 00:00:00 2001 From: engigu Date: Wed, 14 Jan 2026 14:37:26 +0800 Subject: [PATCH 11/34] fix: online editer eol error --- web/src/views/editor/Editor.vue | 5 ++++- web/src/views/scripts/Scripts.vue | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/web/src/views/editor/Editor.vue b/web/src/views/editor/Editor.vue index a895f9d..479c385 100644 --- a/web/src/views/editor/Editor.vue +++ b/web/src/views/editor/Editor.vue @@ -411,7 +411,10 @@ onUnmounted(() => { tabSize: 4, insertSpaces: true, readOnly: !isEditMode, - domReadOnly: !isEditMode + domReadOnly: !isEditMode, + files: { + eol: '\n' + } }" />
diff --git a/web/src/views/scripts/Scripts.vue b/web/src/views/scripts/Scripts.vue index e760971..1a4034a 100644 --- a/web/src/views/scripts/Scripts.vue +++ b/web/src/views/scripts/Scripts.vue @@ -308,7 +308,10 @@ onMounted(loadTree) scrollBeyondLastLine: false, automaticLayout: true, tabSize: 2, - wordWrap: 'on' + wordWrap: 'on', + files: { + eol: '\n' + } }" style="height: 100%" /> From 31a8b44f0ff96718c53cd866545e28de8794aae5 Mon Sep 17 00:00:00 2001 From: engigu Date: Wed, 14 Jan 2026 14:54:01 +0800 Subject: [PATCH 12/34] feat: fix docs --- web/src/views/editor/Editor.vue | 21 ++++++++++++++++----- web/src/views/scripts/Scripts.vue | 21 ++++++++++++++++----- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/web/src/views/editor/Editor.vue b/web/src/views/editor/Editor.vue index 479c385..704bc2a 100644 --- a/web/src/views/editor/Editor.vue +++ b/web/src/views/editor/Editor.vue @@ -1,5 +1,5 @@