diff --git a/dao/cid.go b/dao/cid.go new file mode 100644 index 0000000..3c4102d --- /dev/null +++ b/dao/cid.go @@ -0,0 +1,102 @@ +package dao + +import ( + "fmt" + "gorm.io/gorm" +) + +type CID struct { + gorm.Model + Auth_id int `gorm:"column:auth_id"` + Name string `gorm:"column:name"` + Url string `gorm:"column:url"` + Script string `gorm:"column:script"` + Token string `gorm:"column:token"` // 用于外部回调 +} + +type CIDRunLog struct { + gorm.Model + CID_id int `gorm:"column:cid_id"` + Auth_id int `form:"column:auth_id"` + Log string `gorm:"column:log"` + Error string `gorm:"column:error"` +} + +// CreateCID 创建持续集成、部署 +func CreateCID(name, url, script, token string, auth_id int) uint { + cid := CID{Name: name, Url: url, Script: script, Token: token, Auth_id: auth_id} + result := DB.Debug().Create(&cid) + if result.Error != nil { + return 0 + } + return cid.ID +} + +// DeleteCIDByID 删除持续集成、部署 +func DeleteCIDByID(id, auth_id int) bool { + res := DB.Debug().Model(&CID{}).Where("id = ? and auth_id = ?", id, auth_id).Delete(&CID{}) + if res.Error != nil { + return false + } + return true +} + +// FindCIDByID 查找持续集成、部署 +func FindCIDByID(id, auth_id int) CID { + var cid CID + DB.Debug().Where("id = ? and auth_id = ?", id, auth_id).First(&cid) + return cid +} + +// FindCIDByAuthID 查找持续集成、部署 +func FindCIDByAuthID(auth_id int) []CID { + var cids []CID + DB.Debug().Where("auth_id = ?", auth_id).Find(&cids) + return cids +} + +// UpdateCIDByID 更新持续集成、部署 +func UpdateCIDByID(id, auth_id int, name, url, script, token string) bool { + pd := FindCIDByID(id, auth_id) + if pd.ID == 0 { + return false + } + // 如果token为空,则不更新token + if token == "" { + token = pd.Token + } + result := DB.Debug().Model(&CID{}).Where("id = ? and auth_id = ?", id, auth_id).Updates(CID{Name: name, Url: url, Script: script, Token: token}) + if result.Error != nil { + return false + } + return true +} + +// CreateRunLog,添加执行日志 +func CreateRunLog(cid_id, auth_id int, log, err string) uint { + cidRunLog := CIDRunLog{CID_id: cid_id, Auth_id: auth_id, Log: log, Error: err} + result := DB.Debug().Create(&cidRunLog) + if result != nil { + fmt.Println(err) + return 0 + } + return cidRunLog.ID +} + +func FindRunLogByAuthID(auth_id int) []CIDRunLog { + var cidRunLogs []CIDRunLog + DB.Debug().Where(" auth_id = ?", auth_id).Find(&cidRunLogs).Order("created_at desc") + return cidRunLogs +} + +func FindRunLogByID(auth_id, cid_id int) CIDRunLog { + var cidRunLog CIDRunLog + DB.Debug().Where("cid_id = ? and auth_id = ?", cid_id, auth_id).First(&cidRunLog) + return cidRunLog +} + +func FindCIDByIDAndToken(id int, token string) CID { + var cid CID + DB.Debug().Where("id = ? and token = ?", id, token).First(&cid) + return cid +} diff --git a/dao/db.go b/dao/db.go index 344e21e..5bf9c7f 100644 --- a/dao/db.go +++ b/dao/db.go @@ -32,6 +32,14 @@ func Init() { if err != nil { fmt.Println("logger table:", err) } // 自动迁移,创建表,如果表已经存在,会自动更新表结构,不会删除表,只会创建不存在的表 + err = db.AutoMigrate(&CID{}) + if err != nil { + fmt.Println("cid table:", err) + } // 自动迁移,创建表,如果表已经存在,会自动更新表结构,不会删除表,只会创建不存在的表 + err = db.AutoMigrate(&CIDRunLog{}) + if err != nil { + fmt.Println("cidrunlog table:", err) + } // 自动迁移,创建表,如果表已经存在,会自动更新表结构,不会删除表,只会创建不存在的表 DB = db } diff --git a/handler/cid.go b/handler/cid.go new file mode 100644 index 0000000..f3421de --- /dev/null +++ b/handler/cid.go @@ -0,0 +1,210 @@ +package handler + +import ( + "bytes" + "fmt" + "github.com/gin-gonic/gin" + "os/exec" + "regexp" + "strconv" + "videoplayer/dao" + "videoplayer/proto" +) + +type CIDCreateReq struct { + Name string `json:"name" form:"name"` + Url string `json:"url" form:"url"` + Script string `json:"script" form:"script"` +} + +type CIDDeleteReq struct { + ID int `json:"id" form:"id"` +} + +type CIDRunReq struct { + ID int `json:"id" form:"id"` +} + +type CIDUpdateReq struct { + ID int `json:"id" form:"id"` + Name string `json:"name" form:"name"` + Url string `json:"url" form:"url"` + Script string `json:"script" form:"script"` + Token string `json:"cidtoken" form:"cidtoken"` +} + +func SetUpCIDGroup(router *gin.Engine) { + cidGroup := router.Group("/cid") //持续集成、部署 + cidGroup.POST("/create", CreateCID) + cidGroup.POST("/delete", DeleteCID) + cidGroup.POST("/update", UpdateCID) + cidGroup.POST("/list", GetCIDList) + cidGroup.POST("/run", RunCID) + cidGroup.POST("/log", GetCIDLogList) //获取执行日志 + cidGroup.POST("/log/detail", GetCIDLog) //获取执行日志详情 + cidGroup.POST("/callback", CIDCallback) +} +func RunCID(c *gin.Context) { + var req CIDRunReq + if err := c.ShouldBind(&req); err == nil { + // 获取用户ID + id, _ := c.Get("id") + authID := int(id.(float64)) + 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 { + re := regexp.MustCompile(`(?i)(?:https?://|git@)[^/]+/([^/]+)/([^/]+)(?:\.git)?$`) + matches := re.FindStringSubmatch(cid.Url) + name := matches[2] + scriptContent := `#!/bin/bash + TARGET_DIR = ` + proto.CID_BASE_DIR + `/workspace` + name + ` + if [ ! -d $TARGET_DIR ]; then + git clone ` + cid.Url + ` + cd $TARGET_DIR/` + name + ` + else + cd $TARGET_DIR/` + name + ` + git pull + fi + ` + cid.Script + //执行脚本 + cmd := exec.Command("/bin/bash", "-c", scriptContent) + err3 := cmd.Run() + fmt.Println("bash content:", scriptContent) + // 使用bytes.Buffer捕获输出 + var out bytes.Buffer + cmd.Stdout = &out + dao.CreateRunLog(req.ID, authID, out.String(), err3.Error()) //添加执行日志 + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": "success"}) + + } + } else { + c.JSON(200, gin.H{"error": err.Error(), "code": proto.ParameterError, "message": "failed"}) + } +} + +func CreateCID(c *gin.Context) { + var req CIDCreateReq + if err := c.ShouldBind(&req); err == nil { + // 获取用户ID + id, _ := c.Get("id") + authID := int(id.(float64)) + token, _ := generateRandomHexString(32) + + res := dao.CreateCID(req.Name, req.Url, req.Script, token, authID) + if res != 0 { + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": res}) + } else { + c.JSON(200, gin.H{"error": "CreateCID failed", "code": proto.OperationFailed, "message": "failed"}) + } + } else { + c.JSON(200, gin.H{"error": err.Error(), "code": proto.ParameterError, "message": "failed"}) + } +} + +func DeleteCID(c *gin.Context) { + var req CIDDeleteReq + if err := c.ShouldBind(&req); err == nil { + // 获取用户ID + id, _ := c.Get("id") + authID := int(id.(float64)) + cid := dao.DeleteCIDByID(req.ID, authID) + if cid == false { + c.JSON(200, gin.H{"error": "CID not found", "code": proto.OperationFailed, "message": "failed"}) + return + } else { + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": "success"}) + } + } else { + c.JSON(200, gin.H{"error": err.Error(), "code": proto.ParameterError, "message": "failed"}) + } +} + +func UpdateCID(c *gin.Context) { + var req CIDUpdateReq + if err := c.ShouldBind(&req); err == nil { + // 获取用户ID + id, _ := c.Get("id") + authID := int(id.(float64)) + cid := dao.UpdateCIDByID(req.ID, authID, req.Name, req.Url, req.Script, req.Token) + if cid == false { + c.JSON(200, gin.H{"error": "CID not found", "code": proto.OperationFailed, "message": "failed"}) + return + } else { + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": "success"}) + } + } else { + c.JSON(200, gin.H{"error": err.Error(), "code": proto.ParameterError, "message": "failed"}) + } + +} + +func GetCIDList(c *gin.Context) { + // 获取用户ID + id, _ := c.Get("id") + authID := int(id.(float64)) + cids := dao.FindCIDByAuthID(authID) + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": cids}) +} + +func GetCIDLog(c *gin.Context) { + var req CIDRunReq + if err := c.ShouldBind(&req); err == nil { + // 获取用户ID + id, _ := c.Get("id") + authID := int(id.(float64)) + cidLogs := dao.FindRunLogByID(req.ID, authID) + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": cidLogs}) + } else { + c.JSON(200, gin.H{"error": err.Error(), "code": proto.ParameterError, "message": "failed"}) + } +} + +func GetCIDLogList(c *gin.Context) { + // 获取用户ID + id, _ := c.Get("id") + authID := int(id.(float64)) + cidLogs := dao.FindRunLogByAuthID(authID) + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": cidLogs}) +} + +func CIDCallback(c *gin.Context) { + // 获取用户ID + token := c.Query("token") + cid_id := c.Query("id") + //将cid转换为int + cid, _ := strconv.Atoi(cid_id) + if token == "" || cid == 0 { + c.JSON(200, gin.H{"error": "parameter error", "code": proto.ParameterError, "message": "failed"}) + return + } + res := dao.FindCIDByIDAndToken(cid, token) + if res.ID != 0 { + //从url获取仓库名称 + re := regexp.MustCompile(`(?i)(?:https?://|git@)[^/]+/([^/]+)/([^/]+)(?:\.git)?$`) + matches := re.FindStringSubmatch(res.Url) + name := matches[2] + scriptContent := `#!/bin/bash + TARGET_DIR = ` + proto.CID_BASE_DIR + `/workspace` + name + ` + if [ ! -d $TARGET_DIR ]; then + git clone ` + res.Url + ` + cd $TARGET_DIR/` + name + ` + else + cd $TARGET_DIR/` + name + ` + git pull + fi + ` + res.Script + //执行脚本 + cmd := exec.Command("/bin/bash", "-c", scriptContent) + err3 := cmd.Run() + // 使用bytes.Buffer捕获输出 + var out bytes.Buffer + cmd.Stdout = &out + dao.CreateRunLog(cid, res.Auth_id, out.String(), err3.Error()) //添加执行日志 + 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"}) + return + } +} diff --git a/main.go b/main.go index eeccbda..eb212f4 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt" "io" + "os" "strings" "videoplayer/dao" "videoplayer/handler" @@ -24,10 +25,17 @@ func main() { handler.SetUpUserGroup(r) // User handler.SetUpDeviceGroup(r) // Device handler.SetUpIMGroup(r) // IM + handler.SetUpCIDGroup(r) // CID,持续集成、部署 r.Run(":8083") // listen and serve on 0.0.0.0:8082 defer dao.Close() defer worker.CloseRedis() } +func init() { + // 创建cid的目录 + os.MkdirAll(proto.CID_BASE_DIR, os.ModePerm) + os.MkdirAll(proto.CID_BASE_DIR+"script", os.ModePerm) + os.MkdirAll(proto.CID_BASE_DIR+"workspace", os.ModePerm) +} func writeLogger(c *gin.Context) { ip := c.ClientIP() diff --git a/proto/conf.go b/proto/conf.go index f27810c..5130b5f 100644 --- a/proto/conf.go +++ b/proto/conf.go @@ -15,6 +15,9 @@ const ( REIDS_DB = 2 TOKEN_SECRET = "mfjurnc_32ndj9dfhj" + + // 以下是持续集成、部署的配置 + CID_BASE_DIR = "/home/lijun/cid/" ) type User struct {