diff --git a/go.mod b/go.mod index b324382..9403142 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 gorm.io/driver/mysql v1.5.6 gorm.io/driver/postgres v1.5.9 - gorm.io/gorm v1.25.10 + gorm.io/gorm v1.30.0 ) require ( @@ -39,6 +39,7 @@ require ( 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/mattn/go-sqlite3 v1.14.22 // 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 @@ -48,9 +49,10 @@ require ( 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/sync v0.1.0 // indirect + golang.org/x/sync v0.9.0 // indirect golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/text v0.20.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/sqlite v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 40ddc6a..831f1c2 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,8 @@ 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/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 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= @@ -115,12 +117,16 @@ 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/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= @@ -139,8 +145,12 @@ gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= +gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/handler/cid.go b/handler/cid.go index 2154dc5..4ca9df6 100644 --- a/handler/cid.go +++ b/handler/cid.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "github.com/gin-gonic/gin" + "net/http" "os/exec" "strconv" "strings" @@ -57,6 +58,27 @@ func SetUpCIDGroup(router *gin.Engine) { cidGroup.POST("/log/detail", GetCIDLog) //获取执行日志详情 cidGroup.GET("/callback", CIDCallback) cidGroup.POST("/callback", CIDCallback) + cidGroup.GET("/running", GetRunningCIDs) +} +func GetRunningCIDs(c *gin.Context) { + id, _ := c.Get("id") + user_id := int(id.(float64)) + req_type := c.Query("type") //请求方式 0默认自己, 1为所有(管理员可选) + req_type_ := 0 + if req_type != "" { + req_type_, _ = strconv.Atoi(req_type) + } + resp_data, err := service.GetCIDRunningList(user_id, req_type_) + + var resp proto.GeneralResp + if err != nil { + resp.Code = proto.InternalServerError + resp.Message = err.Error() + } else { + resp.Code, resp.Message = 0, "" + resp.Data = resp_data + } + c.JSON(http.StatusOK, resp) } func RunCID(c *gin.Context) { var req CIDRunReq @@ -72,13 +94,12 @@ func RunCID(c *gin.Context) { if err := c.ShouldBind(&req); err == nil { // 获取用户ID - username, _ := c.Get("username") cid := dao.FindCIDByID(req.ID, authID) if cid.ID == 0 { c.JSON(200, gin.H{"error": "CID not found", "code": proto.OperationFailed, "message": "failed"}) return } else { - go RunShell(username.(string), cid.Url, cid.Script, req.ID, authID) + go RunShellCID(cid.Name, cid.Url, cid.Script, req.ID, authID) c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": "success"}) } } else { @@ -206,8 +227,8 @@ func CIDCallback(c *gin.Context) { return } if res.ID != 0 { - user := dao.FindUserByID(res.Auth_id) - go RunShell(user[0].Name, res.Url, res.Script, int(res.ID), res.Auth_id) + user_info := dao.FindUserByID(res.Auth_id) + go RunShellCID(user_info[0].Name, res.Url, res.Script, int(res.ID), res.Auth_id) c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": "success"}) } else { c.JSON(200, gin.H{"error": "CID not found by id and token", "code": proto.OperationFailed, "message": "failed"}) @@ -220,6 +241,49 @@ func RunShell(username, url, script string, id, authID int) { name := strs[len(strs)-1] names := strings.Split(name, ".") name = names[0] + now := time.Now() + var cid_running proto.CIDRunning + cid_running.ID = id + cid_running.AuthID = authID + cid_running.StartTime = now + //脚本内容,不同用户的持续集成、部署目录不同 + scriptContent := ` +echo "start" +` + script + ` +echo "end"` + start := time.Now() + //执行脚本 + cmd := exec.Command("/bin/bash", "-c", scriptContent) + // 使用bytes.Buffer捕获输出 + var out bytes.Buffer + cmd.Stdout = &out + err3 := cmd.Run() + err3_info := "" + if err3 != nil { + err3_info = err3.Error() + } + elapsed := time.Since(start) + //fmt.Println("bash content:", scriptContent) + dao.CreateRunLog(id, authID, scriptContent, out.String(), err3_info, elapsed.Seconds()) //添加执行日志 +} + +func RunShellCID(cid_name, url, script string, id, authID int) { + strs := strings.Split(url, "/") + name := strs[len(strs)-1] + names := strings.Split(name, ".") + name = names[0] + now := time.Now() + var cid_running proto.CIDRunning + cid_running.ID = id + cid_running.AuthID = authID + cid_running.StartTime = now + cid_running.CID = cid_name + //加入正在运行 + proto.CID_RunningMutex.Lock() + user_running_list := proto.CID_Running_Map[authID] + user_running_list = append(user_running_list, cid_running) + proto.CID_Running_Map[authID] = user_running_list + proto.CID_RunningMutex.Unlock() //脚本内容,不同用户的持续集成、部署目录不同 scriptContent := ` @@ -240,6 +304,18 @@ echo "end"` elapsed := time.Since(start) //fmt.Println("bash content:", scriptContent) dao.CreateRunLog(id, authID, scriptContent, out.String(), err3_info, elapsed.Seconds()) //添加执行日志 + //移除正在运行 + proto.CID_RunningMutex.Lock() + user_running_list = proto.CID_Running_Map[authID] + for i, v := range user_running_list { + if v.StartTime.Equal(now) == true { + //删除 + user_running_list = append(user_running_list[:i], user_running_list[i+1:]...) + break + } + } + proto.CID_Running_Map[authID] = user_running_list + proto.CID_RunningMutex.Unlock() } // 定时任务处理逻辑 diff --git a/proto/conf.go b/proto/conf.go index 2253348..79a51e3 100644 --- a/proto/conf.go +++ b/proto/conf.go @@ -15,8 +15,10 @@ var SigningKey = []byte{} var Url_map = map[string]bool{"/login": true, "/register": true, "/uuid": true, "/gqr": true, "/cid/callback": true, "/tool/monitor": true, "/user/sync": true, "/tool/file/": true, "/user/reset": true, "/tool/dlp": true} // 不需要token验证的url var Per_menu_map = map[string]int{"/video/": 1, "/device/": 2, "/cid/": 3} var File_Type = map[string]int{"im": 1, "avatar": 2, "file": 3, "config": 4} // 文件类型 +var CID_Running_Map = map[int][]CIDRunning{} //正在运行的cid // 配置读写锁 +var CID_RunningMutex = sync.RWMutex{} var ConfigRWLock = &sync.RWMutex{} var SigningKeyRWLock = &sync.RWMutex{} diff --git a/proto/im.go b/proto/im.go index 49f4c3e..f53771d 100644 --- a/proto/im.go +++ b/proto/im.go @@ -1,5 +1,7 @@ package proto +import "time" + type ImKeyReq struct { To_user_id int `json:"to_user_id" form:"to_user_id" binding:"required"` } @@ -12,3 +14,11 @@ type Message struct { From_user_id int `json:"from_user_id"` Session string `json:"session"` } + +// cid正在运行结构 +type CIDRunning struct { + ID int `json:"id" form:"id"` //cid的id + CID string `json:"cid" form:"cid"` //cid名称 + AuthID int `json:"auth_id" form:"auth_id"` //所属用户 + StartTime time.Time `json:"start_time" form:"start_time"` //开始时间 +} diff --git a/service/toolService.go b/service/toolService.go index 12ca8f5..c6da1ab 100644 --- a/service/toolService.go +++ b/service/toolService.go @@ -325,3 +325,28 @@ func GetTokenSecretFromUserCenter() (*proto.SecretSyncSettings, error) { } return &secretResp, nil } + +// 获取cid正在运行 +func GetCIDRunningList(user_id int, req_type int) ([]proto.CIDRunning, error) { + var err error + var resp []proto.CIDRunning + if req_type == 0 { + proto.CID_RunningMutex.RLock() + resp = proto.CID_Running_Map[user_id] + proto.CID_RunningMutex.RUnlock() + } else if req_type == 1 { + user := GetUserByIDFromUserCenter(user_id) + if user.Role != "admin" { + err = errors.New("no permission") + } else { + proto.CID_RunningMutex.RLock() + for _, v := range proto.CID_Running_Map { + resp = append(resp, v...) + } + proto.CID_RunningMutex.RUnlock() + } + } else { + err = errors.New("request type is error") + } + return resp, err +}