Private
Public Access
1
0

新增:SFTP直连+连接池+autoConnect+文件服务器端口自动回退

- SFTP模块:连接/断开/文件CRUD/系统信息采集/base64二进制写入
- 连接池:多服务器同时在线,瞬间切换profile
- autoConnect:启动时自动连接所有非本地服务器
- 端口自动回退:listenWithFallback消除TOCTOU,解决端口冲突崩溃
- 文件服务器URL集中管理:file-server.ts消除8+处硬编码端口
- Sidebar设置面板:添加服务器/自动连接/自动刷新开关
- 修复:validateFilePath越界panic、正则预编译
- 修复:注释准确性(RemoveAll/端口8073/动态端口文档)
This commit is contained in:
2026-05-04 15:33:19 +08:00
parent 6eaaa56eb6
commit 6bee55b96f
41 changed files with 2620 additions and 458 deletions

View File

@@ -46,8 +46,8 @@ func (h *Handler) HTMLPreviewProxy(c echo.Context) error {
}
theme := c.QueryParam("theme")
targetURL := fmt.Sprintf("http://localhost:8073/localfs/html-preview?path=%s&theme=%s",
url.QueryEscape(clean), url.QueryEscape(theme))
targetURL := fmt.Sprintf("%s/localfs/html-preview?path=%s&theme=%s",
h.cfg.FileServerAddr(), url.QueryEscape(clean), url.QueryEscape(theme))
resp, err := http.Get(targetURL)
if err != nil {

View File

@@ -1,6 +1,7 @@
package handler
import (
"fmt"
"net/http"
"os"
"runtime"
@@ -9,6 +10,9 @@ import (
"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 健康检查
@@ -111,3 +115,39 @@ func (h *Handler) Drives(c echo.Context) error {
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))
}