init: 自 zomaintain/backend/remote-core 平移独立成库; module path 改为 git.zeroonesoft.cn/golib/remote-core; 修复 ResizeDataType 未导出字段致 json tag 无效的遗留问题

This commit is contained in:
w11
2026-09-19 21:14:49 +08:00
commit 4936f5baf0
8 changed files with 912 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
bin/
.idea/
+17
View File
@@ -0,0 +1,17 @@
// 通用小工具(自 remote-backend-server internal/libs/common 裁剪)。
package common
import "os"
// PathExists 路径是否存在。
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
+398
View File
@@ -0,0 +1,398 @@
package file
// 移植自 remote-backend-server internal/handlers/file(去 DB 依赖)。
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jaypipes/ghw"
"github.com/shirou/gopsutil/v3/disk"
"net/http"
"os"
"path/filepath"
"git.zeroonesoft.cn/golib/remote-core/common"
"strconv"
)
// RegisterRoutersGatewayFile 注册路由
func RegisterRoutersGatewayFile(r *gin.RouterGroup) {
r.GET("/file/list", getGatewayFileList())
r.POST("/file/list", postGatewayFileList())
r.DELETE("/file", deleteGatewayFile())
r.PUT("/file/name", putGatewayFileName())
r.GET("/file/download", getGatewayFileDownload())
r.HEAD("/file/download", headGatewayFileDownload())
r.POST("/file/upload", postGatewayFileUpload())
r.POST("/file/dir", postGatewayFileDir())
}
type PathData struct {
Path string `json:"path"`
}
// 传递文件路径不方便 比如 路径中包含 #
func getGatewayFileList() gin.HandlerFunc {
return func(c *gin.Context) {
dir := c.Query("path")
data, err := getFileList(dir)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + err.Error(),
"data": "OK",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "操作成功",
"data": gin.H{
"list": data,
"count": len(data),
"path": dir,
},
})
}
}
func deleteGatewayFile() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Query("path")
ok, err := common.PathExists(path)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + err.Error(),
"data": "OK",
})
return
}
if !ok {
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + "文件不存在",
"data": "OK",
})
return
}
}
err = os.Remove(path)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + err.Error(),
"data": "OK",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "操作成功",
"data": nil,
})
}
}
func putGatewayFileName() gin.HandlerFunc {
return func(c *gin.Context) {
oldPath := c.Query("old")
newFile := c.Query("new")
fileInfo, err := os.Stat(oldPath)
if err != nil {
if os.IsNotExist(err) {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + "文件不存在",
"data": "OK",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + err.Error(),
"data": "OK",
})
return
}
dir := filepath.Dir(oldPath)
newPath := filepath.Join(dir, newFile)
fmt.Println(fileInfo)
fmt.Println(oldPath, newPath)
err = os.Rename(oldPath, newPath)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + err.Error(),
"data": "OK",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "操作成功",
"data": nil,
})
}
}
func postGatewayFileDir() gin.HandlerFunc {
return func(c *gin.Context) {
dir := c.Query("dir")
ok, err := common.PathExists(dir)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + err.Error(),
"data": "OK",
})
return
}
if ok {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + "文件不存在",
"data": "OK",
})
return
}
err = os.MkdirAll(dir, 0750)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + err.Error(),
"data": "OK",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "操作成功",
"data": nil,
})
}
}
func postGatewayFileList() gin.HandlerFunc {
return func(c *gin.Context) {
dir := PathData{}
err := c.BindJSON(&dir)
fmt.Println("dir", dir)
data, err := getFileList(dir.Path)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "error:" + err.Error(),
"data": "OK",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "操作成功",
"data": gin.H{
"list": data,
"count": len(data),
"path": dir.Path,
},
})
}
}
type FileInfo struct {
Name string `json:"name"`
Path string `json:"path"`
Size int64 `json:"size"`
Mode string `json:"mode"`
ModTime string `json:"modTime"`
IsDir bool `json:"isDir"`
IsDrive bool `json:"isDrive"`
Used int64 `json:"used"`
DriveType string `json:"driveType"`
}
func getFileList(dir string) ([]FileInfo, error) {
list := make([]FileInfo, 0)
/*filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
list = append(list, FileInfo{
Name: info.Name(),
Size: info.Size(),
Mode: info.Mode().String(),
ModTime: time.Time{},
IsDir: info.IsDir(),
})
fmt.Println(info.Mode().Type())
return nil
})*/
if dir == "" {
partitions, err := disk.Partitions(false)
//fmt.Printf("disk partitions:%v\n\n", partitions)
if err != nil {
// return nil, err
}
block, err := ghw.Block()
for i, partition := range partitions {
usage, err := disk.Usage(partitions[i].Mountpoint)
if err != nil {
break
}
label, err := getVolumeName(partition.Device)
name := label
if label != partition.Device {
name = name + " (" + partition.Device + ")"
}
driveType := ""
for _, d := range block.Disks {
// fmt.Printf(" %v\n", disk.StorageController)
for _, part := range d.Partitions {
if part.Name == partition.Device {
driveType = d.DriveType.String()
break
}
}
}
list = append(list, FileInfo{
Name: name,
Path: partition.Mountpoint + "/",
Size: int64(usage.Total),
Used: int64(usage.Used),
Mode: "",
ModTime: "",
IsDir: false,
IsDrive: true,
DriveType: driveType,
})
}
return list, nil
}
f, err := os.Open(dir)
if err != nil {
fmt.Println(err)
}
files, err := f.Readdir(-1)
f.Close()
if err != nil {
fmt.Println(err)
}
for _, file := range files {
list = append(list, FileInfo{
Name: file.Name(),
Path: filepath.Join(dir, file.Name()),
Size: file.Size(),
Mode: file.Mode().String(),
ModTime: file.ModTime().Format("2006-01-02 15:04:05"),
IsDir: file.IsDir(),
})
}
return list, nil
}
func getGatewayFileDownload() gin.HandlerFunc {
return func(c *gin.Context) {
filePath := c.Query("filename")
fileName := filepath.Base(filePath)
c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Disposition", "attachment; filename="+fileName)
c.Header("Content-Transfer-Encoding", "binary")
fi, err := os.Stat(filePath)
if err == nil {
c.Header("Content-Length", strconv.FormatInt(fi.Size(), 10))
}
c.File(filePath)
}
}
func headGatewayFileDownload() gin.HandlerFunc {
return func(c *gin.Context) {
filePath := c.Query("filename")
fileName := filepath.Base(filePath)
c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Disposition", "attachment; filename="+fileName)
c.Header("Content-Transfer-Encoding", "binary")
fmt.Println(filePath)
fi, err := os.Stat(filePath)
if err == nil {
fmt.Println("file size is ", fi.Size(), err)
}
c.Header("Content-Length", strconv.FormatInt(fi.Size(), 10))
}
}
type Win32_Volume struct {
Name string
Label string
}
type FileData struct {
OriginalFileName string `json:"originalFileName"`
FileName string `json:"fileName"`
FilePath string `json:"filePath"`
Size int64 `json:"size"`
Md5 string `json:"md5"`
}
func postGatewayFileUpload() gin.HandlerFunc {
return func(c *gin.Context) {
dir := c.Query("dir")
file, err := HttpUploadFile(c, dir)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "上传失败:" + err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "success",
"status": "done",
"file": file,
})
}
}
func HttpUploadFile(c *gin.Context, saveDir string) (*FileData, error) {
file, err := c.FormFile("file")
if err != nil {
return nil, err
}
fileName := file.Filename
// 保存路径 绝对路径
savePath := filepath.Join(saveDir, file.Filename)
err = c.SaveUploadedFile(file, savePath)
if err != nil {
return nil, err
}
fileData := &FileData{
OriginalFileName: file.Filename,
FileName: fileName,
FilePath: savePath,
Size: file.Size,
}
return fileData, nil
}
+29
View File
@@ -0,0 +1,29 @@
//go:build windows
package file
import (
"fmt"
"github.com/yusufpapurcu/wmi"
"strings"
)
func getVolumeName(device string) (string, error) {
var volumes []Win32_Volume
query := "SELECT Name, Label FROM Win32_Volume WHERE DriveType = 3" // DriveType = 3 表示磁盘分区
err := wmi.Query(query, &volumes)
if err != nil {
fmt.Println("Failed to query Win32_Volume:", err)
return device, err
}
for _, volume := range volumes {
// fmt.Println("Name:", volume.Name, "Label:", volume.Label)
if strings.ToLower(volume.Name[:2]) == strings.ToLower(device[:2]) {
return volume.Label, nil
}
}
return device, nil
}
+52
View File
@@ -0,0 +1,52 @@
module git.zeroonesoft.cn/golib/remote-core
go 1.24.0
require (
github.com/gin-gonic/gin v1.10.0
github.com/gorilla/websocket v1.5.3
github.com/jaypipes/ghw v0.13.0
github.com/runletapp/go-console v0.0.0-20211204140000-27323a28410a
github.com/shirou/gopsutil/v3 v3.24.5
github.com/sirupsen/logrus v1.9.3
github.com/yusufpapurcu/wmi v1.2.4
)
require (
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/creack/pty v1.1.17 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/iamacarpet/go-winpty v1.0.2 // indirect
github.com/jaypipes/pcidb v1.0.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v1.0.0 // indirect
)
+129
View File
@@ -0,0 +1,129 @@
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/iamacarpet/go-winpty v1.0.2 h1:jwPVTYrjAHZx6Mcm6K5i9G4opMp5TblEHH5EQCl/Gzw=
github.com/iamacarpet/go-winpty v1.0.2/go.mod h1:/GHKJicG/EVRQIK1IQikMYBakBkhj/3hTjLgdzYsmpI=
github.com/jaypipes/ghw v0.13.0 h1:log8MXuB8hzTNnSktqpXMHc0c/2k/WgjOMSUtnI1RV4=
github.com/jaypipes/ghw v0.13.0/go.mod h1:In8SsaDqlb1oTyrbmTC14uy+fbBMvp+xdqX51MidlD8=
github.com/jaypipes/pcidb v1.0.1 h1:WB2zh27T3nwg8AE8ei81sNRb9yWBii3JGNJtT7K9Oic=
github.com/jaypipes/pcidb v1.0.1/go.mod h1:6xYUz/yYEyOkIkUt2t2J2folIuZ4Yg6uByCGFXMCeE4=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/runletapp/go-console v0.0.0-20211204140000-27323a28410a h1:1hh8CSomjZSJPk7AgHV8o33Su13bZby81PrC6pIvJqQ=
github.com/runletapp/go-console v0.0.0-20211204140000-27323a28410a/go.mod h1:9Y3jw1valnPKqsYSsBWxQNAuxqNSBuwd2ZEeElxgNUI=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+67
View File
@@ -0,0 +1,67 @@
package shell
import (
"encoding/json"
"io"
"sync"
"time"
)
type RecType string
const (
InputType RecType = "i"
OutPutType RecType = "o"
)
type RecHeader struct {
Version int `json:"version"`
Width int `json:"width"`
Height int `json:"height"`
Timestamp int64 `json:"timestamp"`
Env struct {
Shell string `json:"SHELL"`
Term string `json:"TERM"`
} `json:"env"`
}
func defaultRecHeader() *RecHeader {
recHeader := new(RecHeader)
recHeader.Version = 2
recHeader.Env.Shell = "/bin/bash"
recHeader.Env.Term = "xterm-256color"
return recHeader
}
type Recorder struct {
StartTime time.Time
Writer io.Writer
sync.Mutex
}
func NewRecorder(writer io.Writer) *Recorder {
return &Recorder{
StartTime: time.Now(),
Writer: writer,
}
}
func (rec *Recorder) WriteHeader(height, width int) {
header := defaultRecHeader()
header.Timestamp = rec.StartTime.Unix()
header.Height = height
header.Width = width
b, _ := json.Marshal(header)
rec.Writer.Write(b)
rec.Writer.Write([]byte("\r\n"))
}
func (rec *Recorder) WriteData(rectype RecType, data string) {
recData := make([]interface{}, 3)
recData[0] = float64(time.Since(rec.StartTime).Microseconds()) / float64(1000000)
recData[1] = rectype
recData[2] = data
b, _ := json.Marshal(recData)
rec.Writer.Write(b)
rec.Writer.Write([]byte("\r\n"))
}
+218
View File
@@ -0,0 +1,218 @@
package shell
// 移植自 remote-backend-server internal/handlers/shell(去 DB 依赖, ConPTY 终端 + asciinema 录制)。
import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/runletapp/go-console"
"github.com/sirupsen/logrus"
"io/ioutil"
"net/http"
"os"
"path"
"git.zeroonesoft.cn/golib/remote-core/common"
"runtime"
"strings"
"time"
)
type windowSize struct {
Rows uint16 `json:"rows"`
Cols uint16 `json:"cols"`
X uint16
Y uint16
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// 解决跨域问题
CheckOrigin: func(r *http.Request) bool {
return true
},
}
type OpDataType struct {
Op string `json:"op" form:"op"`
Data interface{} `json:"data" form:"data" `
}
type ResizeDataType struct {
Cols int `json:"cols"`
Rows int `json:"rows"`
}
// RegisterRoutersWebShell 注册路由
func RegisterRoutersWebShell(r *gin.RouterGroup, recDir string) {
r.GET("/web/shell", getWebShell(recDir))
r.GET("/web/shell/record", RecoderList)
}
func getWebShell(recDir string) gin.HandlerFunc {
return func(c *gin.Context) {
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
logrus.Errorf("Unable to upgrade connection")
return
}
proc, err := console.New(180, 30)
if err != nil {
panic(err)
}
arr := make([]string, 0)
if runtime.GOOS == "windows" {
if ok, _ := common.PathExists("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"); ok {
arr = append(arr, "powershell.exe") // powershell.exe cmd.exe
} else {
arr = append(arr, "cmd.exe")
}
} else {
arr = []string{"bash"} // powershell.exe cmd.exe
}
if err := proc.Start(arr); err != nil {
logrus.Errorf("启动出错:%s", err.Error())
panic(err)
}
if err != nil {
logrus.Errorf("Unable to start pty/cmd")
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
defer func() {
proc.Close()
conn.Close()
}()
var recorder *Recorder
os.MkdirAll("./rec", 0766)
fileName := path.Join("./rec/", fmt.Sprintf("%s.cast", time.Now().Format("20060102_150405")))
logrus.Debugf("记录文件名:%s", fileName)
f, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0766)
if err != nil {
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
logrus.Errorf("创建记录文件失败")
return
}
defer f.Close()
recorder = NewRecorder(f)
if recorder != nil {
recorder.Lock()
recorder.WriteHeader(30, 150)
recorder.Unlock()
}
go func() {
for {
buf := make([]byte, 0x10000)
read, err := proc.Read(buf)
if err != nil {
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
logrus.Errorf("读取错误:%s", err.Error())
return
}
conn.WriteMessage(websocket.BinaryMessage, buf[:read])
if recorder != nil {
recorder.Lock()
recorder.WriteData(InputType, string(buf[:read]))
recorder.Unlock()
}
}
}()
for {
messageType, reader, err := conn.NextReader()
if err != nil {
logrus.Errorf("Unable to grab next reader")
return
}
if messageType != websocket.TextMessage {
logrus.Errorf("无效的消息")
//conn.WriteMessage(websocket.TextMessage, []byte("Unexpected text message"))
//continue
}
buf := make([]byte, 0x10000)
n, err := reader.Read(buf)
if err != nil {
logrus.Errorf("Error after copying %d bytes", n)
}
if n > 0 {
opData := OpDataType{}
newBuf := buf[:n]
fmt.Println("收到数据", string(newBuf))
err = json.Unmarshal(newBuf, &opData)
if err != nil {
logrus.Errorf("解析错误:%s", err.Error())
}
if opData.Op == "stdin" {
value, ok := opData.Data.(string)
if ok {
n, err = proc.Write([]byte(value))
if err != nil {
logrus.Errorf("写放数据错误g %d bytes", n)
} else {
fmt.Println("写入成功")
}
} else {
}
} else if opData.Op == "resize" {
fmt.Println("resize")
value, ok := opData.Data.(ResizeDataType)
if ok {
err = proc.SetSize(value.Cols, value.Rows)
if err != nil {
logrus.Errorf("重置大小错误 %d bytes", n)
} else {
fmt.Println("重置大小成功")
}
} else {
}
}
}
}
}
}
func RecoderList(c *gin.Context) {
files, err := ioutil.ReadDir("./rec")
if err != nil {
c.AbortWithStatusJSON(200, gin.H{"ok": false, "msg": err.Error()})
return
}
var filesName []string
for _, f := range files {
if f.IsDir() {
continue
}
if strings.HasSuffix(f.Name(), ".cast") {
filesName = append(filesName, f.Name())
}
}
c.JSON(200, filesName)
}