- SFTP模块:连接/断开/文件CRUD/系统信息采集/base64二进制写入 - 连接池:多服务器同时在线,瞬间切换profile - autoConnect:启动时自动连接所有非本地服务器 - 端口自动回退:listenWithFallback消除TOCTOU,解决端口冲突崩溃 - 文件服务器URL集中管理:file-server.ts消除8+处硬编码端口 - Sidebar设置面板:添加服务器/自动连接/自动刷新开关 - 修复:validateFilePath越界panic、正则预编译 - 修复:注释准确性(RemoveAll/端口8073/动态端口文档)
154 lines
3.7 KiB
Go
154 lines
3.7 KiB
Go
package handler
|
||
|
||
import (
|
||
"fmt"
|
||
"net/http"
|
||
"os"
|
||
"runtime"
|
||
"strings"
|
||
|
||
"u-desk/internal/agent/model"
|
||
|
||
"github.com/labstack/echo/v4"
|
||
"github.com/shirou/gopsutil/v3/cpu"
|
||
"github.com/shirou/gopsutil/v3/disk"
|
||
"github.com/shirou/gopsutil/v3/mem"
|
||
)
|
||
|
||
// Ping 健康检查
|
||
func (h *Handler) Ping(c echo.Context) error {
|
||
return c.JSON(http.StatusOK, model.OK(map[string]string{
|
||
"status": "ok",
|
||
}))
|
||
}
|
||
|
||
// Info 返回 Agent 信息
|
||
func (h *Handler) Info(c echo.Context) error {
|
||
hostname, _ := os.Hostname()
|
||
return c.JSON(http.StatusOK, model.OK(map[string]interface{}{
|
||
"version": "0.1.0",
|
||
"os": runtime.GOOS,
|
||
"arch": runtime.GOARCH,
|
||
"hostname": hostname,
|
||
}))
|
||
}
|
||
|
||
// CommonPaths 返回常用系统路径
|
||
func (h *Handler) CommonPaths(c echo.Context) error {
|
||
paths := map[string]string{}
|
||
|
||
home, _ := os.UserHomeDir()
|
||
if home != "" {
|
||
paths["home"] = home
|
||
paths["desktop"] = home + "/Desktop"
|
||
paths["documents"] = home + "/Documents"
|
||
paths["downloads"] = home + "/Downloads"
|
||
}
|
||
|
||
// 根据平台添加盘符/根路径
|
||
if runtime.GOOS == "windows" {
|
||
for _, drive := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
|
||
_, err := os.Stat(string(drive) + ":\\")
|
||
if err == nil {
|
||
paths["drive_"+strings.ToLower(string(drive))] = string(drive) + ":\\"
|
||
}
|
||
}
|
||
} else {
|
||
paths["root"] = "/"
|
||
_, err := os.Stat("/home")
|
||
if err == nil {
|
||
paths["users"] = "/home"
|
||
}
|
||
}
|
||
|
||
return c.JSON(http.StatusOK, model.OK(paths))
|
||
}
|
||
|
||
// Drives 返回可用磁盘列表
|
||
func (h *Handler) Drives(c echo.Context) error {
|
||
type DriveInfo struct {
|
||
Name string `json:"name"`
|
||
Path string `json:"path"`
|
||
FsType string `json:"fs_type,omitempty"`
|
||
Total uint64 `json:"total"`
|
||
Free uint64 `json:"free"`
|
||
}
|
||
|
||
var drives []DriveInfo
|
||
|
||
if runtime.GOOS == "windows" {
|
||
for _, drive := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
|
||
drivePath := string(drive) + ":\\"
|
||
if _, err := os.Stat(drivePath); err != nil {
|
||
continue
|
||
}
|
||
drives = append(drives, DriveInfo{
|
||
Name: strings.ToLower(string(drive)),
|
||
Path: drivePath,
|
||
Total: 0,
|
||
Free: 0,
|
||
})
|
||
}
|
||
} else {
|
||
parts, err := os.ReadDir("/")
|
||
if err == nil {
|
||
for _, p := range parts {
|
||
name := p.Name()
|
||
if len(name) == 2 && name[0] != '.' && name[1] != '.' && p.IsDir() {
|
||
// 可能是挂载点
|
||
fullPath := "/" + name
|
||
if stat, err := os.Stat(fullPath); err == nil && stat.IsDir() {
|
||
drives = append(drives, DriveInfo{
|
||
Name: name,
|
||
Path: fullPath,
|
||
})
|
||
_ = stat
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// 至少返回根目录
|
||
if len(drives) == 0 {
|
||
drives = append(drives, DriveInfo{Name: "/", Path: "/"})
|
||
}
|
||
}
|
||
|
||
return c.JSON(http.StatusOK, model.OK(drives))
|
||
}
|
||
|
||
// Stats 返回系统资源使用统计(CPU/内存/磁盘)
|
||
func (h *Handler) Stats(c echo.Context) error {
|
||
info := make(map[string]interface{})
|
||
|
||
// CPU
|
||
if cores, err := cpu.Counts(true); err == nil {
|
||
info["cpu_cores"] = cores
|
||
}
|
||
if percents, err := cpu.Percent(0, false); err == nil && len(percents) > 0 {
|
||
info["cpu_usage"] = fmt.Sprintf("%.0f%%", percents[0])
|
||
}
|
||
|
||
// 内存
|
||
if memInfo, err := mem.VirtualMemory(); err == nil {
|
||
info["mem_total"] = memInfo.Total
|
||
info["mem_used"] = memInfo.Used
|
||
info["mem_usage"] = fmt.Sprintf("%.0f%%", memInfo.UsedPercent)
|
||
}
|
||
|
||
// 磁盘(取根分区)
|
||
if partitions, err := disk.Partitions(false); err == nil {
|
||
for _, p := range partitions {
|
||
if p.Mountpoint == "/" || (runtime.GOOS == "windows" && len(p.Mountpoint) == 3 && p.Mountpoint[1] == ':') {
|
||
if usage, err := disk.Usage(p.Mountpoint); err == nil {
|
||
info["disk_total"] = usage.Total
|
||
info["disk_used"] = usage.Used
|
||
info["disk_usage"] = fmt.Sprintf("%.0f%%", usage.UsedPercent)
|
||
}
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
return c.JSON(http.StatusOK, model.OK(info))
|
||
}
|