feat: SSH 成功获取系统信息时视为机器在线

在连通性检测逻辑中,当 network ping / ICMP / TCP 三层探测均失败后,
若机器配置了 SSH 凭据,则同步尝试调用 GetSSHInfo 进行 SSH 信息采集:
- 若 SSH 采集成功,则将该机器判定为在线,并保存获取到的系统信息;
- 若 SSH 采集也失败,则保持离线状态,按原逻辑记录离线日志。

此改进可避免以下场景导致的误判:
- 机器禁 ping 或 ICMP 被防火墙拦截;
- SSH 端口未被网络层探测正确识别;
- 只要 SSH 服务正常且能正确登录并获取系统信息,即认为机器处于可用状态。

其他改动:
- 提取 saveSSHResult 辅助函数,统一保存 SSH 采集结果,避免重复代码。
- 同步更新了本地与 Linux 部署二进制文件。
This commit is contained in:
shirainbown
2026-04-15 01:39:16 +08:00
parent 1a5f5ee3b5
commit d34ed5842a

View File

@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"lan-manager/server/db" "lan-manager/server/db"
"lan-manager/server/models"
"lan-manager/server/utils" "lan-manager/server/utils"
"net" "net"
"os" "os"
@@ -146,9 +147,43 @@ type machinePing struct {
wasOnline bool 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) { func handlePingResult(m *machinePing, res PingResult) {
online := res.Online online := res.Online
reason := res.Reason 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 var onlineInt int
if online { if online {
onlineInt = 1 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) _, _ = db.DB.Exec(`UPDATE machines SET total_offline_seconds=total_offline_seconds+? WHERE id=?`, duration, m.id)
} }
m.wasOnline = true m.wasOnline = true
if sshResult != nil {
saveSSHResult(m.id, m.ip, m.sshPort, sshResult)
}
return return
} }
@@ -197,33 +235,23 @@ func handlePingResult(m *machinePing, res PingResult) {
} }
if online && m.sshUsername != "" && m.sshPassword != "" { if online && m.sshUsername != "" && m.sshPassword != "" {
go func(mid int64, mip string, mport int, muser, mpass string) { if sshResult != nil {
plainPass, decryptErr := utils.Decrypt(mpass) saveSSHResult(m.id, m.ip, m.sshPort, sshResult)
if decryptErr != nil { } else {
fmt.Printf("[SSH] decrypt failed for %s:%d: %v\n", mip, mport, decryptErr) go func(mid int64, mip string, mport int, muser, mpass string) {
return plainPass, decryptErr := utils.Decrypt(mpass)
} if decryptErr != nil {
result, err := GetSSHInfo(mip, mport, muser, plainPass) fmt.Printf("[SSH] decrypt failed for %s:%d: %v\n", mip, mport, decryptErr)
if err != nil { return
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]
} }
} result, err := GetSSHInfo(mip, mport, muser, plainPass)
_, 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=?`, if err != nil {
result.RawCPUInfo, result.RawMemoryInfo, result.RawDiskInfo, result.Uptime, portsStr, mid) fmt.Printf("[SSH] auto-fetch failed for %s:%d: %v\n", mip, mport, err)
if dbErr != nil { return
fmt.Printf("[SSH] auto-fetch db error for %s:%d: %v\n", mip, mport, dbErr) }
} else { saveSSHResult(mid, mip, mport, result)
fmt.Printf("[SSH] auto-fetch success for %s:%d\n", mip, mport) }(m.id, m.ip, m.sshPort, m.sshUsername, m.sshPassword)
} }
}(m.id, m.ip, m.sshPort, m.sshUsername, m.sshPassword)
} }
} }