From d34ed5842a5a95f6b43a62dbd40665886a3f045b Mon Sep 17 00:00:00 2001 From: shirainbown Date: Wed, 15 Apr 2026 01:39:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20SSH=20=E6=88=90=E5=8A=9F=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E7=B3=BB=E7=BB=9F=E4=BF=A1=E6=81=AF=E6=97=B6=E8=A7=86?= =?UTF-8?q?=E4=B8=BA=E6=9C=BA=E5=99=A8=E5=9C=A8=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在连通性检测逻辑中,当 network ping / ICMP / TCP 三层探测均失败后, 若机器配置了 SSH 凭据,则同步尝试调用 GetSSHInfo 进行 SSH 信息采集: - 若 SSH 采集成功,则将该机器判定为在线,并保存获取到的系统信息; - 若 SSH 采集也失败,则保持离线状态,按原逻辑记录离线日志。 此改进可避免以下场景导致的误判: - 机器禁 ping 或 ICMP 被防火墙拦截; - SSH 端口未被网络层探测正确识别; - 只要 SSH 服务正常且能正确登录并获取系统信息,即认为机器处于可用状态。 其他改动: - 提取 saveSSHResult 辅助函数,统一保存 SSH 采集结果,避免重复代码。 - 同步更新了本地与 Linux 部署二进制文件。 --- server/services/ping.go | 80 +++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/server/services/ping.go b/server/services/ping.go index d768eb0..c16c88b 100644 --- a/server/services/ping.go +++ b/server/services/ping.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "lan-manager/server/db" + "lan-manager/server/models" "lan-manager/server/utils" "net" "os" @@ -146,9 +147,43 @@ type machinePing struct { wasOnline bool } +func saveSSHResult(mid int64, mip string, mport int, result *models.SSHInfoResult) { + portsStr := "" + if len(result.ListenPorts) > 0 { + b, _ := json.Marshal(result.ListenPorts) + portsStr = string(b) + if len(portsStr) > 2 { + portsStr = portsStr[1 : len(portsStr)-1] + } + } + _, dbErr := db.DB.Exec(`UPDATE machines SET cpu_info=?, memory_info=?, disk_info=?, uptime=?, listen_ports=?, ssh_synced_at=CURRENT_TIMESTAMP, updated_at=CURRENT_TIMESTAMP WHERE id=?`, + result.RawCPUInfo, result.RawMemoryInfo, result.RawDiskInfo, result.Uptime, portsStr, mid) + if dbErr != nil { + fmt.Printf("[SSH] auto-fetch db error for %s:%d: %v\n", mip, mport, dbErr) + } else { + fmt.Printf("[SSH] auto-fetch success for %s:%d\n", mip, mport) + } +} + func handlePingResult(m *machinePing, res PingResult) { online := res.Online reason := res.Reason + var sshResult *models.SSHInfoResult + + // 如果网络层检测失败,尝试通过 SSH 获取系统信息来判定在线 + if !online && m.sshUsername != "" && m.sshPassword != "" { + plainPass, decryptErr := utils.Decrypt(m.sshPassword) + if decryptErr == nil { + result, err := GetSSHInfo(m.ip, m.sshPort, m.sshUsername, plainPass) + if err == nil { + online = true + reason = "" + sshResult = result + fmt.Printf("[Ping] %s -> network unreachable, but SSH info fetched successfully, treating as online\n", m.ip) + } + } + } + var onlineInt int if online { onlineInt = 1 @@ -185,6 +220,9 @@ func handlePingResult(m *machinePing, res PingResult) { _, _ = db.DB.Exec(`UPDATE machines SET total_offline_seconds=total_offline_seconds+? WHERE id=?`, duration, m.id) } m.wasOnline = true + if sshResult != nil { + saveSSHResult(m.id, m.ip, m.sshPort, sshResult) + } return } @@ -197,33 +235,23 @@ func handlePingResult(m *machinePing, res PingResult) { } if online && m.sshUsername != "" && m.sshPassword != "" { - go func(mid int64, mip string, mport int, muser, mpass string) { - plainPass, decryptErr := utils.Decrypt(mpass) - if decryptErr != nil { - fmt.Printf("[SSH] decrypt failed for %s:%d: %v\n", mip, mport, decryptErr) - return - } - result, err := GetSSHInfo(mip, mport, muser, plainPass) - if err != nil { - fmt.Printf("[SSH] auto-fetch failed for %s:%d: %v\n", mip, mport, err) - return - } - portsStr := "" - if len(result.ListenPorts) > 0 { - b, _ := json.Marshal(result.ListenPorts) - portsStr = string(b) - if len(portsStr) > 2 { - portsStr = portsStr[1 : len(portsStr)-1] + if sshResult != nil { + saveSSHResult(m.id, m.ip, m.sshPort, sshResult) + } else { + go func(mid int64, mip string, mport int, muser, mpass string) { + plainPass, decryptErr := utils.Decrypt(mpass) + if decryptErr != nil { + fmt.Printf("[SSH] decrypt failed for %s:%d: %v\n", mip, mport, decryptErr) + return } - } - _, dbErr := db.DB.Exec(`UPDATE machines SET cpu_info=?, memory_info=?, disk_info=?, uptime=?, listen_ports=?, ssh_synced_at=CURRENT_TIMESTAMP, updated_at=CURRENT_TIMESTAMP WHERE id=?`, - result.RawCPUInfo, result.RawMemoryInfo, result.RawDiskInfo, result.Uptime, portsStr, mid) - if dbErr != nil { - fmt.Printf("[SSH] auto-fetch db error for %s:%d: %v\n", mip, mport, dbErr) - } else { - fmt.Printf("[SSH] auto-fetch success for %s:%d\n", mip, mport) - } - }(m.id, m.ip, m.sshPort, m.sshUsername, m.sshPassword) + result, err := GetSSHInfo(mip, mport, muser, plainPass) + if err != nil { + fmt.Printf("[SSH] auto-fetch failed for %s:%d: %v\n", mip, mport, err) + return + } + saveSSHResult(mid, mip, mport, result) + }(m.id, m.ip, m.sshPort, m.sshUsername, m.sshPassword) + } } }