refactor(file,shell,process): 去 gin 化——标准库 http.HandlerFunc + webhttp.Router 适配层
- 新增 webhttp 包: Router 最小路由接口(5 动词) + ServeMuxRouter 适配器 + WriteJSON 统一响应 - file/shell/process 三个被控能力包改以标准库 handler 形态注册, 不再绑死 gin - 由此 gin 及其连带重依赖(quic-go/http3、mongo bson、ugorji codec、goccy、validator)退出被控端二进制 - shell: recDir 死参数修复——原实现硬编码 ./rec, 传入的录制目录不生效; 现录制写入与记录列表同源 - shell: 终端创建失败由 panic 改为记日志后关连接(net/http 按连接兜底恢复, 不再依赖框架 Recovery) - file: 上传落盘不再沿用 SaveUploadedFile 的 MkdirAll+Chmod(Windows 盘根 Chmod 报参数不正确), 保留 MkdirAll 去掉 Chmod - 消费方: remote-client 纯 ServeMux 承载; remote-service 保留 gin 栈经 gin.WrapH 桥接(middle 链照常生效) 破坏性变更: RegisterRouters* 形参 gin.RouterGroup → webhttp.Router, 升级 tag v0.5.0
This commit is contained in:
+79
-77
@@ -5,14 +5,16 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shirou/gopsutil/v3/process"
|
||||
|
||||
"git.zeroonesoft.cn/golib/remote-core/webhttp"
|
||||
)
|
||||
|
||||
// ProcItem 进程列表行。
|
||||
@@ -30,89 +32,89 @@ type ProcItem struct {
|
||||
}
|
||||
|
||||
// RegisterRoutersProcess 装配进程管理路由: GET /process/list, POST /process/kill。
|
||||
func RegisterRoutersProcess(r *gin.RouterGroup) {
|
||||
r.GET("/process/list", listProcesses())
|
||||
r.POST("/process/kill", killProcess())
|
||||
func RegisterRoutersProcess(r webhttp.Router) {
|
||||
r.GET("/process/list", listProcesses)
|
||||
r.POST("/process/kill", killProcess)
|
||||
}
|
||||
|
||||
func fail(w http.ResponseWriter, msg string) {
|
||||
webhttp.WriteJSON(w, http.StatusOK, map[string]any{"code": 1, "msg": msg, "data": nil})
|
||||
}
|
||||
|
||||
// listProcesses 全量进程列表(按内存占用降序)。
|
||||
func listProcesses() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
procs, err := process.Processes()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1, "msg": "枚举进程失败: " + err.Error(), "data": nil})
|
||||
return
|
||||
}
|
||||
items := make([]ProcItem, 0, len(procs))
|
||||
for _, p := range procs {
|
||||
item := ProcItem{Pid: p.Pid}
|
||||
if name, e := p.Name(); e == nil {
|
||||
item.Name = name
|
||||
}
|
||||
if st, e := p.Status(); e == nil && len(st) > 0 {
|
||||
item.Status = st[0]
|
||||
}
|
||||
if u, e := p.Username(); e == nil {
|
||||
item.User = u
|
||||
}
|
||||
if cpu, e := p.CPUPercent(); e == nil {
|
||||
item.CPUPercent = cpu
|
||||
}
|
||||
if mem, e := p.MemoryInfo(); e == nil {
|
||||
item.MemRSS = mem.RSS
|
||||
}
|
||||
if mp, e := p.MemoryPercent(); e == nil {
|
||||
item.MemPercent = mp
|
||||
}
|
||||
if ct, e := p.CreateTime(); e == nil {
|
||||
item.CreateTime = ct
|
||||
}
|
||||
if exe, e := p.Exe(); e == nil {
|
||||
item.Exe = exe
|
||||
}
|
||||
if cmd, e := p.Cmdline(); e == nil {
|
||||
item.Cmdline = cmd
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool { return items[i].MemPercent > items[j].MemPercent })
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "OK", "data": gin.H{"list": items}})
|
||||
func listProcesses(w http.ResponseWriter, _ *http.Request) {
|
||||
procs, err := process.Processes()
|
||||
if err != nil {
|
||||
fail(w, "枚举进程失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
items := make([]ProcItem, 0, len(procs))
|
||||
for _, p := range procs {
|
||||
item := ProcItem{Pid: p.Pid}
|
||||
if name, e := p.Name(); e == nil {
|
||||
item.Name = name
|
||||
}
|
||||
if st, e := p.Status(); e == nil && len(st) > 0 {
|
||||
item.Status = st[0]
|
||||
}
|
||||
if u, e := p.Username(); e == nil {
|
||||
item.User = u
|
||||
}
|
||||
if cpu, e := p.CPUPercent(); e == nil {
|
||||
item.CPUPercent = cpu
|
||||
}
|
||||
if mem, e := p.MemoryInfo(); e == nil {
|
||||
item.MemRSS = mem.RSS
|
||||
}
|
||||
if mp, e := p.MemoryPercent(); e == nil {
|
||||
item.MemPercent = mp
|
||||
}
|
||||
if ct, e := p.CreateTime(); e == nil {
|
||||
item.CreateTime = ct
|
||||
}
|
||||
if exe, e := p.Exe(); e == nil {
|
||||
item.Exe = exe
|
||||
}
|
||||
if cmd, e := p.Cmdline(); e == nil {
|
||||
item.Cmdline = cmd
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool { return items[i].MemPercent > items[j].MemPercent })
|
||||
webhttp.WriteJSON(w, http.StatusOK, map[string]any{"code": 0, "msg": "OK", "data": map[string]any{"list": items}})
|
||||
}
|
||||
|
||||
// killProcess 结束进程: {pid: 必填, name: 可选校验(防 pid 复用误杀)}。
|
||||
// Windows 上 gopsutil 权限不足时回退 taskkill /F /T(连子进程树强杀)。
|
||||
func killProcess() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var req struct {
|
||||
Pid int32 `json:"pid"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil || req.Pid <= 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1, "msg": "参数错误: pid 必填且为正整数", "data": nil})
|
||||
return
|
||||
}
|
||||
p, err := process.NewProcess(req.Pid)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1, "msg": "进程不存在: " + err.Error(), "data": nil})
|
||||
return
|
||||
}
|
||||
if req.Name != "" {
|
||||
if name, e := p.Name(); e == nil && name != "" && name != req.Name {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1, "msg": "进程名不匹配(实际 " + name + "), 已拒绝结束", "data": nil})
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := p.Kill(); err != nil {
|
||||
if runtime.GOOS != "windows" {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1, "msg": "结束进程失败: " + err.Error(), "data": nil})
|
||||
return
|
||||
}
|
||||
if out, e := exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(int(req.Pid))).CombinedOutput(); e != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1, "msg": "结束进程失败: " + err.Error() + "; taskkill: " + string(out), "data": nil})
|
||||
return
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "OK", "data": gin.H{"pid": req.Pid}})
|
||||
func killProcess(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Pid int32 `json:"pid"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Pid <= 0 {
|
||||
fail(w, "参数错误: pid 必填且为正整数")
|
||||
return
|
||||
}
|
||||
p, err := process.NewProcess(req.Pid)
|
||||
if err != nil {
|
||||
fail(w, "进程不存在: "+err.Error())
|
||||
return
|
||||
}
|
||||
if req.Name != "" {
|
||||
if name, e := p.Name(); e == nil && name != "" && name != req.Name {
|
||||
fail(w, "进程名不匹配(实际 "+name+"), 已拒绝结束")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := p.Kill(); err != nil {
|
||||
if runtime.GOOS != "windows" {
|
||||
fail(w, "结束进程失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
if out, e := exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(int(req.Pid))).CombinedOutput(); e != nil {
|
||||
fail(w, "结束进程失败: "+err.Error()+"; taskkill: "+string(out))
|
||||
return
|
||||
}
|
||||
}
|
||||
webhttp.WriteJSON(w, http.StatusOK, map[string]any{"code": 0, "msg": "OK", "data": map[string]any{"pid": req.Pid}})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user