mirror of
https://gitee.com/dromara/mayfly-go
synced 2026-02-04 03:45:48 +08:00
refactor: 后端包结构重构、去除无用的文件
This commit is contained in:
289
server/internal/devops/api/db.go
Normal file
289
server/internal/devops/api/db.go
Normal file
@@ -0,0 +1,289 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"mayfly-go/internal/devops/api/form"
|
||||
"mayfly-go/internal/devops/api/vo"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
sysApplication "mayfly-go/internal/sys/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils"
|
||||
"mayfly-go/pkg/ws"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Db struct {
|
||||
DbApp application.Db
|
||||
MsgApp sysApplication.Msg
|
||||
ProjectApp application.Project
|
||||
}
|
||||
|
||||
// @router /api/dbs [get]
|
||||
func (d *Db) Dbs(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
m := &entity.Db{EnvId: uint64(ginx.QueryInt(g, "envId", 0)),
|
||||
ProjectId: uint64(ginx.QueryInt(g, "projectId", 0)),
|
||||
}
|
||||
m.CreatorId = rc.LoginAccount.Id
|
||||
rc.ResData = d.DbApp.GetPageList(m, ginx.GetPageParam(rc.GinCtx), new([]vo.SelectDataDbVO))
|
||||
}
|
||||
|
||||
func (d *Db) Save(rc *ctx.ReqCtx) {
|
||||
form := &form.DbForm{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, form)
|
||||
|
||||
rc.ReqParam = form
|
||||
|
||||
db := new(entity.Db)
|
||||
utils.Copy(db, form)
|
||||
db.SetBaseInfo(rc.LoginAccount)
|
||||
d.DbApp.Save(db)
|
||||
}
|
||||
|
||||
func (d *Db) DeleteDb(rc *ctx.ReqCtx) {
|
||||
d.DbApp.Delete(GetDbId(rc.GinCtx))
|
||||
}
|
||||
|
||||
func (d *Db) TableInfos(rc *ctx.ReqCtx) {
|
||||
rc.ResData = d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx)).GetTableInfos()
|
||||
}
|
||||
|
||||
func (d *Db) TableIndex(rc *ctx.ReqCtx) {
|
||||
tn := rc.GinCtx.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
rc.ResData = d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx)).GetTableIndex(tn)
|
||||
}
|
||||
|
||||
func (d *Db) GetCreateTableDdl(rc *ctx.ReqCtx) {
|
||||
tn := rc.GinCtx.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
rc.ResData = d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx)).GetCreateTableDdl(tn)
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/exec-sql [get]
|
||||
func (d *Db) ExecSql(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
|
||||
id, db := GetIdAndDb(g)
|
||||
dbInstance := d.DbApp.GetDbInstance(id, db)
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbInstance.ProjectId), "%s")
|
||||
|
||||
// 去除前后空格及换行符
|
||||
sql := strings.TrimFunc(g.Query("sql"), func(r rune) bool {
|
||||
s := string(r)
|
||||
return s == " " || s == "\n"
|
||||
})
|
||||
rc.ReqParam = fmt.Sprintf("db: %d:%s | sql: %s", id, db, sql)
|
||||
|
||||
biz.NotEmpty(sql, "sql不能为空")
|
||||
if strings.HasPrefix(sql, "SELECT") || strings.HasPrefix(sql, "select") || strings.HasPrefix(sql, "show") {
|
||||
colNames, res, err := dbInstance.SelectData(sql)
|
||||
biz.ErrIsNilAppendErr(err, "查询失败: %s")
|
||||
colAndRes := make(map[string]interface{})
|
||||
colAndRes["colNames"] = colNames
|
||||
colAndRes["res"] = res
|
||||
rc.ResData = colAndRes
|
||||
} else {
|
||||
rowsAffected, err := dbInstance.Exec(sql)
|
||||
biz.ErrIsNilAppendErr(err, "执行失败: %s")
|
||||
res := make([]map[string]string, 0)
|
||||
resData := make(map[string]string)
|
||||
resData["影响条数"] = fmt.Sprintf("%d", rowsAffected)
|
||||
res = append(res, resData)
|
||||
|
||||
colAndRes := make(map[string]interface{})
|
||||
colAndRes["colNames"] = []string{"影响条数"}
|
||||
colAndRes["res"] = res
|
||||
|
||||
rc.ResData = colAndRes
|
||||
}
|
||||
}
|
||||
|
||||
// 执行sql文件
|
||||
func (d *Db) ExecSqlFile(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
fileheader, err := g.FormFile("file")
|
||||
biz.ErrIsNilAppendErr(err, "读取sql文件失败: %s")
|
||||
|
||||
// 读取sql文件并根据;切割sql语句
|
||||
file, _ := fileheader.Open()
|
||||
filename := fileheader.Filename
|
||||
bytes, _ := ioutil.ReadAll(file)
|
||||
sqlContent := string(bytes)
|
||||
sqls := strings.Split(sqlContent, ";")
|
||||
dbId, db := GetIdAndDb(g)
|
||||
|
||||
go func() {
|
||||
db := d.DbApp.GetDbInstance(dbId, db)
|
||||
|
||||
dbEntity := d.DbApp.GetById(dbId)
|
||||
dbInfo := fmt.Sprintf("于%s的%s环境", dbEntity.Name, dbEntity.Env)
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
switch t := err.(type) {
|
||||
case *biz.BizError:
|
||||
d.MsgApp.CreateAndSend(rc.LoginAccount, ws.ErrMsg("sql脚本执行失败", fmt.Sprintf("[%s]%s执行失败: [%s]", filename, dbInfo, t.Error())))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, db.ProjectId), "%s")
|
||||
|
||||
for _, sql := range sqls {
|
||||
sql = strings.Trim(sql, " ")
|
||||
if sql == "" || sql == "\n" {
|
||||
continue
|
||||
}
|
||||
_, err := db.Exec(sql)
|
||||
if err != nil {
|
||||
d.MsgApp.CreateAndSend(rc.LoginAccount, ws.ErrMsg("sql脚本执行失败", fmt.Sprintf("[%s]%s执行失败: [%s]", filename, dbInfo, err.Error())))
|
||||
return
|
||||
}
|
||||
}
|
||||
d.MsgApp.CreateAndSend(rc.LoginAccount, ws.SuccessMsg("sql脚本执行成功", fmt.Sprintf("[%s]%s执行完成", filename, dbInfo)))
|
||||
}()
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/t-metadata [get]
|
||||
func (d *Db) TableMA(rc *ctx.ReqCtx) {
|
||||
dbi := d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbi.ProjectId), "%s")
|
||||
rc.ResData = dbi.GetTableMetedatas()
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/c-metadata [get]
|
||||
func (d *Db) ColumnMA(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
tn := g.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
|
||||
dbi := d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbi.ProjectId), "%s")
|
||||
rc.ResData = dbi.GetColumnMetadatas(tn)
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/hint-tables [get]
|
||||
func (d *Db) HintTables(rc *ctx.ReqCtx) {
|
||||
dbi := d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbi.ProjectId), "%s")
|
||||
// 获取所有表
|
||||
tables := dbi.GetTableMetedatas()
|
||||
|
||||
tableNames := make([]string, 0)
|
||||
for _, v := range tables {
|
||||
tableNames = append(tableNames, v["tableName"])
|
||||
}
|
||||
// key = 表名,value = 列名数组
|
||||
res := make(map[string][]string)
|
||||
|
||||
// 表为空,则直接返回
|
||||
if len(tableNames) == 0 {
|
||||
rc.ResData = res
|
||||
return
|
||||
}
|
||||
|
||||
// 获取所有表下的所有列信息
|
||||
columnMds := dbi.GetColumnMetadatas(tableNames...)
|
||||
for _, v := range columnMds {
|
||||
tName := v["tableName"]
|
||||
if res[tName] == nil {
|
||||
res[tName] = make([]string, 0)
|
||||
}
|
||||
|
||||
columnName := fmt.Sprintf("%s [%s]", v["columnName"], v["columnType"])
|
||||
comment := v["columnComment"]
|
||||
// 如果字段备注不为空,则加上备注信息
|
||||
if comment != "" {
|
||||
columnName = fmt.Sprintf("%s[%s]", columnName, comment)
|
||||
}
|
||||
|
||||
res[tName] = append(res[tName], columnName)
|
||||
}
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/sql [post]
|
||||
func (d *Db) SaveSql(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
account := rc.LoginAccount
|
||||
dbSqlForm := &form.DbSqlSaveForm{}
|
||||
ginx.BindJsonAndValid(g, dbSqlForm)
|
||||
rc.ReqParam = dbSqlForm
|
||||
|
||||
dbId := GetDbId(g)
|
||||
// 判断dbId是否存在
|
||||
err := model.GetById(new(entity.Db), dbId)
|
||||
biz.ErrIsNil(err, "该数据库信息不存在")
|
||||
|
||||
// 获取用于是否有该dbsql的保存记录,有则更改,否则新增
|
||||
dbSql := &entity.DbSql{Type: dbSqlForm.Type, DbId: dbId, Name: dbSqlForm.Name, Db: dbSqlForm.Db}
|
||||
dbSql.CreatorId = account.Id
|
||||
e := model.GetBy(dbSql)
|
||||
|
||||
dbSql.SetBaseInfo(account)
|
||||
// 更新sql信息
|
||||
dbSql.Sql = dbSqlForm.Sql
|
||||
if e == nil {
|
||||
model.UpdateById(dbSql)
|
||||
} else {
|
||||
model.Insert(dbSql)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有保存的sql names
|
||||
func (d *Db) GetSqlNames(rc *ctx.ReqCtx) {
|
||||
id, db := GetIdAndDb(rc.GinCtx)
|
||||
// 获取用于是否有该dbsql的保存记录,有则更改,否则新增
|
||||
dbSql := &entity.DbSql{Type: 1, DbId: id, Db: db}
|
||||
dbSql.CreatorId = rc.LoginAccount.Id
|
||||
var sqls []entity.DbSql
|
||||
model.ListBy(dbSql, &sqls, "id", "name")
|
||||
|
||||
rc.ResData = sqls
|
||||
}
|
||||
|
||||
// 删除保存的sql
|
||||
func (d *Db) DeleteSql(rc *ctx.ReqCtx) {
|
||||
dbSql := &entity.DbSql{Type: 1, DbId: GetDbId(rc.GinCtx)}
|
||||
dbSql.CreatorId = rc.LoginAccount.Id
|
||||
dbSql.Name = rc.GinCtx.Query("name")
|
||||
|
||||
model.DeleteByCondition(dbSql)
|
||||
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/sql [get]
|
||||
func (d *Db) GetSql(rc *ctx.ReqCtx) {
|
||||
id, db := GetIdAndDb(rc.GinCtx)
|
||||
// 根据创建者id, 数据库id,以及sql模板名称查询保存的sql信息
|
||||
dbSql := &entity.DbSql{Type: 1, DbId: id, Db: db}
|
||||
dbSql.CreatorId = rc.LoginAccount.Id
|
||||
dbSql.Name = rc.GinCtx.Query("name")
|
||||
|
||||
e := model.GetBy(dbSql)
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
rc.ResData = dbSql
|
||||
}
|
||||
|
||||
func GetDbId(g *gin.Context) uint64 {
|
||||
dbId, _ := strconv.Atoi(g.Param("dbId"))
|
||||
biz.IsTrue(dbId > 0, "dbId错误")
|
||||
return uint64(dbId)
|
||||
}
|
||||
|
||||
func GetIdAndDb(g *gin.Context) (uint64, string) {
|
||||
db := g.Query("db")
|
||||
biz.NotEmpty(db, "db不能为空")
|
||||
return GetDbId(g), db
|
||||
}
|
||||
16
server/internal/devops/api/form/db.go
Normal file
16
server/internal/devops/api/form/db.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package form
|
||||
|
||||
type DbForm struct {
|
||||
Id uint64
|
||||
Name string `binding:"required" json:"name"`
|
||||
Type string `binding:"required" json:"type"` // 类型,mysql oracle等
|
||||
Host string `binding:"required" json:"host"`
|
||||
Port int `binding:"required" json:"port"`
|
||||
Username string `binding:"required" json:"username"`
|
||||
Password string `json:"password"`
|
||||
Database string `binding:"required" json:"database"`
|
||||
ProjectId uint64 `binding:"required" json:"projectId"`
|
||||
Project string `json:"project"`
|
||||
Env string `json:"env"`
|
||||
EnvId uint64 `binding:"required" json:"envId"`
|
||||
}
|
||||
52
server/internal/devops/api/form/form.go
Normal file
52
server/internal/devops/api/form/form.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package form
|
||||
|
||||
type MachineForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
// IP地址
|
||||
Ip string `json:"ip" binding:"required"`
|
||||
// 用户名
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password"`
|
||||
// 端口号
|
||||
Port int `json:"port" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type MachineRunForm struct {
|
||||
MachineId int64 `binding:"required"`
|
||||
Cmd string `binding:"required"`
|
||||
}
|
||||
|
||||
type MachineFileForm struct {
|
||||
Id uint64
|
||||
Name string `binding:"required"`
|
||||
MachineId uint64 `binding:"required"`
|
||||
Type int `binding:"required"`
|
||||
Path string `binding:"required"`
|
||||
}
|
||||
|
||||
type MachineScriptForm struct {
|
||||
Id uint64
|
||||
Name string `binding:"required"`
|
||||
MachineId uint64 `binding:"required"`
|
||||
Type int `binding:"required"`
|
||||
Description string `binding:"required"`
|
||||
Params string
|
||||
Script string `binding:"required"`
|
||||
}
|
||||
|
||||
type DbSqlSaveForm struct {
|
||||
Name string
|
||||
Sql string `binding:"required"`
|
||||
Type int `binding:"required"`
|
||||
Db string `binding:"required"`
|
||||
}
|
||||
|
||||
type MachineFileUpdateForm struct {
|
||||
Content string `binding:"required"`
|
||||
Id uint64 `binding:"required"`
|
||||
Path string `binding:"required"`
|
||||
}
|
||||
40
server/internal/devops/api/form/mongo.go
Normal file
40
server/internal/devops/api/form/mongo.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package form
|
||||
|
||||
type Mongo struct {
|
||||
Id uint64
|
||||
Uri string `binding:"required" json:"uri"`
|
||||
Name string `binding:"required" json:"name"`
|
||||
ProjectId uint64 `binding:"required" json:"projectId"`
|
||||
Project string `json:"project"`
|
||||
Env string `json:"env"`
|
||||
EnvId uint64 `binding:"required" json:"envId"`
|
||||
}
|
||||
|
||||
type MongoCommand struct {
|
||||
Database string `binding:"required" json:"database"`
|
||||
Collection string `binding:"required" json:"collection"`
|
||||
Filter map[string]interface{} `json:"filter"`
|
||||
}
|
||||
|
||||
type MongoRunCommand struct {
|
||||
Database string `binding:"required" json:"database"`
|
||||
Command map[string]interface{} `json:"command"`
|
||||
}
|
||||
|
||||
type MongoFindCommand struct {
|
||||
MongoCommand
|
||||
Sort map[string]interface{} `json:"sort"`
|
||||
Skip int64
|
||||
Limit int64
|
||||
}
|
||||
|
||||
type MongoUpdateByIdCommand struct {
|
||||
MongoCommand
|
||||
DocId interface{} `binding:"required" json:"docId"`
|
||||
Update map[string]interface{} `json:"update"`
|
||||
}
|
||||
|
||||
type MongoInsertCommand struct {
|
||||
MongoCommand
|
||||
Doc map[string]interface{} `json:"doc"`
|
||||
}
|
||||
32
server/internal/devops/api/form/redis.go
Normal file
32
server/internal/devops/api/form/redis.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package form
|
||||
|
||||
type Redis struct {
|
||||
Id uint64
|
||||
Host string `binding:"required" json:"host"`
|
||||
Password string `json:"password"`
|
||||
Db int `json:"db"`
|
||||
ProjectId uint64 `binding:"required" json:"projectId"`
|
||||
Project string `json:"project"`
|
||||
Env string `json:"env"`
|
||||
EnvId uint64 `binding:"required" json:"envId"`
|
||||
}
|
||||
|
||||
type KeyInfo struct {
|
||||
Key string `binding:"required" json:"key"`
|
||||
Timed int64
|
||||
}
|
||||
|
||||
type StringValue struct {
|
||||
KeyInfo
|
||||
Value interface{} `binding:"required" json:"value"`
|
||||
}
|
||||
|
||||
type HashValue struct {
|
||||
KeyInfo
|
||||
Value []map[string]interface{} `binding:"required" json:"value"`
|
||||
}
|
||||
|
||||
type SetValue struct {
|
||||
KeyInfo
|
||||
Value []interface{} `binding:"required" json:"value"`
|
||||
}
|
||||
165
server/internal/devops/api/machine.go
Normal file
165
server/internal/devops/api/machine.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/api/form"
|
||||
"mayfly-go/internal/devops/api/vo"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/infrastructure/machine"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/utils"
|
||||
"mayfly-go/pkg/ws"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
MachineApp application.Machine
|
||||
ProjectApp application.Project
|
||||
}
|
||||
|
||||
func (m *Machine) Machines(rc *ctx.ReqCtx) {
|
||||
condition := new(entity.Machine)
|
||||
// 使用创建者id模拟账号成员id
|
||||
condition.CreatorId = rc.LoginAccount.Id
|
||||
condition.Ip = rc.GinCtx.Query("ip")
|
||||
condition.Name = rc.GinCtx.Query("name")
|
||||
condition.ProjectId = uint64(ginx.QueryInt(rc.GinCtx, "projectId", 0))
|
||||
|
||||
res := m.MachineApp.GetMachineList(condition, ginx.GetPageParam(rc.GinCtx), new([]*vo.MachineVO))
|
||||
if res.Total == 0 {
|
||||
rc.ResData = res
|
||||
return
|
||||
}
|
||||
|
||||
list := res.List.(*[]*vo.MachineVO)
|
||||
for _, mv := range *list {
|
||||
mv.HasCli = machine.HasCli(*mv.Id)
|
||||
}
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Machine) MachineStats(rc *ctx.ReqCtx) {
|
||||
stats := m.MachineApp.GetCli(GetMachineId(rc.GinCtx)).GetAllStats()
|
||||
rc.ResData = stats
|
||||
}
|
||||
|
||||
func (m *Machine) SaveMachine(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
machineForm := new(form.MachineForm)
|
||||
ginx.BindJsonAndValid(g, machineForm)
|
||||
|
||||
entity := new(entity.Machine)
|
||||
utils.Copy(entity, machineForm)
|
||||
|
||||
entity.SetBaseInfo(rc.LoginAccount)
|
||||
m.MachineApp.Save(entity)
|
||||
}
|
||||
|
||||
func (m *Machine) ChangeStatus(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
id := uint64(ginx.PathParamInt(g, "machineId"))
|
||||
status := int8(ginx.PathParamInt(g, "status"))
|
||||
rc.ReqParam = fmt.Sprintf("id: %d -- status: %d", id, status)
|
||||
m.MachineApp.ChangeStatus(id, status)
|
||||
}
|
||||
|
||||
func (m *Machine) DeleteMachine(rc *ctx.ReqCtx) {
|
||||
id := uint64(ginx.PathParamInt(rc.GinCtx, "machineId"))
|
||||
rc.ReqParam = id
|
||||
m.MachineApp.Delete(id)
|
||||
}
|
||||
|
||||
// 关闭机器客户端
|
||||
func (m *Machine) CloseCli(rc *ctx.ReqCtx) {
|
||||
machine.DeleteCli(GetMachineId(rc.GinCtx))
|
||||
}
|
||||
|
||||
// 获取进程列表信息
|
||||
func (m *Machine) GetProcess(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
cmd := "ps -aux "
|
||||
sortType := g.Query("sortType")
|
||||
if sortType == "2" {
|
||||
cmd += "--sort -pmem "
|
||||
} else {
|
||||
cmd += "--sort -pcpu "
|
||||
}
|
||||
|
||||
pname := g.Query("name")
|
||||
if pname != "" {
|
||||
cmd += fmt.Sprintf("| grep %s ", pname)
|
||||
}
|
||||
|
||||
count := g.Query("count")
|
||||
if count == "" {
|
||||
count = "10"
|
||||
}
|
||||
|
||||
cmd += "| head -n " + count
|
||||
|
||||
cli := m.MachineApp.GetCli(GetMachineId(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(m.ProjectApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().ProjectId), "%s")
|
||||
|
||||
res, err := cli.Run(cmd)
|
||||
biz.ErrIsNilAppendErr(err, "获取进程信息失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
// 终止进程
|
||||
func (m *Machine) KillProcess(rc *ctx.ReqCtx) {
|
||||
pid := rc.GinCtx.Query("pid")
|
||||
biz.NotEmpty(pid, "进程id不能为空")
|
||||
|
||||
cli := m.MachineApp.GetCli(GetMachineId(rc.GinCtx))
|
||||
biz.ErrIsNilAppendErr(m.ProjectApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().ProjectId), "%s")
|
||||
|
||||
_, err := cli.Run("kill -9 " + pid)
|
||||
biz.ErrIsNilAppendErr(err, "终止进程失败: %s")
|
||||
}
|
||||
|
||||
func (m *Machine) WsSSH(g *gin.Context) {
|
||||
wsConn, err := ws.Upgrader.Upgrade(g.Writer, g.Request, nil)
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
wsConn.WriteMessage(websocket.TextMessage, []byte(err.(error).Error()))
|
||||
wsConn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
panic(biz.NewBizErr("升级websocket失败"))
|
||||
}
|
||||
// 权限校验
|
||||
rc := ctx.NewReqCtxWithGin(g).WithRequiredPermission(ctx.NewPermission("machine:terminal"))
|
||||
if err = ctx.PermissionHandler(rc); err != nil {
|
||||
panic(biz.NewBizErr("没有权限"))
|
||||
}
|
||||
|
||||
cols := ginx.QueryInt(g, "cols", 80)
|
||||
rows := ginx.QueryInt(g, "rows", 40)
|
||||
|
||||
cli := m.MachineApp.GetCli(GetMachineId(g))
|
||||
biz.ErrIsNilAppendErr(m.ProjectApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().ProjectId), "%s")
|
||||
|
||||
sws, err := machine.NewLogicSshWsSession(cols, rows, cli, wsConn)
|
||||
biz.ErrIsNilAppendErr(err, "连接失败:%s")
|
||||
defer sws.Close()
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
sws.Start(quitChan)
|
||||
go sws.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
}
|
||||
|
||||
func GetMachineId(g *gin.Context) uint64 {
|
||||
machineId, _ := strconv.Atoi(g.Param("machineId"))
|
||||
biz.IsTrue(machineId != 0, "machineId错误")
|
||||
return uint64(machineId)
|
||||
}
|
||||
177
server/internal/devops/api/machine_file.go
Normal file
177
server/internal/devops/api/machine_file.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"mayfly-go/internal/devops/api/form"
|
||||
"mayfly-go/internal/devops/api/vo"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
sysApplication "mayfly-go/internal/sys/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/utils"
|
||||
"mayfly-go/pkg/ws"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type MachineFile struct {
|
||||
MachineFileApp application.MachineFile
|
||||
MachineApp application.Machine
|
||||
MsgApp sysApplication.Msg
|
||||
}
|
||||
|
||||
const (
|
||||
file = "-"
|
||||
dir = "d"
|
||||
link = "l"
|
||||
max_read_size = 1 * 1024 * 1024
|
||||
)
|
||||
|
||||
func (m *MachineFile) MachineFiles(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
condition := &entity.MachineFile{MachineId: GetMachineId(g)}
|
||||
rc.ResData = m.MachineFileApp.GetPageList(condition, ginx.GetPageParam(g), new([]vo.MachineFileVO))
|
||||
}
|
||||
|
||||
func (m *MachineFile) SaveMachineFiles(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
fileForm := new(form.MachineFileForm)
|
||||
ginx.BindJsonAndValid(g, fileForm)
|
||||
|
||||
entity := new(entity.MachineFile)
|
||||
utils.Copy(entity, fileForm)
|
||||
|
||||
entity.SetBaseInfo(rc.LoginAccount)
|
||||
|
||||
m.MachineFileApp.Save(entity)
|
||||
}
|
||||
|
||||
func (m *MachineFile) DeleteFile(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
fid := GetMachineFileId(g)
|
||||
m.MachineFileApp.Delete(fid)
|
||||
}
|
||||
|
||||
/*** sftp相关操作 */
|
||||
|
||||
func (m *MachineFile) ReadFileContent(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
fid := GetMachineFileId(g)
|
||||
readPath := g.Query("path")
|
||||
readType := g.Query("type")
|
||||
|
||||
sftpFile := m.MachineFileApp.ReadFile(fid, readPath)
|
||||
defer sftpFile.Close()
|
||||
|
||||
fileInfo, _ := sftpFile.Stat()
|
||||
// 如果是读取文件内容,则校验文件大小
|
||||
if readType != "1" {
|
||||
biz.IsTrue(fileInfo.Size() < max_read_size, "文件超过1m,请使用下载查看")
|
||||
}
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("path: %s", readPath)
|
||||
// 如果读取类型为下载,则下载文件,否则获取文件内容
|
||||
if readType == "1" {
|
||||
// 截取文件名,如/usr/local/test.java -》 test.java
|
||||
path := strings.Split(readPath, "/")
|
||||
rc.Download(sftpFile, path[len(path)-1])
|
||||
} else {
|
||||
datas, err := ioutil.ReadAll(sftpFile)
|
||||
biz.ErrIsNilAppendErr(err, "读取文件内容失败: %s")
|
||||
rc.ResData = string(datas)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MachineFile) GetDirEntry(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
fid := GetMachineFileId(g)
|
||||
readPath := g.Query("path")
|
||||
|
||||
if !strings.HasSuffix(readPath, "/") {
|
||||
readPath = readPath + "/"
|
||||
}
|
||||
fis := m.MachineFileApp.ReadDir(fid, readPath)
|
||||
fisVO := make([]vo.MachineFileInfo, 0)
|
||||
for _, fi := range fis {
|
||||
fisVO = append(fisVO, vo.MachineFileInfo{
|
||||
Name: fi.Name(),
|
||||
Size: fi.Size(),
|
||||
Path: readPath + fi.Name(),
|
||||
Type: getFileType(fi.Mode()),
|
||||
})
|
||||
}
|
||||
rc.ResData = fisVO
|
||||
rc.ReqParam = fmt.Sprintf("path: %s", readPath)
|
||||
}
|
||||
|
||||
func (m *MachineFile) WriteFileContent(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
fid := GetMachineFileId(g)
|
||||
|
||||
form := new(form.MachineFileUpdateForm)
|
||||
ginx.BindJsonAndValid(g, form)
|
||||
path := form.Path
|
||||
|
||||
m.MachineFileApp.WriteFileContent(fid, path, []byte(form.Content))
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("path: %s", path)
|
||||
}
|
||||
|
||||
func (m *MachineFile) UploadFile(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
fid := GetMachineFileId(g)
|
||||
path := g.PostForm("path")
|
||||
|
||||
fileheader, err := g.FormFile("file")
|
||||
biz.ErrIsNilAppendErr(err, "读取文件失败: %s")
|
||||
|
||||
file, _ := fileheader.Open()
|
||||
rc.ReqParam = fmt.Sprintf("path: %s", path)
|
||||
|
||||
la := rc.LoginAccount
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
switch t := err.(type) {
|
||||
case *biz.BizError:
|
||||
m.MsgApp.CreateAndSend(la, ws.ErrMsg("文件上传失败", fmt.Sprintf("执行文件上传失败:\n<-e errCode: %d, errMsg: %s", t.Code(), t.Error())))
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer file.Close()
|
||||
m.MachineFileApp.UploadFile(fid, path, fileheader.Filename, file)
|
||||
// 保存消息并发送文件上传成功通知
|
||||
machine := m.MachineApp.GetById(m.MachineFileApp.GetById(fid).MachineId)
|
||||
m.MsgApp.CreateAndSend(la, ws.SuccessMsg("文件上传成功", fmt.Sprintf("[%s]文件已成功上传至 %s[%s:%s]", fileheader.Filename, machine.Name, machine.Ip, path)))
|
||||
}()
|
||||
}
|
||||
|
||||
func (m *MachineFile) RemoveFile(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
fid := GetMachineFileId(g)
|
||||
// mid := GetMachineId(g)
|
||||
path := g.Query("path")
|
||||
|
||||
m.MachineFileApp.RemoveFile(fid, path)
|
||||
|
||||
rc.ReqParam = fmt.Sprintf("path: %s", path)
|
||||
}
|
||||
|
||||
func getFileType(fm fs.FileMode) string {
|
||||
if fm.IsDir() {
|
||||
return dir
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
func GetMachineFileId(g *gin.Context) uint64 {
|
||||
fileId, _ := strconv.Atoi(g.Param("fileId"))
|
||||
biz.IsTrue(fileId != 0, "fileId错误")
|
||||
return uint64(fileId)
|
||||
}
|
||||
82
server/internal/devops/api/machine_script.go
Normal file
82
server/internal/devops/api/machine_script.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/api/form"
|
||||
"mayfly-go/internal/devops/api/vo"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type MachineScript struct {
|
||||
MachineScriptApp application.MachineScript
|
||||
MachineApp application.Machine
|
||||
ProjectApp application.Project
|
||||
}
|
||||
|
||||
func (m *MachineScript) MachineScripts(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
condition := &entity.MachineScript{MachineId: GetMachineId(g)}
|
||||
rc.ResData = m.MachineScriptApp.GetPageList(condition, ginx.GetPageParam(g), new([]vo.MachineScriptVO))
|
||||
}
|
||||
|
||||
func (m *MachineScript) SaveMachineScript(rc *ctx.ReqCtx) {
|
||||
form := new(form.MachineScriptForm)
|
||||
ginx.BindJsonAndValid(rc.GinCtx, form)
|
||||
rc.ReqParam = form
|
||||
|
||||
// 转换为entity,并设置基本信息
|
||||
machineScript := new(entity.MachineScript)
|
||||
utils.Copy(machineScript, form)
|
||||
machineScript.SetBaseInfo(rc.LoginAccount)
|
||||
|
||||
m.MachineScriptApp.Save(machineScript)
|
||||
}
|
||||
|
||||
func (m *MachineScript) DeleteMachineScript(rc *ctx.ReqCtx) {
|
||||
msa := m.MachineScriptApp
|
||||
sid := GetMachineScriptId(rc.GinCtx)
|
||||
ms := msa.GetById(sid)
|
||||
biz.NotNil(ms, "该脚本不存在")
|
||||
rc.ReqParam = fmt.Sprintf("[scriptId: %d, name: %s, desc: %s, script: %s]", sid, ms.Name, ms.Description, ms.Script)
|
||||
msa.Delete(sid)
|
||||
}
|
||||
|
||||
func (m *MachineScript) RunMachineScript(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
|
||||
scriptId := GetMachineScriptId(g)
|
||||
machineId := GetMachineId(g)
|
||||
ms := m.MachineScriptApp.GetById(scriptId, "MachineId", "Name", "Script")
|
||||
biz.NotNil(ms, "该脚本不存在")
|
||||
biz.IsTrue(ms.MachineId == application.Common_Script_Machine_Id || ms.MachineId == machineId, "该脚本不属于该机器")
|
||||
|
||||
script := ms.Script
|
||||
// 如果有脚本参数,则用脚本参数替换脚本中的模板占位符参数
|
||||
if params := g.Query("params"); params != "" {
|
||||
script = utils.TemplateParse(ms.Script, utils.Json2Map(params))
|
||||
}
|
||||
cli := m.MachineApp.GetCli(machineId)
|
||||
biz.ErrIsNilAppendErr(m.ProjectApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().ProjectId), "%s")
|
||||
|
||||
res, err := cli.Run(script)
|
||||
// 记录请求参数
|
||||
rc.ReqParam = fmt.Sprintf("[machineId: %d, scriptId: %d, name: %s]", machineId, scriptId, ms.Name)
|
||||
if err != nil {
|
||||
panic(biz.NewBizErr(fmt.Sprintf("执行命令失败:%s", err.Error())))
|
||||
}
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func GetMachineScriptId(g *gin.Context) uint64 {
|
||||
scriptId, _ := strconv.Atoi(g.Param("scriptId"))
|
||||
biz.IsTrue(scriptId > 0, "scriptId错误")
|
||||
return uint64(scriptId)
|
||||
}
|
||||
168
server/internal/devops/api/mongo.go
Normal file
168
server/internal/devops/api/mongo.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/devops/api/form"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type Mongo struct {
|
||||
MongoApp application.Mongo
|
||||
}
|
||||
|
||||
func (m *Mongo) Mongos(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
mc := &entity.Mongo{EnvId: uint64(ginx.QueryInt(g, "envId", 0)),
|
||||
ProjectId: uint64(ginx.QueryInt(g, "projectId", 0)),
|
||||
}
|
||||
mc.CreatorId = rc.LoginAccount.Id
|
||||
rc.ResData = m.MongoApp.GetPageList(mc, ginx.GetPageParam(rc.GinCtx), new([]entity.Mongo))
|
||||
}
|
||||
|
||||
func (m *Mongo) Save(rc *ctx.ReqCtx) {
|
||||
form := &form.Mongo{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, form)
|
||||
|
||||
rc.ReqParam = form
|
||||
|
||||
mongo := new(entity.Mongo)
|
||||
utils.Copy(mongo, form)
|
||||
mongo.SetBaseInfo(rc.LoginAccount)
|
||||
m.MongoApp.Save(mongo)
|
||||
}
|
||||
|
||||
func (m *Mongo) DeleteMongo(rc *ctx.ReqCtx) {
|
||||
m.MongoApp.Delete(m.GetMongoId(rc.GinCtx))
|
||||
}
|
||||
|
||||
func (m *Mongo) Databases(rc *ctx.ReqCtx) {
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(rc.GinCtx))
|
||||
res, err := cli.ListDatabases(context.TODO(), bson.D{})
|
||||
biz.ErrIsNilAppendErr(err, "获取mongo所有库信息失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) Collections(rc *ctx.ReqCtx) {
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(rc.GinCtx))
|
||||
db := rc.GinCtx.Query("database")
|
||||
biz.NotEmpty(db, "database不能为空")
|
||||
ctx := context.TODO()
|
||||
res, err := cli.Database(db).ListCollectionNames(ctx, bson.D{})
|
||||
biz.ErrIsNilAppendErr(err, "获取库集合信息失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) RunCommand(rc *ctx.ReqCtx) {
|
||||
commandForm := new(form.MongoRunCommand)
|
||||
ginx.BindJsonAndValid(rc.GinCtx, commandForm)
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(rc.GinCtx))
|
||||
|
||||
ctx := context.TODO()
|
||||
var bm bson.M
|
||||
err := cli.Database(commandForm.Database).RunCommand(
|
||||
ctx,
|
||||
commandForm.Command,
|
||||
).Decode(&bm)
|
||||
|
||||
biz.ErrIsNilAppendErr(err, "执行命令失败: %s")
|
||||
rc.ResData = bm
|
||||
}
|
||||
|
||||
func (m *Mongo) FindCommand(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(g))
|
||||
commandForm := new(form.MongoFindCommand)
|
||||
ginx.BindJsonAndValid(g, commandForm)
|
||||
|
||||
limit := commandForm.Limit
|
||||
if limit != 0 {
|
||||
biz.IsTrue(limit <= 100, "limit不能超过100")
|
||||
}
|
||||
opts := options.Find().SetSort(commandForm.Sort).
|
||||
SetSkip(commandForm.Skip).
|
||||
SetLimit(limit)
|
||||
ctx := context.TODO()
|
||||
cur, err := cli.Database(commandForm.Database).Collection(commandForm.Collection).Find(ctx, commandForm.Filter, opts)
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
|
||||
var res []bson.M
|
||||
cur.All(ctx, &res)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) UpdateByIdCommand(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(g))
|
||||
commandForm := new(form.MongoUpdateByIdCommand)
|
||||
ginx.BindJsonAndValid(g, commandForm)
|
||||
|
||||
// 解析docId文档id,如果为string类型则使用ObjectId解析,解析失败则为普通字符串
|
||||
docId := commandForm.DocId
|
||||
docIdVal, ok := docId.(string)
|
||||
if ok {
|
||||
objId, err := primitive.ObjectIDFromHex(docIdVal)
|
||||
if err == nil {
|
||||
docId = objId
|
||||
}
|
||||
}
|
||||
|
||||
res, err := cli.Database(commandForm.Database).Collection(commandForm.Collection).UpdateByID(context.TODO(), docId, commandForm.Update)
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
|
||||
rc.ReqParam = commandForm
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) DeleteByIdCommand(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(g))
|
||||
commandForm := new(form.MongoUpdateByIdCommand)
|
||||
ginx.BindJsonAndValid(g, commandForm)
|
||||
|
||||
// 解析docId文档id,如果为string类型则使用ObjectId解析,解析失败则为普通字符串
|
||||
docId := commandForm.DocId
|
||||
docIdVal, ok := docId.(string)
|
||||
if ok {
|
||||
objId, err := primitive.ObjectIDFromHex(docIdVal)
|
||||
if err == nil {
|
||||
docId = objId
|
||||
}
|
||||
}
|
||||
|
||||
res, err := cli.Database(commandForm.Database).Collection(commandForm.Collection).DeleteOne(context.TODO(), bson.D{{"_id", docId}})
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
|
||||
rc.ReqParam = commandForm
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *Mongo) InsertOneCommand(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
cli := m.MongoApp.GetMongoCli(m.GetMongoId(g))
|
||||
commandForm := new(form.MongoInsertCommand)
|
||||
ginx.BindJsonAndValid(g, commandForm)
|
||||
|
||||
res, err := cli.Database(commandForm.Database).Collection(commandForm.Collection).InsertOne(context.TODO(), commandForm.Doc)
|
||||
biz.ErrIsNilAppendErr(err, "命令执行失败: %s")
|
||||
|
||||
rc.ReqParam = commandForm
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
// 获取请求路径上的mongo id
|
||||
func (m *Mongo) GetMongoId(g *gin.Context) uint64 {
|
||||
dbId, _ := strconv.Atoi(g.Param("id"))
|
||||
biz.IsTrue(dbId > 0, "mongoId错误")
|
||||
return uint64(dbId)
|
||||
}
|
||||
104
server/internal/devops/api/project.go
Normal file
104
server/internal/devops/api/project.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/api/vo"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
sys_applicaiton "mayfly-go/internal/sys/application"
|
||||
sys_entity "mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
ProjectApp application.Project
|
||||
AccountApp sys_applicaiton.Account
|
||||
}
|
||||
|
||||
// 获取当前登录用户可以访问的项目列表
|
||||
func (p *Project) GetProjectsByLoginAccount(rc *ctx.ReqCtx) {
|
||||
// 获取登录用户拥有的项目ids
|
||||
projectMembers := &[]entity.ProjectMember{}
|
||||
p.ProjectApp.ListMember(&entity.ProjectMember{AccountId: rc.LoginAccount.Id}, projectMembers)
|
||||
var pids []uint64
|
||||
for _, pm := range *projectMembers {
|
||||
pids = append(pids, pm.ProjectId)
|
||||
}
|
||||
|
||||
// 获取项目信息
|
||||
projects := &vo.AccountProjects{}
|
||||
p.ProjectApp.ListProjectByIds(pids, projects)
|
||||
rc.ResData = projects
|
||||
}
|
||||
|
||||
func (p *Project) GetProjects(rc *ctx.ReqCtx) {
|
||||
condition := &entity.Project{}
|
||||
ginx.BindQuery(rc.GinCtx, condition)
|
||||
// condition.Name = rc.GinCtx.Query("name")
|
||||
rc.ResData = p.ProjectApp.GetPageList(condition, ginx.GetPageParam(rc.GinCtx), new([]entity.Project))
|
||||
}
|
||||
|
||||
func (p *Project) SaveProject(rc *ctx.ReqCtx) {
|
||||
project := &entity.Project{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, project)
|
||||
rc.ReqParam = project
|
||||
|
||||
project.SetBaseInfo(rc.LoginAccount)
|
||||
p.ProjectApp.SaveProject(project)
|
||||
}
|
||||
|
||||
func (p *Project) DelProject(rc *ctx.ReqCtx) {
|
||||
p.ProjectApp.DelProject(uint64(ginx.QueryInt(rc.GinCtx, "id", 0)))
|
||||
}
|
||||
|
||||
// 获取项目下的环境信息
|
||||
func (p *Project) GetProjectEnvs(rc *ctx.ReqCtx) {
|
||||
projectEnvs := &[]entity.ProjectEnv{}
|
||||
p.ProjectApp.ListEnvByProjectId(uint64(ginx.PathParamInt(rc.GinCtx, "projectId")), projectEnvs)
|
||||
rc.ResData = projectEnvs
|
||||
}
|
||||
|
||||
//保存项目下的环境信息
|
||||
func (p *Project) SaveProjectEnvs(rc *ctx.ReqCtx) {
|
||||
projectEnv := &entity.ProjectEnv{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, projectEnv)
|
||||
rc.ReqParam = projectEnv
|
||||
|
||||
projectEnv.SetBaseInfo(rc.LoginAccount)
|
||||
p.ProjectApp.SaveProjectEnv(projectEnv)
|
||||
}
|
||||
|
||||
// 获取项目下的成员信息
|
||||
func (p *Project) GetProjectMembers(rc *ctx.ReqCtx) {
|
||||
projectMems := &[]entity.ProjectMember{}
|
||||
rc.ResData = p.ProjectApp.GetMemberPage(&entity.ProjectMember{ProjectId: uint64(ginx.PathParamInt(rc.GinCtx, "projectId"))},
|
||||
ginx.GetPageParam(rc.GinCtx), projectMems)
|
||||
}
|
||||
|
||||
//保存项目的成员信息
|
||||
func (p *Project) SaveProjectMember(rc *ctx.ReqCtx) {
|
||||
projectMem := &entity.ProjectMember{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, projectMem)
|
||||
rc.ReqParam = projectMem
|
||||
|
||||
// 校验账号,并赋值username
|
||||
account := &sys_entity.Account{}
|
||||
account.Id = projectMem.AccountId
|
||||
biz.ErrIsNil(p.AccountApp.GetAccount(account, "Id", "Username"), "账号不存在")
|
||||
projectMem.Username = account.Username
|
||||
|
||||
projectMem.SetBaseInfo(rc.LoginAccount)
|
||||
p.ProjectApp.SaveProjectMember(projectMem)
|
||||
}
|
||||
|
||||
//删除项目成员
|
||||
func (p *Project) DelProjectMember(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
pid := ginx.PathParamInt(g, "projectId")
|
||||
aid := ginx.PathParamInt(g, "accountId")
|
||||
rc.ReqParam = fmt.Sprintf("projectId: %d, accountId: %d", pid, aid)
|
||||
|
||||
p.ProjectApp.DeleteMember(uint64(pid), uint64(aid))
|
||||
}
|
||||
216
server/internal/devops/api/redis.go
Normal file
216
server/internal/devops/api/redis.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/api/form"
|
||||
"mayfly-go/internal/devops/api/vo"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ctx"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Redis struct {
|
||||
RedisApp application.Redis
|
||||
ProjectApp application.Project
|
||||
}
|
||||
|
||||
func (r *Redis) RedisList(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
m := &entity.Redis{EnvId: uint64(ginx.QueryInt(g, "envId", 0)),
|
||||
ProjectId: uint64(ginx.QueryInt(g, "projectId", 0)),
|
||||
}
|
||||
m.CreatorId = rc.LoginAccount.Id
|
||||
rc.ResData = r.RedisApp.GetPageList(m, ginx.GetPageParam(rc.GinCtx), new([]vo.Redis))
|
||||
}
|
||||
|
||||
func (r *Redis) Save(rc *ctx.ReqCtx) {
|
||||
form := &form.Redis{}
|
||||
ginx.BindJsonAndValid(rc.GinCtx, form)
|
||||
|
||||
rc.ReqParam = form
|
||||
|
||||
redis := new(entity.Redis)
|
||||
utils.Copy(redis, form)
|
||||
redis.SetBaseInfo(rc.LoginAccount)
|
||||
r.RedisApp.Save(redis)
|
||||
}
|
||||
|
||||
func (r *Redis) DeleteRedis(rc *ctx.ReqCtx) {
|
||||
r.RedisApp.Delete(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
|
||||
}
|
||||
|
||||
func (r *Redis) RedisInfo(rc *ctx.ReqCtx) {
|
||||
res, _ := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(rc.GinCtx, "id"))).Cli.Info().Result()
|
||||
|
||||
datas := strings.Split(res, "\r\n")
|
||||
i := 0
|
||||
length := len(datas)
|
||||
|
||||
parseMap := make(map[string]map[string]string)
|
||||
for {
|
||||
if i >= length {
|
||||
break
|
||||
}
|
||||
if strings.Contains(datas[i], "#") {
|
||||
key := utils.SubString(datas[i], strings.Index(datas[i], "#")+1, utils.StrLen(datas[i]))
|
||||
i++
|
||||
key = strings.Trim(key, " ")
|
||||
|
||||
sectionMap := make(map[string]string)
|
||||
for {
|
||||
if i >= length || !strings.Contains(datas[i], ":") {
|
||||
break
|
||||
}
|
||||
pair := strings.Split(datas[i], ":")
|
||||
i++
|
||||
if len(pair) != 2 {
|
||||
continue
|
||||
}
|
||||
sectionMap[pair[0]] = pair[1]
|
||||
}
|
||||
parseMap[key] = sectionMap
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
rc.ResData = parseMap
|
||||
}
|
||||
|
||||
// scan获取redis的key列表信息
|
||||
func (r *Redis) Scan(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
|
||||
keys, cursor := ri.Scan(uint64(ginx.PathParamInt(g, "cursor")), g.Query("match"), int64(ginx.PathParamInt(g, "count")))
|
||||
|
||||
var keyInfoSplit []string
|
||||
if len(keys) > 0 {
|
||||
keyInfoLua := `
|
||||
local result = {}
|
||||
-- KEYS[1]为第1个参数,lua数组下标从1开始
|
||||
local ttl = redis.call('ttl', KEYS[1]);
|
||||
local keyType = redis.call('type', KEYS[1]);
|
||||
for i = 1, #KEYS do
|
||||
local ttl = redis.call('ttl', KEYS[i]);
|
||||
local keyType = redis.call('type', KEYS[i]);
|
||||
table.insert(result, string.format("%d,%s", ttl, keyType['ok']));
|
||||
end;
|
||||
return table.concat(result, ".");`
|
||||
// 通过lua获取 ttl,type.ttl2,type2格式,以便下面切割获取ttl和type。避免多次调用ttl和type函数
|
||||
keyInfos, _ := ri.Cli.Eval(keyInfoLua, keys).Result()
|
||||
keyInfoSplit = strings.Split(keyInfos.(string), ".")
|
||||
}
|
||||
|
||||
kis := make([]*vo.KeyInfo, 0)
|
||||
for i, k := range keys {
|
||||
ttlType := strings.Split(keyInfoSplit[i], ",")
|
||||
ttl, _ := strconv.Atoi(ttlType[0])
|
||||
ki := &vo.KeyInfo{Key: k, Type: ttlType[1], Ttl: int64(ttl)}
|
||||
kis = append(kis, ki)
|
||||
}
|
||||
|
||||
size, _ := ri.Cli.DBSize().Result()
|
||||
rc.ResData = &vo.Keys{Cursor: cursor, Keys: kis, DbSize: size}
|
||||
}
|
||||
|
||||
func (r *Redis) DeleteKey(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
key := g.Query("key")
|
||||
biz.NotEmpty(key, "key不能为空")
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
|
||||
rc.ReqParam = key
|
||||
ri.Cli.Del(key)
|
||||
}
|
||||
|
||||
func (r *Redis) checkKey(rc *ctx.ReqCtx) (*application.RedisInstance, string) {
|
||||
g := rc.GinCtx
|
||||
key := g.Query("key")
|
||||
biz.NotEmpty(key, "key不能为空")
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
|
||||
return ri, key
|
||||
}
|
||||
|
||||
func (r *Redis) GetStringValue(rc *ctx.ReqCtx) {
|
||||
ri, key := r.checkKey(rc)
|
||||
str, err := ri.Cli.Get(key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "获取字符串值失败: %s")
|
||||
rc.ResData = str
|
||||
}
|
||||
|
||||
func (r *Redis) GetHashValue(rc *ctx.ReqCtx) {
|
||||
ri, key := r.checkKey(rc)
|
||||
res, err := ri.Cli.HGetAll(key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "获取hash值失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) SetStringValue(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
keyValue := new(form.StringValue)
|
||||
ginx.BindJsonAndValid(g, keyValue)
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
|
||||
str, err := ri.Cli.Set(keyValue.Key, keyValue.Value, time.Second*time.Duration(keyValue.Timed)).Result()
|
||||
biz.ErrIsNilAppendErr(err, "保存字符串值失败: %s")
|
||||
rc.ResData = str
|
||||
}
|
||||
|
||||
func (r *Redis) SetHashValue(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
hashValue := new(form.HashValue)
|
||||
ginx.BindJsonAndValid(g, hashValue)
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
|
||||
key := hashValue.Key
|
||||
// 简单处理->先删除,后新增
|
||||
ri.Cli.Del(key)
|
||||
for _, v := range hashValue.Value {
|
||||
res := ri.Cli.HSet(key, v["key"].(string), v["value"])
|
||||
biz.ErrIsNilAppendErr(res.Err(), "保存hash值失败: %s")
|
||||
}
|
||||
if hashValue.Timed != -1 {
|
||||
ri.Cli.Expire(key, time.Second*time.Duration(hashValue.Timed))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Redis) GetSetValue(rc *ctx.ReqCtx) {
|
||||
ri, key := r.checkKey(rc)
|
||||
res, err := ri.Cli.SMembers(key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "获取set值失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (r *Redis) SetSetValue(rc *ctx.ReqCtx) {
|
||||
g := rc.GinCtx
|
||||
keyvalue := new(form.SetValue)
|
||||
ginx.BindJsonAndValid(g, keyvalue)
|
||||
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")))
|
||||
biz.ErrIsNilAppendErr(r.ProjectApp.CanAccess(rc.LoginAccount.Id, ri.ProjectId), "%s")
|
||||
|
||||
key := keyvalue.Key
|
||||
// 简单处理->先删除,后新增
|
||||
ri.Cli.Del(key)
|
||||
ri.Cli.SAdd(key, keyvalue.Value...)
|
||||
|
||||
if keyvalue.Timed != -1 {
|
||||
ri.Cli.Expire(key, time.Second*time.Duration(keyvalue.Timed))
|
||||
}
|
||||
}
|
||||
21
server/internal/devops/api/vo/db.go
Normal file
21
server/internal/devops/api/vo/db.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package vo
|
||||
|
||||
import "time"
|
||||
|
||||
type SelectDataDbVO struct {
|
||||
//models.BaseModel
|
||||
Id *int64 `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
Host *string `json:"host"`
|
||||
Port *int `json:"port"`
|
||||
Type *string `json:"type"`
|
||||
Database *string `json:"database"`
|
||||
Username *string `json:"username"`
|
||||
ProjectId *int64 `json:"projectId"`
|
||||
Project *string `json:"project"`
|
||||
Env *string `json:"env"`
|
||||
EnvId *int64 `json:"envId"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
}
|
||||
10
server/internal/devops/api/vo/project.go
Normal file
10
server/internal/devops/api/vo/project.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package vo
|
||||
|
||||
// 用户选择项目
|
||||
type AccountProject struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type AccountProjects []AccountProject
|
||||
29
server/internal/devops/api/vo/redis.go
Normal file
29
server/internal/devops/api/vo/redis.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package vo
|
||||
|
||||
import "time"
|
||||
|
||||
type Redis struct {
|
||||
Id *int64 `json:"id"`
|
||||
// Name *string `json:"name"`
|
||||
Host *string `json:"host"`
|
||||
Db int `json:"db"`
|
||||
ProjectId *int64 `json:"projectId"`
|
||||
Project *string `json:"project"`
|
||||
Env *string `json:"env"`
|
||||
EnvId *int64 `json:"envId"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
}
|
||||
|
||||
type Keys struct {
|
||||
Cursor uint64 `json:"cursor"`
|
||||
Keys []*KeyInfo `json:"keys"`
|
||||
DbSize int64 `json:"dbSize"`
|
||||
}
|
||||
|
||||
type KeyInfo struct {
|
||||
Key string `json:"key"`
|
||||
Ttl int64 `json:"ttl"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
64
server/internal/devops/api/vo/vo.go
Normal file
64
server/internal/devops/api/vo/vo.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package vo
|
||||
|
||||
import "time"
|
||||
|
||||
type AccountVO struct {
|
||||
//models.BaseModel
|
||||
Id *int64 `json:"id"`
|
||||
Username *string `json:"username"`
|
||||
CreateTime *string `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
// Role *RoleVO `json:"roles"`
|
||||
//Status int8 `json:"status"`
|
||||
}
|
||||
|
||||
type MachineVO struct {
|
||||
//models.BaseModel
|
||||
Id *uint64 `json:"id"`
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
Name *string `json:"name"`
|
||||
Username *string `json:"username"`
|
||||
Ip *string `json:"ip"`
|
||||
Port *int `json:"port"`
|
||||
Status *int8 `json:"status"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator *string `json:"creator"`
|
||||
CreatorId *int64 `json:"creatorId"`
|
||||
UpdateTime *time.Time `json:"updateTime"`
|
||||
Modifier *string `json:"modifier"`
|
||||
ModifierId *int64 `json:"modifierId"`
|
||||
HasCli bool `json:"hasCli" gorm:"-"`
|
||||
Remark *string `json:"remark"`
|
||||
}
|
||||
|
||||
type MachineScriptVO struct {
|
||||
Id *int64 `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
Script *string `json:"script"`
|
||||
Type *int `json:"type"`
|
||||
Description *string `json:"description"`
|
||||
Params *string `json:"params"`
|
||||
MachineId *uint64 `json:"machineId"`
|
||||
}
|
||||
|
||||
type MachineFileVO struct {
|
||||
Id *int64 `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
Path *string `json:"path"`
|
||||
Type *int `json:"type"`
|
||||
MachineId *uint64 `json:"machineId"`
|
||||
}
|
||||
|
||||
type MachineFileInfo struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Size int64 `json:"size"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type RoleVO struct {
|
||||
Id *int64
|
||||
Name *string
|
||||
}
|
||||
419
server/internal/devops/application/db_app.go
Normal file
419
server/internal/devops/application/db_app.go
Normal file
@@ -0,0 +1,419 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/internal/devops/infrastructure/persistence"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Db interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Db) int64
|
||||
|
||||
// 根据条件获取
|
||||
GetDbBy(condition *entity.Db, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Db
|
||||
|
||||
Save(entity *entity.Db)
|
||||
|
||||
// 删除数据库信息
|
||||
Delete(id uint64)
|
||||
|
||||
// 获取数据库连接实例
|
||||
// @param id 数据库实例id
|
||||
// @param db 数据库
|
||||
GetDbInstance(id uint64, db string) *DbInstance
|
||||
}
|
||||
|
||||
type dbAppImpl struct {
|
||||
dbRepo repository.Db
|
||||
dbSqlRepo repository.DbSql
|
||||
}
|
||||
|
||||
var DbApp Db = &dbAppImpl{
|
||||
dbRepo: persistence.DbDao,
|
||||
dbSqlRepo: persistence.DbSqlDao,
|
||||
}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *dbAppImpl) GetPageList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return d.dbRepo.GetDbList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (d *dbAppImpl) Count(condition *entity.Db) int64 {
|
||||
return d.dbRepo.Count(condition)
|
||||
}
|
||||
|
||||
// 根据条件获取
|
||||
func (d *dbAppImpl) GetDbBy(condition *entity.Db, cols ...string) error {
|
||||
return d.dbRepo.GetDb(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (d *dbAppImpl) GetById(id uint64, cols ...string) *entity.Db {
|
||||
return d.dbRepo.GetById(id, cols...)
|
||||
}
|
||||
|
||||
func (d *dbAppImpl) Save(dbEntity *entity.Db) {
|
||||
// 默认tcp连接
|
||||
dbEntity.Network = "tcp"
|
||||
// 测试连接
|
||||
if dbEntity.Password != "" {
|
||||
TestConnection(*dbEntity)
|
||||
}
|
||||
|
||||
// 查找是否存在该库
|
||||
oldDb := &entity.Db{Host: dbEntity.Host, Port: dbEntity.Port, EnvId: dbEntity.EnvId}
|
||||
err := d.GetDbBy(oldDb)
|
||||
|
||||
if dbEntity.Id == 0 {
|
||||
biz.NotEmpty(dbEntity.Password, "密码不能为空")
|
||||
biz.IsTrue(err != nil, "该数据库实例已存在")
|
||||
d.dbRepo.Insert(dbEntity)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil {
|
||||
biz.IsTrue(oldDb.Id == dbEntity.Id, "该数据库实例已存在")
|
||||
}
|
||||
|
||||
dbId := dbEntity.Id
|
||||
old := d.GetById(dbId)
|
||||
|
||||
var oldDbs []interface{}
|
||||
for _, v := range strings.Split(old.Database, " ") {
|
||||
oldDbs = append(oldDbs, v)
|
||||
}
|
||||
|
||||
var newDbs []interface{}
|
||||
for _, v := range strings.Split(dbEntity.Database, " ") {
|
||||
newDbs = append(newDbs, v)
|
||||
}
|
||||
// 比较新旧数据库列表,需要将移除的数据库相关联的信息删除
|
||||
_, delDb, _ := utils.ArrayCompare(newDbs, oldDbs, func(i1, i2 interface{}) bool {
|
||||
return i1.(string) == i2.(string)
|
||||
})
|
||||
for _, v := range delDb {
|
||||
// 先关闭数据库连接
|
||||
CloseDb(dbEntity.Id, v.(string))
|
||||
// 删除该库关联的所有sql记录
|
||||
d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: dbId, Db: v.(string)})
|
||||
}
|
||||
|
||||
d.dbRepo.Update(dbEntity)
|
||||
|
||||
}
|
||||
|
||||
func (d *dbAppImpl) Delete(id uint64) {
|
||||
db := d.GetById(id)
|
||||
dbs := strings.Split(db.Database, " ")
|
||||
for _, v := range dbs {
|
||||
// 关闭连接
|
||||
CloseDb(id, v)
|
||||
}
|
||||
d.dbRepo.Delete(id)
|
||||
// 删除该库下用户保存的所有sql信息
|
||||
d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: id})
|
||||
}
|
||||
|
||||
var mutex sync.Mutex
|
||||
|
||||
func (da *dbAppImpl) GetDbInstance(id uint64, db string) *DbInstance {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
// Id不为0,则为需要缓存
|
||||
needCache := id != 0
|
||||
if needCache {
|
||||
load, ok := dbCache.Get(GetDbCacheKey(id, db))
|
||||
if ok {
|
||||
return load.(*DbInstance)
|
||||
}
|
||||
}
|
||||
|
||||
d := da.GetById(id)
|
||||
biz.NotNil(d, "数据库信息不存在")
|
||||
biz.IsTrue(strings.Contains(d.Database, db), "未配置该库的操作权限")
|
||||
global.Log.Infof("连接db: %s:%d/%s", d.Host, d.Port, db)
|
||||
|
||||
// 将数据库替换为要访问的数据库,原本数据库为空格拼接的所有库
|
||||
d.Database = db
|
||||
DB, err := sql.Open(d.Type, getDsn(d))
|
||||
biz.ErrIsNil(err, fmt.Sprintf("Open %s failed, err:%v\n", d.Type, err))
|
||||
perr := DB.Ping()
|
||||
if perr != nil {
|
||||
global.Log.Errorf("连接db失败: %s:%d/%s", d.Host, d.Port, db)
|
||||
panic(biz.NewBizErr(fmt.Sprintf("数据库连接失败: %s", perr.Error())))
|
||||
}
|
||||
|
||||
// 最大连接周期,超过时间的连接就close
|
||||
// DB.SetConnMaxLifetime(100 * time.Second)
|
||||
// 设置最大连接数
|
||||
DB.SetMaxOpenConns(2)
|
||||
// 设置闲置连接数
|
||||
DB.SetMaxIdleConns(1)
|
||||
|
||||
cacheKey := GetDbCacheKey(id, db)
|
||||
dbi := &DbInstance{Id: cacheKey, Type: d.Type, ProjectId: d.ProjectId, db: DB}
|
||||
if needCache {
|
||||
dbCache.Put(cacheKey, dbi)
|
||||
}
|
||||
return dbi
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// 客户端连接缓存,30分钟内没有访问则会被关闭, key为数据库实例id:数据库
|
||||
var dbCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
|
||||
WithUpdateAccessTime(true).
|
||||
OnEvicted(func(key interface{}, value interface{}) {
|
||||
global.Log.Info(fmt.Sprintf("删除db连接缓存 id = %s", key))
|
||||
value.(*DbInstance).Close()
|
||||
})
|
||||
|
||||
func GetDbCacheKey(dbId uint64, db string) string {
|
||||
return fmt.Sprintf("%d:%s", dbId, db)
|
||||
}
|
||||
|
||||
func GetDbInstanceByCache(id string) *DbInstance {
|
||||
if load, ok := dbCache.Get(id); ok {
|
||||
return load.(*DbInstance)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestConnection(d entity.Db) {
|
||||
// 验证第一个库是否可以连接即可
|
||||
d.Database = strings.Split(d.Database, " ")[0]
|
||||
DB, err := sql.Open(d.Type, getDsn(&d))
|
||||
biz.ErrIsNil(err, "Open %s failed, err:%v\n", d.Type, err)
|
||||
defer DB.Close()
|
||||
perr := DB.Ping()
|
||||
biz.ErrIsNilAppendErr(perr, "数据库连接失败: %s")
|
||||
}
|
||||
|
||||
// db实例
|
||||
type DbInstance struct {
|
||||
Id string
|
||||
Type string
|
||||
ProjectId uint64
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// 执行查询语句
|
||||
// 依次返回 列名数组,结果map,错误
|
||||
func (d *DbInstance) SelectData(sql string) ([]string, []map[string]string, error) {
|
||||
sql = strings.Trim(sql, " ")
|
||||
isSelect := strings.HasPrefix(sql, "SELECT") || strings.HasPrefix(sql, "select")
|
||||
isShow := strings.HasPrefix(sql, "show")
|
||||
|
||||
if !isSelect && !isShow {
|
||||
return nil, nil, errors.New("该sql非查询语句")
|
||||
}
|
||||
// 没加limit,则默认限制50条
|
||||
if isSelect && !strings.Contains(sql, "limit") && !strings.Contains(sql, "LIMIT") {
|
||||
sql = sql + " LIMIT 50"
|
||||
}
|
||||
|
||||
rows, err := d.db.Query(sql)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
|
||||
// 后面的链接过来直接报错或拒绝,实际上也没有起效果
|
||||
defer func() {
|
||||
if rows != nil {
|
||||
rows.Close()
|
||||
}
|
||||
}()
|
||||
cols, _ := rows.Columns()
|
||||
// 这里表示一行填充数据
|
||||
scans := make([]interface{}, len(cols))
|
||||
// 这里表示一行所有列的值,用[]byte表示
|
||||
vals := make([][]byte, len(cols))
|
||||
// 这里scans引用vals,把数据填充到[]byte里
|
||||
for k := range vals {
|
||||
scans[k] = &vals[k]
|
||||
}
|
||||
|
||||
result := make([]map[string]string, 0)
|
||||
// 列名
|
||||
colNames := make([]string, 0)
|
||||
// 是否第一次遍历,列名数组只需第一次遍历时加入
|
||||
isFirst := true
|
||||
for rows.Next() {
|
||||
// 不Scan也会导致等待,该链接实际处于未工作的状态,然后也会导致连接数迅速达到最大
|
||||
err := rows.Scan(scans...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// 每行数据
|
||||
rowData := make(map[string]string)
|
||||
// 把vals中的数据复制到row中
|
||||
for k, v := range vals {
|
||||
key := cols[k]
|
||||
// 如果是密码字段,则脱敏显示
|
||||
if key == "password" {
|
||||
v = []byte("******")
|
||||
}
|
||||
if isFirst {
|
||||
colNames = append(colNames, key)
|
||||
}
|
||||
// 这里把[]byte数据转成string
|
||||
rowData[key] = string(v)
|
||||
}
|
||||
//放入结果集
|
||||
result = append(result, rowData)
|
||||
isFirst = false
|
||||
}
|
||||
return colNames, result, nil
|
||||
}
|
||||
|
||||
// 执行 update, insert, delete,建表等sql
|
||||
// 返回影响条数和错误
|
||||
func (d *DbInstance) Exec(sql string) (int64, error) {
|
||||
res, err := d.db.Exec(sql)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
// 关闭连接
|
||||
func (d *DbInstance) Close() {
|
||||
d.db.Close()
|
||||
}
|
||||
|
||||
// 获取dataSourceName
|
||||
func getDsn(d *entity.Db) string {
|
||||
if d.Type == "mysql" {
|
||||
return fmt.Sprintf("%s:%s@%s(%s:%d)/%s?timeout=8s", d.Username, d.Password, d.Network, d.Host, d.Port, d.Database)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 删除db缓存并关闭该数据库所有连接
|
||||
func CloseDb(dbId uint64, db string) {
|
||||
dbCache.Delete(GetDbCacheKey(dbId, db))
|
||||
}
|
||||
|
||||
//-----------------------------------元数据-------------------------------------------
|
||||
|
||||
const (
|
||||
// mysql 表信息元数据
|
||||
MYSQL_TABLE_MA = `SELECT table_name tableName, engine, table_comment tableComment,
|
||||
create_time createTime from information_schema.tables
|
||||
WHERE table_schema = (SELECT database())`
|
||||
|
||||
// mysql 表信息
|
||||
MYSQL_TABLE_INFO = `SELECT table_name tableName, table_comment tableComment, table_rows tableRows,
|
||||
data_length dataLength, index_length indexLength, create_time createTime
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = (SELECT database())`
|
||||
|
||||
// mysql 索引信息
|
||||
MYSQL_INDEX_INFO = `SELECT index_name indexName, column_name columnName, index_type indexType,
|
||||
SEQ_IN_INDEX seqInIndex, INDEX_COMMENT indexComment
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE table_schema = (SELECT database()) AND table_name = '%s'`
|
||||
|
||||
// 默认每次查询列元信息数量
|
||||
DEFAULT_COLUMN_SIZE = 2000
|
||||
|
||||
// mysql 列信息元数据
|
||||
MYSQL_COLOUMN_MA = `SELECT table_name tableName, column_name columnName, column_type columnType,
|
||||
column_comment columnComment, column_key columnKey, extra, is_nullable nullable from information_schema.columns
|
||||
WHERE table_name in (%s) AND table_schema = (SELECT database()) ORDER BY tableName, ordinal_position limit %d, %d`
|
||||
|
||||
// mysql 列信息元数据总数
|
||||
MYSQL_COLOUMN_MA_COUNT = `SELECT COUNT(*) maNum from information_schema.columns
|
||||
WHERE table_name in (%s) AND table_schema = (SELECT database())`
|
||||
)
|
||||
|
||||
func (d *DbInstance) GetTableMetedatas() []map[string]string {
|
||||
var sql string
|
||||
if d.Type == "mysql" {
|
||||
sql = MYSQL_TABLE_MA
|
||||
}
|
||||
_, res, _ := d.SelectData(sql)
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DbInstance) GetColumnMetadatas(tableNames ...string) []map[string]string {
|
||||
var sql, tableName string
|
||||
for i := 0; i < len(tableNames); i++ {
|
||||
if i != 0 {
|
||||
tableName = tableName + ", "
|
||||
}
|
||||
tableName = tableName + "'" + tableNames[i] + "'"
|
||||
}
|
||||
|
||||
var countSqlTmp string
|
||||
var sqlTmp string
|
||||
if d.Type == "mysql" {
|
||||
countSqlTmp = MYSQL_COLOUMN_MA_COUNT
|
||||
sqlTmp = MYSQL_COLOUMN_MA
|
||||
}
|
||||
|
||||
countSql := fmt.Sprintf(countSqlTmp, tableName)
|
||||
_, countRes, _ := d.SelectData(countSql)
|
||||
// 查询出所有列信息总数,手动分页获取所有数据
|
||||
maCount, _ := strconv.Atoi(countRes[0]["maNum"])
|
||||
// 计算需要查询的页数
|
||||
pageNum := maCount / DEFAULT_COLUMN_SIZE
|
||||
if maCount%DEFAULT_COLUMN_SIZE > 0 {
|
||||
pageNum++
|
||||
}
|
||||
|
||||
res := make([]map[string]string, 0)
|
||||
for index := 0; index < pageNum; index++ {
|
||||
sql = fmt.Sprintf(sqlTmp, tableName, index*DEFAULT_COLUMN_SIZE, DEFAULT_COLUMN_SIZE)
|
||||
_, result, err := d.SelectData(sql)
|
||||
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
||||
res = append(res, result...)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DbInstance) GetTableInfos() []map[string]string {
|
||||
var sql string
|
||||
if d.Type == "mysql" {
|
||||
sql = MYSQL_TABLE_INFO
|
||||
}
|
||||
_, res, _ := d.SelectData(sql)
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DbInstance) GetTableIndex(tableName string) []map[string]string {
|
||||
var sql string
|
||||
if d.Type == "mysql" {
|
||||
sql = fmt.Sprintf(MYSQL_INDEX_INFO, tableName)
|
||||
}
|
||||
_, res, _ := d.SelectData(sql)
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DbInstance) GetCreateTableDdl(tableName string) []map[string]string {
|
||||
var sql string
|
||||
if d.Type == "mysql" {
|
||||
sql = fmt.Sprintf("show create table %s ", tableName)
|
||||
}
|
||||
_, res, _ := d.SelectData(sql)
|
||||
return res
|
||||
}
|
||||
128
server/internal/devops/application/machine_app.go
Normal file
128
server/internal/devops/application/machine_app.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/internal/devops/infrastructure/machine"
|
||||
"mayfly-go/internal/devops/infrastructure/persistence"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Machine interface {
|
||||
// 根据条件获取账号信息
|
||||
GetMachine(condition *entity.Machine, cols ...string) error
|
||||
|
||||
Save(entity *entity.Machine)
|
||||
|
||||
// 调整机器状态
|
||||
ChangeStatus(id uint64, status int8)
|
||||
|
||||
Count(condition *entity.Machine) int64
|
||||
|
||||
Delete(id uint64)
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Machine
|
||||
|
||||
// 分页获取机器信息列表
|
||||
GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
// 获取机器连接
|
||||
GetCli(id uint64) *machine.Cli
|
||||
}
|
||||
|
||||
type machineAppImpl struct {
|
||||
machineRepo repository.Machine
|
||||
}
|
||||
|
||||
var MachineApp Machine = &machineAppImpl{machineRepo: persistence.MachineDao}
|
||||
|
||||
// 分页获取机器信息列表
|
||||
func (m *machineAppImpl) GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return m.machineRepo.GetMachineList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) Count(condition *entity.Machine) int64 {
|
||||
return m.machineRepo.Count(condition)
|
||||
}
|
||||
|
||||
// 根据条件获取机器信息
|
||||
func (m *machineAppImpl) Save(me *entity.Machine) {
|
||||
// ’修改机器信息且密码不为空‘ or ‘新增’需要测试是否可连接
|
||||
if (me.Id != 0 && me.Password != "") || me.Id == 0 {
|
||||
biz.ErrIsNilAppendErr(machine.TestConn(me), "该机器无法连接: %s")
|
||||
}
|
||||
|
||||
oldMachine := &entity.Machine{Ip: me.Ip, Port: me.Port, Username: me.Username}
|
||||
err := m.GetMachine(oldMachine)
|
||||
|
||||
if me.Id != 0 {
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil {
|
||||
biz.IsTrue(oldMachine.Id == me.Id, "该机器信息已存在")
|
||||
}
|
||||
// 关闭连接
|
||||
machine.DeleteCli(me.Id)
|
||||
m.machineRepo.UpdateById(me)
|
||||
} else {
|
||||
biz.IsTrue(err != nil, "该机器信息已存在")
|
||||
// 新增机器,默认启用状态
|
||||
me.Status = entity.MachineStatusEnable
|
||||
m.machineRepo.Create(me)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) ChangeStatus(id uint64, status int8) {
|
||||
if status == entity.MachineStatusDisable {
|
||||
// 关闭连接
|
||||
machine.DeleteCli(id)
|
||||
}
|
||||
machine := new(entity.Machine)
|
||||
machine.Id = id
|
||||
machine.Status = status
|
||||
m.machineRepo.UpdateById(machine)
|
||||
}
|
||||
|
||||
// 根据条件获取机器信息
|
||||
func (m *machineAppImpl) Delete(id uint64) {
|
||||
// 关闭连接
|
||||
machine.DeleteCli(id)
|
||||
model.Tx(
|
||||
func(db *gorm.DB) error {
|
||||
// 删除machine表信息
|
||||
return db.Delete(new(entity.Machine), "id = ?", id).Error
|
||||
},
|
||||
func(db *gorm.DB) error {
|
||||
// 删除machine_file
|
||||
machineFile := &entity.MachineFile{MachineId: id}
|
||||
return db.Where(machineFile).Delete(machineFile).Error
|
||||
},
|
||||
func(db *gorm.DB) error {
|
||||
// 删除machine_script
|
||||
machineScript := &entity.MachineScript{MachineId: id}
|
||||
return db.Where(machineScript).Delete(machineScript).Error
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 根据条件获取机器信息
|
||||
func (m *machineAppImpl) GetMachine(condition *entity.Machine, cols ...string) error {
|
||||
return m.machineRepo.GetMachine(condition, cols...)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) GetById(id uint64, cols ...string) *entity.Machine {
|
||||
return m.machineRepo.GetById(id, cols...)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) GetCli(id uint64) *machine.Cli {
|
||||
cli, err := machine.GetCli(id, func(machineId uint64) *entity.Machine {
|
||||
machine := m.GetById(machineId)
|
||||
biz.IsTrue(machine.Status == entity.MachineStatusEnable, "该机器已被停用")
|
||||
return machine
|
||||
})
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端错误: %s")
|
||||
return cli
|
||||
}
|
||||
174
server/internal/devops/application/machine_file_app.go
Normal file
174
server/internal/devops/application/machine_file_app.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/internal/devops/infrastructure/persistence"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
)
|
||||
|
||||
type MachineFile interface {
|
||||
// 分页获取机器文件信息列表
|
||||
GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
// 根据条件获取
|
||||
GetMachineFile(condition *entity.MachineFile, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.MachineFile
|
||||
|
||||
Save(entity *entity.MachineFile)
|
||||
|
||||
Delete(id uint64)
|
||||
|
||||
/** sftp 相关操作 **/
|
||||
|
||||
// 读取目录
|
||||
ReadDir(fid uint64, path string) []fs.FileInfo
|
||||
|
||||
// 读取文件内容
|
||||
ReadFile(fileId uint64, path string) *sftp.File
|
||||
|
||||
// 写文件
|
||||
WriteFileContent(fileId uint64, path string, content []byte)
|
||||
|
||||
// 文件上传
|
||||
UploadFile(fileId uint64, path, filename string, reader io.Reader)
|
||||
|
||||
// 移除文件
|
||||
RemoveFile(fileId uint64, path string)
|
||||
}
|
||||
|
||||
type machineFileAppImpl struct {
|
||||
machineFileRepo repository.MachineFile
|
||||
machineRepo repository.Machine
|
||||
}
|
||||
|
||||
// 实现类单例
|
||||
var MachineFileApp MachineFile = &machineFileAppImpl{
|
||||
machineRepo: persistence.MachineDao,
|
||||
machineFileRepo: persistence.MachineFileDao,
|
||||
}
|
||||
|
||||
// 分页获取机器脚本信息列表
|
||||
func (m *machineFileAppImpl) GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return m.machineFileRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
// 根据条件获取
|
||||
func (m *machineFileAppImpl) GetMachineFile(condition *entity.MachineFile, cols ...string) error {
|
||||
return m.machineFileRepo.GetMachineFile(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (m *machineFileAppImpl) GetById(id uint64, cols ...string) *entity.MachineFile {
|
||||
return m.machineFileRepo.GetById(id, cols...)
|
||||
}
|
||||
|
||||
// 保存机器文件配置
|
||||
func (m *machineFileAppImpl) Save(entity *entity.MachineFile) {
|
||||
biz.NotNil(m.machineRepo.GetById(entity.MachineId, "Name"), "该机器不存在")
|
||||
|
||||
if entity.Id != 0 {
|
||||
model.UpdateById(entity)
|
||||
} else {
|
||||
model.Insert(entity)
|
||||
}
|
||||
}
|
||||
|
||||
// 根据id删除
|
||||
func (m *machineFileAppImpl) Delete(id uint64) {
|
||||
m.machineFileRepo.Delete(id)
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) ReadDir(fid uint64, path string) []fs.FileInfo {
|
||||
path, machineId := m.checkAndReturnPathMid(fid, path)
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path = path + "/"
|
||||
}
|
||||
|
||||
sftpCli := m.getSftpCli(machineId)
|
||||
fis, err := sftpCli.ReadDir(path)
|
||||
biz.ErrIsNilAppendErr(err, "读取目录失败: %s")
|
||||
return fis
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) ReadFile(fileId uint64, path string) *sftp.File {
|
||||
path, machineId := m.checkAndReturnPathMid(fileId, path)
|
||||
sftpCli := m.getSftpCli(machineId)
|
||||
// 读取文件内容
|
||||
fc, err := sftpCli.Open(path)
|
||||
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
|
||||
return fc
|
||||
}
|
||||
|
||||
// 写文件内容
|
||||
func (m *machineFileAppImpl) WriteFileContent(fileId uint64, path string, content []byte) {
|
||||
_, machineId := m.checkAndReturnPathMid(fileId, path)
|
||||
|
||||
sftpCli := m.getSftpCli(machineId)
|
||||
f, err := sftpCli.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE|os.O_RDWR)
|
||||
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
|
||||
defer f.Close()
|
||||
|
||||
fi, _ := f.Stat()
|
||||
biz.IsTrue(!fi.IsDir(), "该路径不是文件")
|
||||
f.Write(content)
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
func (m *machineFileAppImpl) UploadFile(fileId uint64, path, filename string, reader io.Reader) {
|
||||
path, machineId := m.checkAndReturnPathMid(fileId, path)
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path = path + "/"
|
||||
}
|
||||
|
||||
sftpCli := m.getSftpCli(machineId)
|
||||
createfile, err := sftpCli.Create(path + filename)
|
||||
biz.ErrIsNilAppendErr(err, "创建文件失败: %s")
|
||||
defer createfile.Close()
|
||||
|
||||
io.Copy(createfile, reader)
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
func (m *machineFileAppImpl) RemoveFile(fileId uint64, path string) {
|
||||
path, machineId := m.checkAndReturnPathMid(fileId, path)
|
||||
|
||||
sftpCli := m.getSftpCli(machineId)
|
||||
file, err := sftpCli.Open(path)
|
||||
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
|
||||
fi, _ := file.Stat()
|
||||
if fi.IsDir() {
|
||||
err = sftpCli.RemoveDirectory(path)
|
||||
} else {
|
||||
err = sftpCli.Remove(path)
|
||||
}
|
||||
biz.ErrIsNilAppendErr(err, "删除文件失败: %s")
|
||||
}
|
||||
|
||||
// 获取sftp client
|
||||
func (m *machineFileAppImpl) getSftpCli(machineId uint64) *sftp.Client {
|
||||
return MachineApp.GetCli(machineId).GetSftpCli()
|
||||
}
|
||||
|
||||
// 校验并返回实际可访问的文件path
|
||||
func (m *machineFileAppImpl) checkAndReturnPathMid(fid uint64, inputPath string) (string, uint64) {
|
||||
biz.IsTrue(fid != 0, "文件id不能为空")
|
||||
mf := m.GetById(uint64(fid))
|
||||
biz.NotNil(mf, "文件不存在")
|
||||
if inputPath != "" {
|
||||
// 接口传入的地址需为配置路径的子路径
|
||||
biz.IsTrue(strings.HasPrefix(inputPath, mf.Path), "无权访问该目录或文件")
|
||||
return inputPath, mf.MachineId
|
||||
} else {
|
||||
return mf.Path, mf.MachineId
|
||||
}
|
||||
}
|
||||
70
server/internal/devops/application/machine_script_app.go
Normal file
70
server/internal/devops/application/machine_script_app.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/internal/devops/infrastructure/persistence"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type MachineScript interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
// 根据条件获取
|
||||
GetMachineScript(condition *entity.MachineScript, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.MachineScript
|
||||
|
||||
Save(entity *entity.MachineScript)
|
||||
|
||||
Delete(id uint64)
|
||||
}
|
||||
|
||||
type machineScriptAppImpl struct {
|
||||
machineScriptRepo repository.MachineScript
|
||||
machineRepo repository.Machine
|
||||
}
|
||||
|
||||
const Common_Script_Machine_Id = 9999999
|
||||
|
||||
// 实现类单例
|
||||
var MachineScriptApp MachineScript = &machineScriptAppImpl{
|
||||
machineRepo: persistence.MachineDao,
|
||||
machineScriptRepo: persistence.MachineScriptDao}
|
||||
|
||||
// 分页获取机器脚本信息列表
|
||||
func (m *machineScriptAppImpl) GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return m.machineScriptRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
// 根据条件获取
|
||||
func (m *machineScriptAppImpl) GetMachineScript(condition *entity.MachineScript, cols ...string) error {
|
||||
return m.machineScriptRepo.GetMachineScript(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (m *machineScriptAppImpl) GetById(id uint64, cols ...string) *entity.MachineScript {
|
||||
return m.machineScriptRepo.GetById(id, cols...)
|
||||
}
|
||||
|
||||
// 保存机器脚本
|
||||
func (m *machineScriptAppImpl) Save(entity *entity.MachineScript) {
|
||||
// 如果机器id不为公共脚本id,则校验机器是否存在
|
||||
if machineId := entity.MachineId; machineId != Common_Script_Machine_Id {
|
||||
biz.NotNil(m.machineRepo.GetById(machineId, "Name"), "该机器不存在")
|
||||
}
|
||||
|
||||
if entity.Id != 0 {
|
||||
model.UpdateById(entity)
|
||||
} else {
|
||||
model.Insert(entity)
|
||||
}
|
||||
}
|
||||
|
||||
// 根据id删除
|
||||
func (m *machineScriptAppImpl) Delete(id uint64) {
|
||||
m.machineScriptRepo.Delete(id)
|
||||
}
|
||||
133
server/internal/devops/application/mongo_app.go
Normal file
133
server/internal/devops/application/mongo_app.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/internal/devops/infrastructure/persistence"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/model"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type Mongo interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Mongo) int64
|
||||
|
||||
// 根据条件获取
|
||||
GetBy(condition *entity.Mongo, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Mongo
|
||||
|
||||
Save(entity *entity.Mongo)
|
||||
|
||||
// 删除数据库信息
|
||||
Delete(id uint64)
|
||||
|
||||
// 获取mongo连接client
|
||||
// @param id mongo id
|
||||
GetMongoCli(id uint64) *mongo.Client
|
||||
}
|
||||
|
||||
type mongoAppImpl struct {
|
||||
mongoRepo repository.Mongo
|
||||
}
|
||||
|
||||
var MongoApp Mongo = &mongoAppImpl{
|
||||
mongoRepo: persistence.MongoDao,
|
||||
}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *mongoAppImpl) GetPageList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return d.mongoRepo.GetList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) Count(condition *entity.Mongo) int64 {
|
||||
return d.mongoRepo.Count(condition)
|
||||
}
|
||||
|
||||
// 根据条件获取
|
||||
func (d *mongoAppImpl) GetBy(condition *entity.Mongo, cols ...string) error {
|
||||
return d.mongoRepo.Get(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (d *mongoAppImpl) GetById(id uint64, cols ...string) *entity.Mongo {
|
||||
return d.mongoRepo.GetById(id, cols...)
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) Delete(id uint64) {
|
||||
d.mongoRepo.Delete(id)
|
||||
DeleteMongoCache(id)
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) Save(m *entity.Mongo) {
|
||||
if m.Id == 0 {
|
||||
d.mongoRepo.Insert(m)
|
||||
} else {
|
||||
// 先关闭连接
|
||||
DeleteMongoCache(m.Id)
|
||||
d.mongoRepo.Update(m)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *mongoAppImpl) GetMongoCli(id uint64) *mongo.Client {
|
||||
cli, err := GetMongoCli(id, func(u uint64) string {
|
||||
mongo := d.GetById(id)
|
||||
biz.NotNil(mongo, "mongo信息不存在")
|
||||
return mongo.Uri
|
||||
})
|
||||
biz.ErrIsNilAppendErr(err, "连接mongo失败: %s")
|
||||
return cli
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
|
||||
//mongo客户端连接缓存,30分钟内没有访问则会被关闭
|
||||
var mongoCliCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
|
||||
WithUpdateAccessTime(true).
|
||||
OnEvicted(func(key interface{}, value interface{}) {
|
||||
global.Log.Info("关闭mongo连接: id = ", key)
|
||||
value.(*mongo.Client).Disconnect(context.TODO())
|
||||
})
|
||||
|
||||
func GetMongoCli(mongoId uint64, getMongoUri func(uint64) string) (*mongo.Client, error) {
|
||||
cli, err := mongoCliCache.ComputeIfAbsent(mongoId, func(key interface{}) (interface{}, error) {
|
||||
c, err := connect(getMongoUri(mongoId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
})
|
||||
|
||||
if cli != nil {
|
||||
return cli.(*mongo.Client), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func DeleteMongoCache(mongoId uint64) {
|
||||
mongoCliCache.Delete(mongoId)
|
||||
}
|
||||
|
||||
// 连接mongo,并返回client
|
||||
func connect(uri string) (*mongo.Client, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri).SetMaxPoolSize(2))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = client.Ping(context.TODO(), nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, err
|
||||
}
|
||||
128
server/internal/devops/application/project_app.go
Normal file
128
server/internal/devops/application/project_app.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/internal/devops/infrastructure/persistence"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Project interface {
|
||||
// 分页获取项目信息列表
|
||||
GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Project) int64
|
||||
|
||||
ListProjectByIds(ids []uint64, toEntity interface{}, orderBy ...string)
|
||||
|
||||
SaveProject(project *entity.Project)
|
||||
|
||||
DelProject(id uint64)
|
||||
|
||||
// 根据项目id获取所有该项目下的环境信息列表
|
||||
ListEnvByProjectId(projectId uint64, listPtr interface{})
|
||||
|
||||
// 保存项目环境信息
|
||||
SaveProjectEnv(projectEnv *entity.ProjectEnv)
|
||||
|
||||
// 根据条件获取项目成员信息
|
||||
ListMember(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string)
|
||||
|
||||
SaveProjectMember(pm *entity.ProjectMember)
|
||||
|
||||
// 根据条件获取项目成员信息
|
||||
GetMemberPage(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
DeleteMember(projectId, accountId uint64)
|
||||
|
||||
// 账号是否有权限访问该项目关联的资源信息
|
||||
CanAccess(accountId, projectId uint64) error
|
||||
}
|
||||
|
||||
type projectAppImpl struct {
|
||||
projectRepo repository.Project
|
||||
projectEnvRepo repository.ProjectEnv
|
||||
projectMemberRepo repository.ProjectMemeber
|
||||
machineRepo repository.Machine
|
||||
redisRepo repository.Redis
|
||||
dbRepo repository.Db
|
||||
}
|
||||
|
||||
var ProjectApp Project = &projectAppImpl{
|
||||
projectRepo: persistence.ProjectRepo,
|
||||
projectEnvRepo: persistence.ProjectEnvRepo,
|
||||
projectMemberRepo: persistence.ProjectMemberRepo,
|
||||
machineRepo: persistence.MachineDao,
|
||||
redisRepo: persistence.RedisDao,
|
||||
dbRepo: persistence.DbDao,
|
||||
}
|
||||
|
||||
// 分页获取项目信息列表
|
||||
func (p *projectAppImpl) GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return p.projectRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) Count(condition *entity.Project) int64 {
|
||||
return p.projectRepo.Count(condition)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) ListProjectByIds(ids []uint64, toEntity interface{}, orderBy ...string) {
|
||||
p.projectRepo.GetByIdIn(ids, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) SaveProject(project *entity.Project) {
|
||||
if project.Id == 0 {
|
||||
p.projectRepo.Save(project)
|
||||
} else {
|
||||
// 防止误传导致项目名更新
|
||||
project.Name = ""
|
||||
p.projectRepo.Update(project)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) DelProject(id uint64) {
|
||||
biz.IsTrue(p.machineRepo.Count(&entity.Machine{ProjectId: id}) == 0, "请先删除该项目关联的机器信息")
|
||||
biz.IsTrue(p.redisRepo.Count(&entity.Redis{ProjectId: id}) == 0, "请先删除该项目关联的redis信息")
|
||||
biz.IsTrue(p.dbRepo.Count(&entity.Db{ProjectId: id}) == 0, "请先删除该项目关联的数据库信息")
|
||||
p.projectRepo.Delete(id)
|
||||
p.projectEnvRepo.DeleteEnvs(id)
|
||||
p.projectMemberRepo.DeleteMems(id)
|
||||
}
|
||||
|
||||
// 根据项目id获取所有该项目下的环境信息列表
|
||||
func (p *projectAppImpl) ListEnvByProjectId(projectId uint64, listPtr interface{}) {
|
||||
p.projectEnvRepo.ListEnv(&entity.ProjectEnv{ProjectId: projectId}, listPtr)
|
||||
}
|
||||
|
||||
// 保存项目环境信息
|
||||
func (p *projectAppImpl) SaveProjectEnv(projectEnv *entity.ProjectEnv) {
|
||||
p.projectEnvRepo.Save(projectEnv)
|
||||
}
|
||||
|
||||
// 根据条件获取项目成员信息
|
||||
func (p *projectAppImpl) ListMember(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string) {
|
||||
p.projectMemberRepo.ListMemeber(condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) SaveProjectMember(pm *entity.ProjectMember) {
|
||||
pms := new([]entity.ProjectMember)
|
||||
p.ListMember(&entity.ProjectMember{ProjectId: pm.ProjectId, AccountId: pm.AccountId}, pms)
|
||||
biz.IsTrue(len(*pms) == 0, "该成员已存在")
|
||||
p.projectMemberRepo.Save(pm)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) GetMemberPage(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return p.projectMemberRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) DeleteMember(projectId, accountId uint64) {
|
||||
p.projectMemberRepo.DeleteByPidMid(projectId, accountId)
|
||||
}
|
||||
|
||||
func (p *projectAppImpl) CanAccess(accountId, projectId uint64) error {
|
||||
if p.projectMemberRepo.IsExist(projectId, accountId) {
|
||||
return nil
|
||||
}
|
||||
return biz.NewBizErr("您无权操作该资源")
|
||||
}
|
||||
161
server/internal/devops/application/redis_app.go
Normal file
161
server/internal/devops/application/redis_app.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/internal/devops/infrastructure/persistence"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/model"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
type Redis interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Redis) int64
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Redis
|
||||
|
||||
// 根据条件获取
|
||||
GetRedisBy(condition *entity.Redis, cols ...string) error
|
||||
|
||||
Save(entity *entity.Redis)
|
||||
|
||||
// 删除数据库信息
|
||||
Delete(id uint64)
|
||||
|
||||
// 获取数据库连接实例
|
||||
GetRedisInstance(id uint64) *RedisInstance
|
||||
}
|
||||
|
||||
type redisAppImpl struct {
|
||||
redisRepo repository.Redis
|
||||
}
|
||||
|
||||
var RedisApp Redis = &redisAppImpl{
|
||||
redisRepo: persistence.RedisDao,
|
||||
}
|
||||
|
||||
// 分页获取机器脚本信息列表
|
||||
func (r *redisAppImpl) GetPageList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return r.redisRepo.GetRedisList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (r *redisAppImpl) Count(condition *entity.Redis) int64 {
|
||||
return r.redisRepo.Count(condition)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (r *redisAppImpl) GetById(id uint64, cols ...string) *entity.Redis {
|
||||
return r.redisRepo.GetById(id, cols...)
|
||||
}
|
||||
|
||||
// 根据条件获取
|
||||
func (r *redisAppImpl) GetRedisBy(condition *entity.Redis, cols ...string) error {
|
||||
return r.redisRepo.GetRedis(condition, cols...)
|
||||
}
|
||||
|
||||
func (r *redisAppImpl) Save(re *entity.Redis) {
|
||||
TestRedisConnection(re)
|
||||
|
||||
// 查找是否存在该库
|
||||
oldRedis := &entity.Redis{Host: re.Host, Db: re.Db}
|
||||
err := r.GetRedisBy(oldRedis)
|
||||
|
||||
if re.Id == 0 {
|
||||
biz.IsTrue(err != nil, "该库已存在")
|
||||
r.redisRepo.Insert(re)
|
||||
} else {
|
||||
// 如果存在该库,则校验修改的库是否为该库
|
||||
if err == nil {
|
||||
biz.IsTrue(oldRedis.Id == re.Id, "该库已存在")
|
||||
}
|
||||
// 先关闭数据库连接
|
||||
CloseRedis(re.Id)
|
||||
r.redisRepo.Update(re)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除Redis信息
|
||||
func (r *redisAppImpl) Delete(id uint64) {
|
||||
CloseRedis(id)
|
||||
r.redisRepo.Delete(id)
|
||||
}
|
||||
|
||||
// 获取数据库连接实例
|
||||
func (r *redisAppImpl) GetRedisInstance(id uint64) *RedisInstance {
|
||||
// Id不为0,则为需要缓存
|
||||
needCache := id != 0
|
||||
if needCache {
|
||||
load, ok := redisCache.Get(id)
|
||||
if ok {
|
||||
return load.(*RedisInstance)
|
||||
}
|
||||
}
|
||||
// 缓存不存在,则回调获取redis信息
|
||||
re := r.GetById(id)
|
||||
biz.NotNil(re, "redis信息不存在")
|
||||
global.Log.Infof("连接redis: %s", re.Host)
|
||||
|
||||
rcli := redis.NewClient(&redis.Options{
|
||||
Addr: re.Host,
|
||||
Password: re.Password, // no password set
|
||||
DB: re.Db, // use default DB
|
||||
})
|
||||
// 测试连接
|
||||
_, e := rcli.Ping().Result()
|
||||
biz.ErrIsNilAppendErr(e, "redis连接失败: %s")
|
||||
|
||||
ri := &RedisInstance{Id: id, ProjectId: re.ProjectId, Cli: rcli}
|
||||
if needCache {
|
||||
redisCache.Put(re.Id, ri)
|
||||
}
|
||||
return ri
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// redis客户端连接缓存,30分钟内没有访问则会被关闭
|
||||
var redisCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
|
||||
WithUpdateAccessTime(true).
|
||||
OnEvicted(func(key interface{}, value interface{}) {
|
||||
global.Log.Info(fmt.Sprintf("删除redis连接缓存 id = %d", key))
|
||||
value.(*RedisInstance).Cli.Close()
|
||||
})
|
||||
|
||||
// redis实例
|
||||
type RedisInstance struct {
|
||||
Id uint64
|
||||
ProjectId uint64
|
||||
Cli *redis.Client
|
||||
}
|
||||
|
||||
// 移除redis连接缓存并关闭redis连接
|
||||
func CloseRedis(id uint64) {
|
||||
redisCache.Delete(id)
|
||||
}
|
||||
|
||||
func TestRedisConnection(re *entity.Redis) {
|
||||
rcli := redis.NewClient(&redis.Options{
|
||||
Addr: re.Host,
|
||||
Password: re.Password, // no password set
|
||||
DB: re.Db, // use default DB
|
||||
})
|
||||
defer rcli.Close()
|
||||
// 测试连接
|
||||
_, e := rcli.Ping().Result()
|
||||
biz.ErrIsNilAppendErr(e, "Redis连接失败: %s")
|
||||
}
|
||||
|
||||
func (r *RedisInstance) Scan(cursor uint64, match string, count int64) ([]string, uint64) {
|
||||
keys, newcursor, err := r.Cli.Scan(cursor, match, count).Result()
|
||||
biz.ErrIsNilAppendErr(err, "scan失败: %s")
|
||||
return keys, newcursor
|
||||
}
|
||||
22
server/internal/devops/domain/entity/db.go
Normal file
22
server/internal/devops/domain/entity/db.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Db struct {
|
||||
model.Model
|
||||
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Type string `orm:"column(type)" json:"type"` // 类型,mysql oracle等
|
||||
Host string `orm:"column(host)" json:"host"`
|
||||
Port int `orm:"column(port)" json:"port"`
|
||||
Network string `orm:"column(network)" json:"network"`
|
||||
Username string `orm:"column(username)" json:"username"`
|
||||
Password string `orm:"column(password)" json:"-"`
|
||||
Database string `orm:"column(database)" json:"database"`
|
||||
ProjectId uint64
|
||||
Project string
|
||||
EnvId uint64
|
||||
Env string
|
||||
}
|
||||
15
server/internal/devops/domain/entity/db_sql.go
Normal file
15
server/internal/devops/domain/entity/db_sql.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type DbSql struct {
|
||||
model.Model `orm:"-"`
|
||||
|
||||
DbId uint64 `json:"dbId"`
|
||||
Db string `json:"db"`
|
||||
Type int `json:"type"` // 类型
|
||||
Sql string `json:"sql"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
23
server/internal/devops/domain/entity/machine.go
Normal file
23
server/internal/devops/domain/entity/machine.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
model.Model
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
ProjectName string `json:"projectName"`
|
||||
Name string `json:"name"`
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Username string `json:"username"` // 用户名
|
||||
Password string `json:"-"`
|
||||
Port int `json:"port"` // 端口号
|
||||
Status int8 `json:"status"` // 状态 1:启用;2:停用
|
||||
Remark string `json:"remark"` // 备注
|
||||
}
|
||||
|
||||
const (
|
||||
MachineStatusEnable int8 = 1 // 启用状态
|
||||
MachineStatusDisable int8 = -1 // 禁用状态
|
||||
)
|
||||
13
server/internal/devops/domain/entity/machine_file.go
Normal file
13
server/internal/devops/domain/entity/machine_file.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
type MachineFile struct {
|
||||
model.Model
|
||||
Name string `json:"name"`
|
||||
// 机器id
|
||||
MachineId uint64 `json:"machineId"`
|
||||
Type int `json:"type"`
|
||||
// 路径
|
||||
Path string `json:"path"`
|
||||
}
|
||||
14
server/internal/devops/domain/entity/machine_monitor.go
Normal file
14
server/internal/devops/domain/entity/machine_monitor.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type MachineMonitor struct {
|
||||
Id uint64 `json:"id"`
|
||||
MachineId uint64 `json:"machineId"`
|
||||
CpuRate float32 `json:"cpuRate"`
|
||||
MemRate float32 `json:"memRate"`
|
||||
SysLoad string `json:"sysLoad"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
}
|
||||
13
server/internal/devops/domain/entity/machine_script.go
Normal file
13
server/internal/devops/domain/entity/machine_script.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
type MachineScript struct {
|
||||
model.Model
|
||||
Name string `json:"name"`
|
||||
MachineId uint64 `json:"machineId"` // 机器id
|
||||
Type int `json:"type"`
|
||||
Description string `json:"description"` // 脚本描述
|
||||
Params string `json:"params"` // 参数列表json
|
||||
Script string `json:"script"` // 脚本内容
|
||||
}
|
||||
14
server/internal/devops/domain/entity/mongo.go
Normal file
14
server/internal/devops/domain/entity/mongo.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
type Mongo struct {
|
||||
model.Model
|
||||
|
||||
Name string `orm:"column(name)" json:"name"`
|
||||
Uri string `orm:"column(uri)" json:"uri"`
|
||||
ProjectId uint64 `json:"projectId"`
|
||||
Project string `json:"project"`
|
||||
EnvId uint64 `json:"envId"`
|
||||
Env string `json:"env"`
|
||||
}
|
||||
10
server/internal/devops/domain/entity/project.go
Normal file
10
server/internal/devops/domain/entity/project.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
// 项目
|
||||
type Project struct {
|
||||
model.Model
|
||||
Name string `json:"name"` // 项目名
|
||||
Remark string `json:"remark"` // 备注说明
|
||||
}
|
||||
11
server/internal/devops/domain/entity/project_env.go
Normal file
11
server/internal/devops/domain/entity/project_env.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
// 项目环境
|
||||
type ProjectEnv struct {
|
||||
model.Model
|
||||
Name string `json:"name"` // 环境名
|
||||
ProjectId uint64 `json:"projectId"` // 项目id
|
||||
Remark string `json:"remark"` // 备注说明
|
||||
}
|
||||
11
server/internal/devops/domain/entity/project_member.go
Normal file
11
server/internal/devops/domain/entity/project_member.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package entity
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
// 项目成员,用于对项目下组件的访问控制
|
||||
type ProjectMember struct {
|
||||
model.Model
|
||||
AccountId uint64 `json:"accountId"` // 账号
|
||||
Username string `json:"username"` // 账号用户名
|
||||
ProjectId uint64 `json:"projectId"` // 项目id
|
||||
}
|
||||
17
server/internal/devops/domain/entity/redis.go
Normal file
17
server/internal/devops/domain/entity/redis.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Redis struct {
|
||||
model.Model
|
||||
|
||||
Host string `orm:"column(host)" json:"host"`
|
||||
Password string `orm:"column(password)" json:"-"`
|
||||
Db int `orm:"column(database)" json:"db"`
|
||||
ProjectId uint64
|
||||
Project string
|
||||
EnvId uint64
|
||||
Env string
|
||||
}
|
||||
25
server/internal/devops/domain/repository/db.go
Normal file
25
server/internal/devops/domain/repository/db.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Db interface {
|
||||
// 分页获取机器信息列表
|
||||
GetDbList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Db) int64
|
||||
|
||||
// 根据条件获取账号信息
|
||||
GetDb(condition *entity.Db, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Db
|
||||
|
||||
Insert(db *entity.Db)
|
||||
|
||||
Update(db *entity.Db)
|
||||
|
||||
Delete(id uint64)
|
||||
}
|
||||
7
server/internal/devops/domain/repository/db_sql.go
Normal file
7
server/internal/devops/domain/repository/db_sql.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package repository
|
||||
|
||||
import "mayfly-go/internal/devops/domain/entity"
|
||||
|
||||
type DbSql interface {
|
||||
DeleteBy(condition *entity.DbSql)
|
||||
}
|
||||
23
server/internal/devops/domain/repository/machine.go
Normal file
23
server/internal/devops/domain/repository/machine.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Machine interface {
|
||||
// 分页获取机器信息列表
|
||||
GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Machine) int64
|
||||
|
||||
// 根据条件获取账号信息
|
||||
GetMachine(condition *entity.Machine, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Machine
|
||||
|
||||
Create(entity *entity.Machine)
|
||||
|
||||
UpdateById(entity *entity.Machine)
|
||||
}
|
||||
23
server/internal/devops/domain/repository/machine_file.go
Normal file
23
server/internal/devops/domain/repository/machine_file.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type MachineFile interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
// 根据条件获取
|
||||
GetMachineFile(condition *entity.MachineFile, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.MachineFile
|
||||
|
||||
Delete(id uint64)
|
||||
|
||||
Create(entity *entity.MachineFile)
|
||||
|
||||
UpdateById(entity *entity.MachineFile)
|
||||
}
|
||||
23
server/internal/devops/domain/repository/machine_script.go
Normal file
23
server/internal/devops/domain/repository/machine_script.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type MachineScript interface {
|
||||
// 分页获取机器脚本信息列表
|
||||
GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
// 根据条件获取
|
||||
GetMachineScript(condition *entity.MachineScript, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.MachineScript
|
||||
|
||||
Delete(id uint64)
|
||||
|
||||
Create(entity *entity.MachineScript)
|
||||
|
||||
UpdateById(entity *entity.MachineScript)
|
||||
}
|
||||
25
server/internal/devops/domain/repository/mongo.go
Normal file
25
server/internal/devops/domain/repository/mongo.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Mongo interface {
|
||||
// 分页获取列表
|
||||
GetList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Mongo) int64
|
||||
|
||||
// 根据条件获取
|
||||
Get(condition *entity.Mongo, cols ...string) error
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Mongo
|
||||
|
||||
Insert(db *entity.Mongo)
|
||||
|
||||
Update(db *entity.Mongo)
|
||||
|
||||
Delete(id uint64)
|
||||
}
|
||||
20
server/internal/devops/domain/repository/project.go
Normal file
20
server/internal/devops/domain/repository/project.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Project interface {
|
||||
GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Project) int64
|
||||
|
||||
GetByIdIn(ids []uint64, toEntity interface{}, orderBy ...string)
|
||||
|
||||
Save(p *entity.Project)
|
||||
|
||||
Update(project *entity.Project)
|
||||
|
||||
Delete(id uint64)
|
||||
}
|
||||
12
server/internal/devops/domain/repository/project_env.go
Normal file
12
server/internal/devops/domain/repository/project_env.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package repository
|
||||
|
||||
import "mayfly-go/internal/devops/domain/entity"
|
||||
|
||||
type ProjectEnv interface {
|
||||
// 获取项目环境列表
|
||||
ListEnv(condition *entity.ProjectEnv, toEntity interface{}, orderBy ...string)
|
||||
|
||||
Save(entity *entity.ProjectEnv)
|
||||
|
||||
DeleteEnvs(projectId uint64)
|
||||
}
|
||||
24
server/internal/devops/domain/repository/project_member.go
Normal file
24
server/internal/devops/domain/repository/project_member.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type ProjectMemeber interface {
|
||||
|
||||
// 获取项目成员列表
|
||||
ListMemeber(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string)
|
||||
|
||||
Save(mp *entity.ProjectMember)
|
||||
|
||||
GetPageList(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
// 根据成员id和项目id删除关联关系
|
||||
DeleteByPidMid(projectId, accountId uint64)
|
||||
|
||||
DeleteMems(projectId uint64)
|
||||
|
||||
// 是否存在指定的项目成员关联信息
|
||||
IsExist(projectId, accountId uint64) bool
|
||||
}
|
||||
24
server/internal/devops/domain/repository/redis.go
Normal file
24
server/internal/devops/domain/repository/redis.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type Redis interface {
|
||||
// 分页获取机器信息列表
|
||||
GetRedisList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
|
||||
|
||||
Count(condition *entity.Redis) int64
|
||||
|
||||
// 根据id获取
|
||||
GetById(id uint64, cols ...string) *entity.Redis
|
||||
|
||||
GetRedis(condition *entity.Redis, cols ...string) error
|
||||
|
||||
Insert(redis *entity.Redis)
|
||||
|
||||
Update(redis *entity.Redis)
|
||||
|
||||
Delete(id uint64)
|
||||
}
|
||||
186
server/internal/devops/infrastructure/machine/machine.go
Normal file
186
server/internal/devops/infrastructure/machine/machine.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/global"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// 客户端信息
|
||||
type Cli struct {
|
||||
machine *entity.Machine
|
||||
// ssh客户端
|
||||
client *ssh.Client
|
||||
|
||||
sftpClient *sftp.Client
|
||||
}
|
||||
|
||||
// 机器客户端连接缓存,45分钟内没有访问则会被关闭
|
||||
var cliCache = cache.NewTimedCache(45*time.Minute, 5*time.Second).
|
||||
WithUpdateAccessTime(true).
|
||||
OnEvicted(func(key interface{}, value interface{}) {
|
||||
value.(*Cli).Close()
|
||||
})
|
||||
|
||||
// 是否存在指定id的客户端连接
|
||||
func HasCli(machineId uint64) bool {
|
||||
if _, ok := cliCache.Get(machineId); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 删除指定机器客户端,并关闭客户端连接
|
||||
func DeleteCli(id uint64) {
|
||||
cliCache.Delete(id)
|
||||
}
|
||||
|
||||
// 从缓存中获取客户端信息,不存在则回调获取机器信息函数,并新建
|
||||
func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, error) {
|
||||
cli, err := cliCache.ComputeIfAbsent(machineId, func(key interface{}) (interface{}, error) {
|
||||
c, err := newClient(getMachine(machineId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
})
|
||||
|
||||
if cli != nil {
|
||||
return cli.(*Cli), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//根据机器信息创建客户端对象
|
||||
func newClient(machine *entity.Machine) (*Cli, error) {
|
||||
if machine == nil {
|
||||
return nil, errors.New("机器不存在")
|
||||
}
|
||||
|
||||
global.Log.Infof("[%s]机器连接:%s:%d", machine.Name, machine.Ip, machine.Port)
|
||||
cli := new(Cli)
|
||||
cli.machine = machine
|
||||
err := cli.connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
//连接
|
||||
func (c *Cli) connect() error {
|
||||
// 如果已经有client则直接返回
|
||||
if c.client != nil {
|
||||
return nil
|
||||
}
|
||||
m := c.machine
|
||||
config := ssh.ClientConfig{
|
||||
User: m.Username,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(m.Password)},
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
|
||||
sshClient, err := ssh.Dial("tcp", addr, &config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.client = sshClient
|
||||
return nil
|
||||
}
|
||||
|
||||
// 测试连接
|
||||
func TestConn(m *entity.Machine) error {
|
||||
config := ssh.ClientConfig{
|
||||
User: m.Username,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(m.Password)},
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
|
||||
sshClient, err := ssh.Dial("tcp", addr, &config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sshClient.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 关闭client和并从缓存中移除
|
||||
func (c *Cli) Close() {
|
||||
m := c.machine
|
||||
global.Log.Info(fmt.Sprintf("关闭机器客户端连接-> id: %d, name: %s, ip: %s", m.Id, m.Name, m.Ip))
|
||||
if c.client != nil {
|
||||
c.client.Close()
|
||||
c.client = nil
|
||||
}
|
||||
if c.sftpClient != nil {
|
||||
c.sftpClient.Close()
|
||||
c.sftpClient = nil
|
||||
}
|
||||
}
|
||||
|
||||
// 获取sftp client
|
||||
func (c *Cli) GetSftpCli() *sftp.Client {
|
||||
if c.client == nil {
|
||||
if err := c.connect(); err != nil {
|
||||
panic(biz.NewBizErr("连接ssh失败:" + err.Error()))
|
||||
}
|
||||
}
|
||||
sftpclient := c.sftpClient
|
||||
// 如果sftpClient为nil,则连接
|
||||
if sftpclient == nil {
|
||||
sc, serr := sftp.NewClient(c.client)
|
||||
if serr != nil {
|
||||
panic(biz.NewBizErr("获取sftp client失败:" + serr.Error()))
|
||||
}
|
||||
sftpclient = sc
|
||||
c.sftpClient = sftpclient
|
||||
}
|
||||
|
||||
return sftpclient
|
||||
}
|
||||
|
||||
// 获取session
|
||||
func (c *Cli) GetSession() (*ssh.Session, error) {
|
||||
if c.client == nil {
|
||||
if err := c.connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.client.NewSession()
|
||||
}
|
||||
|
||||
//执行shell
|
||||
//@param shell shell脚本命令
|
||||
func (c *Cli) Run(shell string) (*string, error) {
|
||||
session, err := c.GetSession()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
defer session.Close()
|
||||
buf, rerr := session.CombinedOutput(shell)
|
||||
if rerr != nil {
|
||||
return nil, rerr
|
||||
}
|
||||
res := string(buf)
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (c *Cli) GetMachine() *entity.Machine {
|
||||
return c.machine
|
||||
}
|
||||
143
server/internal/devops/infrastructure/machine/machine_test.go
Normal file
143
server/internal/devops/infrastructure/machine/machine_test.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/pkg/utils"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSSH(t *testing.T) {
|
||||
//ssh.ListenAndServe("148.70.36.197")
|
||||
//cli := New("148.70.36.197", "root", "g..91mn#", 22)
|
||||
////output, err := cli.Run("free -h")
|
||||
////fmt.Printf("%v\n%v", output, err)
|
||||
//err := cli.RunTerminal("tail -f /usr/local/java/logs/eatlife-info.log", os.Stdout, os.Stdin)
|
||||
//fmt.Println(err)
|
||||
|
||||
res := "top - 17:14:07 up 5 days, 6:30, 2 users, load average: 0.03, 0.04, 0.05\nTasks: 101 total, 1 running, 100 sleeping, 0 stopped, 0 zombie\n%Cpu(s): 6.2 us, 0.0 sy, 0.0 ni, 93.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st\nKiB Mem : 1882012 total, 73892 free, 770360 used, 1037760 buff/cache\nKiB Swap: 0 total, 0 free, 0 used. 933492 avail Mem"
|
||||
split := strings.Split(res, "\n")
|
||||
//var firstLine string
|
||||
//for i := 0; i < len(split); i++ {
|
||||
// if i == 0 {
|
||||
// val := strings.Split(split[i], "top -")[1]
|
||||
// vals := strings.Split(val, ",")
|
||||
//
|
||||
// }
|
||||
//}
|
||||
firstLine := strings.Split(strings.Split(split[0], "top -")[1], ",")
|
||||
// 17:14:07 up 5 days
|
||||
up := strings.Trim(strings.Split(firstLine[0], "up")[1], " ") + firstLine[1]
|
||||
// 2 users
|
||||
users := strings.Split(strings.Trim(firstLine[2], " "), " ")[0]
|
||||
// load average: 0.03
|
||||
oneMinLa := strings.Trim(strings.Split(strings.Trim(firstLine[3], " "), ":")[1], " ")
|
||||
fiveMinLa := strings.Trim(firstLine[4], " ")
|
||||
fietMinLa := strings.Trim(firstLine[5], " ")
|
||||
fmt.Println(firstLine, up, users, oneMinLa, fiveMinLa, fietMinLa)
|
||||
tasks := Parse(strings.Split(split[1], "Tasks:")[1])
|
||||
cpu := Parse(strings.Split(split[2], "%Cpu(s):")[1])
|
||||
mem := Parse(strings.Split(split[3], "KiB Mem :")[1])
|
||||
fmt.Println(tasks, cpu, mem)
|
||||
}
|
||||
|
||||
func Parse(val string) map[string]string {
|
||||
res := make(map[string]string)
|
||||
vals := strings.Split(val, ",")
|
||||
for i := 0; i < len(vals); i++ {
|
||||
trimData := strings.Trim(vals[i], " ")
|
||||
keyValue := strings.Split(trimData, " ")
|
||||
res[keyValue[1]] = keyValue[0]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func TestTemplateRev(t *testing.T) {
|
||||
temp := "hello my name is {name} hahahaha lihaiba {age} years old {public}"
|
||||
str := "hello my name is hmlhmlhm 慌慌信息 hahahaha lihaiba 15 years old private protected"
|
||||
|
||||
//temp1 := " top - {up}, {users} users, load average: {loadavg}"
|
||||
//str1 := " top - 17:14:07 up 5 days, 6:30, 2 users, load average: 0.03, 0.04, 0.05"
|
||||
|
||||
//taskTemp := "Tasks: {total} total, {running} running, {sleeping} sleeping, {stopped} stopped, {zombie} zombie"
|
||||
//taskVal := "Tasks: 101 total, 1 running, 100 sleeping, 0 stopped, 0 zombie"
|
||||
|
||||
//nameRunne := []rune(str)
|
||||
//index := strings.Index(temp, "{")
|
||||
//ei := strings.Index(temp, "}") + 1
|
||||
//next := temp[ei:]
|
||||
//key := temp[index+1 : ei-1]
|
||||
//value := SubString(str, index, UnicodeIndex(str, next))
|
||||
res := make(map[string]interface{})
|
||||
utils.ReverStrTemplate(temp, str, res)
|
||||
fmt.Println(res)
|
||||
}
|
||||
|
||||
//func ReverStrTemplate(temp, str string, res map[string]string) {
|
||||
// index := UnicodeIndex(temp, "{")
|
||||
// ei := UnicodeIndex(temp, "}") + 1
|
||||
// next := temp[ei:]
|
||||
// nextContain := UnicodeIndex(next, "{")
|
||||
// nextIndexValue := next
|
||||
// if nextContain != -1 {
|
||||
// nextIndexValue = SubString(next, 0, nextContain)
|
||||
// }
|
||||
// key := temp[index+1 : ei-1]
|
||||
// // 如果后面没有内容了,则取字符串的长度即可
|
||||
// var valueLastIndex int
|
||||
// if nextIndexValue == "" {
|
||||
// valueLastIndex = StrLen(str)
|
||||
// } else {
|
||||
// valueLastIndex = UnicodeIndex(str, nextIndexValue)
|
||||
// }
|
||||
// value := SubString(str, index, valueLastIndex)
|
||||
// res[key] = value
|
||||
//
|
||||
// if nextContain != -1 {
|
||||
// ReverStrTemplate(next, SubString(str, UnicodeIndex(str, value)+StrLen(value), StrLen(str)), res)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func StrLen(str string) int {
|
||||
// return len([]rune(str))
|
||||
//}
|
||||
//
|
||||
//func SubString(str string, begin, end int) (substr string) {
|
||||
// // 将字符串的转换成[]rune
|
||||
// rs := []rune(str)
|
||||
// lth := len(rs)
|
||||
//
|
||||
// // 简单的越界判断
|
||||
// if begin < 0 {
|
||||
// begin = 0
|
||||
// }
|
||||
// if begin >= lth {
|
||||
// begin = lth
|
||||
// }
|
||||
// if end > lth {
|
||||
// end = lth
|
||||
// }
|
||||
//
|
||||
// // 返回子串
|
||||
// return string(rs[begin:end])
|
||||
//}
|
||||
//
|
||||
//func UnicodeIndex(str, substr string) int {
|
||||
// // 子串在字符串的字节位置
|
||||
// result := strings.Index(str, substr)
|
||||
// if result >= 0 {
|
||||
// // 获得子串之前的字符串并转换成[]byte
|
||||
// prefix := []byte(str)[0:result]
|
||||
// // 将子串之前的字符串转换成[]rune
|
||||
// rs := []rune(string(prefix))
|
||||
// // 获得子串之前的字符串的长度,便是子串在字符串的字符位置
|
||||
// result = len(rs)
|
||||
// }
|
||||
//
|
||||
// return result
|
||||
//}
|
||||
|
||||
func TestTerminal(t *testing.T) {
|
||||
|
||||
// ioutil.ReadAll(file)
|
||||
}
|
||||
19
server/internal/devops/infrastructure/machine/shell.go
Normal file
19
server/internal/devops/infrastructure/machine/shell.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package machine
|
||||
|
||||
const StatsShell = `
|
||||
cat /proc/uptime
|
||||
echo '-----'
|
||||
/bin/hostname -f
|
||||
echo '-----'
|
||||
cat /proc/loadavg
|
||||
echo '-----'
|
||||
cat /proc/meminfo
|
||||
echo '-----'
|
||||
df -B1
|
||||
echo '-----'
|
||||
/sbin/ip -o addr
|
||||
echo '-----'
|
||||
/bin/cat /proc/net/dev
|
||||
echo '-----'
|
||||
top -b -n 1 | grep Cpu
|
||||
`
|
||||
277
server/internal/devops/infrastructure/machine/stats.go
Normal file
277
server/internal/devops/infrastructure/machine/stats.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FSInfo struct {
|
||||
MountPoint string
|
||||
Used uint64
|
||||
Free uint64
|
||||
}
|
||||
|
||||
type NetIntfInfo struct {
|
||||
IPv4 string
|
||||
IPv6 string
|
||||
Rx uint64
|
||||
Tx uint64
|
||||
}
|
||||
|
||||
type CPUInfo struct {
|
||||
User float32
|
||||
Nice float32
|
||||
System float32
|
||||
Idle float32
|
||||
Iowait float32
|
||||
Irq float32
|
||||
SoftIrq float32
|
||||
Steal float32
|
||||
Guest float32
|
||||
}
|
||||
|
||||
type Stats struct {
|
||||
Uptime string
|
||||
Hostname string
|
||||
Load1 string
|
||||
Load5 string
|
||||
Load10 string
|
||||
RunningProcs string
|
||||
TotalProcs string
|
||||
MemTotal uint64
|
||||
MemFree uint64
|
||||
MemBuffers uint64
|
||||
MemAvailable uint64
|
||||
MemCached uint64
|
||||
SwapTotal uint64
|
||||
SwapFree uint64
|
||||
FSInfos []FSInfo
|
||||
NetIntf map[string]NetIntfInfo
|
||||
CPU CPUInfo // or []CPUInfo to get all the cpu-core's stats?
|
||||
}
|
||||
|
||||
func (c *Cli) GetAllStats() *Stats {
|
||||
res, _ := c.Run(StatsShell)
|
||||
infos := strings.Split(*res, "-----")
|
||||
stats := new(Stats)
|
||||
getUptime(infos[0], stats)
|
||||
getHostname(infos[1], stats)
|
||||
getLoad(infos[2], stats)
|
||||
getMemInfo(infos[3], stats)
|
||||
getFSInfo(infos[4], stats)
|
||||
getInterfaces(infos[5], stats)
|
||||
getInterfaceInfo(infos[6], stats)
|
||||
getCPU(infos[7], stats)
|
||||
return stats
|
||||
}
|
||||
|
||||
func getUptime(uptime string, stats *Stats) (err error) {
|
||||
parts := strings.Fields(uptime)
|
||||
if len(parts) == 2 {
|
||||
var upsecs float64
|
||||
upsecs, err = strconv.ParseFloat(parts[0], 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.Uptime = fmtUptime(time.Duration(upsecs * 1e9))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func fmtUptime(dur time.Duration) string {
|
||||
dur = dur - (dur % time.Second)
|
||||
var days int
|
||||
for dur.Hours() > 24.0 {
|
||||
days++
|
||||
dur -= 24 * time.Hour
|
||||
}
|
||||
s1 := dur.String()
|
||||
s2 := ""
|
||||
if days > 0 {
|
||||
s2 = fmt.Sprintf("%dd ", days)
|
||||
}
|
||||
for _, ch := range s1 {
|
||||
s2 += string(ch)
|
||||
if ch == 'h' || ch == 'm' {
|
||||
s2 += " "
|
||||
}
|
||||
}
|
||||
return s2
|
||||
}
|
||||
|
||||
func getHostname(hostname string, stats *Stats) (err error) {
|
||||
stats.Hostname = strings.TrimSpace(hostname)
|
||||
return
|
||||
}
|
||||
|
||||
func getLoad(loadInfo string, stats *Stats) (err error) {
|
||||
parts := strings.Fields(loadInfo)
|
||||
if len(parts) == 5 {
|
||||
stats.Load1 = parts[0]
|
||||
stats.Load5 = parts[1]
|
||||
stats.Load10 = parts[2]
|
||||
if i := strings.Index(parts[3], "/"); i != -1 {
|
||||
stats.RunningProcs = parts[3][0:i]
|
||||
if i+1 < len(parts[3]) {
|
||||
stats.TotalProcs = parts[3][i+1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getMemInfo(memInfo string, stats *Stats) (err error) {
|
||||
// "/bin/cat /proc/meminfo"
|
||||
scanner := bufio.NewScanner(strings.NewReader(memInfo))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) == 3 {
|
||||
val, err := strconv.ParseUint(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
val *= 1024
|
||||
switch parts[0] {
|
||||
case "MemTotal:":
|
||||
stats.MemTotal = val
|
||||
case "MemFree:":
|
||||
stats.MemFree = val
|
||||
case "Buffers:":
|
||||
stats.MemBuffers = val
|
||||
case "Cached:":
|
||||
stats.MemCached = val
|
||||
case "SwapTotal:":
|
||||
stats.SwapTotal = val
|
||||
case "SwapFree:":
|
||||
stats.SwapFree = val
|
||||
case "MemAvailable:":
|
||||
stats.MemAvailable = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getFSInfo(fsInfo string, stats *Stats) (err error) {
|
||||
// "/bin/df -B1"
|
||||
scanner := bufio.NewScanner(strings.NewReader(fsInfo))
|
||||
flag := 0
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
n := len(parts)
|
||||
dev := n > 0 && strings.Index(parts[0], "/dev/") == 0
|
||||
if n == 1 && dev {
|
||||
flag = 1
|
||||
} else if (n == 5 && flag == 1) || (n == 6 && dev) {
|
||||
i := flag
|
||||
flag = 0
|
||||
used, err := strconv.ParseUint(parts[2-i], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
free, err := strconv.ParseUint(parts[3-i], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
stats.FSInfos = append(stats.FSInfos, FSInfo{
|
||||
parts[5-i], used, free,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getInterfaces(iInfo string, stats *Stats) (err error) {
|
||||
// "/sbin/ip -o addr"
|
||||
if stats.NetIntf == nil {
|
||||
stats.NetIntf = make(map[string]NetIntfInfo)
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(iInfo))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 4 && (parts[2] == "inet" || parts[2] == "inet6") {
|
||||
ipv4 := parts[2] == "inet"
|
||||
intfname := parts[1]
|
||||
if info, ok := stats.NetIntf[intfname]; ok {
|
||||
if ipv4 {
|
||||
info.IPv4 = parts[3]
|
||||
} else {
|
||||
info.IPv6 = parts[3]
|
||||
}
|
||||
stats.NetIntf[intfname] = info
|
||||
} else {
|
||||
info := NetIntfInfo{}
|
||||
if ipv4 {
|
||||
info.IPv4 = parts[3]
|
||||
} else {
|
||||
info.IPv6 = parts[3]
|
||||
}
|
||||
stats.NetIntf[intfname] = info
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getInterfaceInfo(iInfo string, stats *Stats) (err error) {
|
||||
// /bin/cat /proc/net/dev
|
||||
if stats.NetIntf == nil {
|
||||
return
|
||||
} // should have been here already
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(iInfo))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) == 17 {
|
||||
intf := strings.TrimSpace(parts[0])
|
||||
intf = strings.TrimSuffix(intf, ":")
|
||||
if info, ok := stats.NetIntf[intf]; ok {
|
||||
rx, err := strconv.ParseUint(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
tx, err := strconv.ParseUint(parts[9], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
info.Rx = rx
|
||||
info.Tx = tx
|
||||
stats.NetIntf[intf] = info
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getCPU(cpuInfo string, stats *Stats) (err error) {
|
||||
// %Cpu(s): 6.1 us, 3.0 sy, 0.0 ni, 90.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
|
||||
value := strings.Split(cpuInfo, ":")[1]
|
||||
values := strings.Split(value, ",")
|
||||
|
||||
us, _ := strconv.ParseFloat(strings.Split(strings.TrimSpace(values[0]), " ")[0], 32)
|
||||
stats.CPU.User = float32(us)
|
||||
|
||||
sy, _ := strconv.ParseFloat(strings.Split(strings.TrimSpace(values[1]), " ")[0], 32)
|
||||
stats.CPU.System = float32(sy)
|
||||
|
||||
id, _ := strconv.ParseFloat(strings.Split(strings.TrimSpace(values[3]), " ")[0], 32)
|
||||
stats.CPU.Idle = float32(id)
|
||||
|
||||
wa, _ := strconv.ParseFloat(strings.Split(strings.TrimSpace(values[4]), " ")[0], 32)
|
||||
stats.CPU.Iowait = float32(wa)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"mayfly-go/pkg/global"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type safeBuffer struct {
|
||||
buffer bytes.Buffer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (w *safeBuffer) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.buffer.Write(p)
|
||||
}
|
||||
func (w *safeBuffer) Bytes() []byte {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.buffer.Bytes()
|
||||
}
|
||||
func (w *safeBuffer) Reset() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
w.buffer.Reset()
|
||||
}
|
||||
|
||||
const (
|
||||
wsMsgCmd = "cmd"
|
||||
wsMsgResize = "resize"
|
||||
)
|
||||
|
||||
type WsMsg struct {
|
||||
Type string `json:"type"`
|
||||
Msg string `json:"msg"`
|
||||
Cols int `json:"cols"`
|
||||
Rows int `json:"rows"`
|
||||
}
|
||||
|
||||
type LogicSshWsSession struct {
|
||||
stdinPipe io.WriteCloser
|
||||
comboOutput *safeBuffer //ssh 终端混合输出
|
||||
inputFilterBuff *safeBuffer //用来过滤输入的命令和ssh_filter配置对比的
|
||||
session *ssh.Session
|
||||
wsConn *websocket.Conn
|
||||
}
|
||||
|
||||
func NewLogicSshWsSession(cols, rows int, cli *Cli, wsConn *websocket.Conn) (*LogicSshWsSession, error) {
|
||||
sshSession, err := cli.GetSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdinP, err := sshSession.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comboWriter := new(safeBuffer)
|
||||
inputBuf := new(safeBuffer)
|
||||
//ssh.stdout and stderr will write output into comboWriter
|
||||
sshSession.Stdout = comboWriter
|
||||
sshSession.Stderr = comboWriter
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 1, // disable echo
|
||||
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
|
||||
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
|
||||
}
|
||||
// Request pseudo terminal
|
||||
if err := sshSession.RequestPty("xterm", rows, cols, modes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Start remote shell
|
||||
if err := sshSession.Shell(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LogicSshWsSession{
|
||||
stdinPipe: stdinP,
|
||||
comboOutput: comboWriter,
|
||||
inputFilterBuff: inputBuf,
|
||||
session: sshSession,
|
||||
wsConn: wsConn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//Close 关闭
|
||||
func (sws *LogicSshWsSession) Close() {
|
||||
if sws.session != nil {
|
||||
sws.session.Close()
|
||||
}
|
||||
if sws.comboOutput != nil {
|
||||
sws.comboOutput = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) Start(quitChan chan bool) {
|
||||
go sws.receiveWsMsg(quitChan)
|
||||
go sws.sendComboOutput(quitChan)
|
||||
}
|
||||
|
||||
//receiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
|
||||
func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {
|
||||
wsConn := sws.wsConn
|
||||
//tells other go routine quit
|
||||
defer setQuit(exitCh)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return
|
||||
default:
|
||||
//read websocket msg
|
||||
_, wsData, err := wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsCloseError(err, websocket.CloseGoingAway, websocket.CloseNoStatusReceived) {
|
||||
return
|
||||
}
|
||||
global.Log.Error("reading webSocket message failed: ", err)
|
||||
return
|
||||
}
|
||||
//unmashal bytes into struct
|
||||
msgObj := WsMsg{}
|
||||
if err := json.Unmarshal(wsData, &msgObj); err != nil {
|
||||
global.Log.Error("unmarshal websocket message failed:", err)
|
||||
}
|
||||
switch msgObj.Type {
|
||||
case wsMsgResize:
|
||||
//handle xterm.js size change
|
||||
if msgObj.Cols > 0 && msgObj.Rows > 0 {
|
||||
if err := sws.session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {
|
||||
global.Log.Error("ssh pty change windows size failed")
|
||||
}
|
||||
}
|
||||
case wsMsgCmd:
|
||||
sws.sendWebsocketInputCommandToSshSessionStdinPipe([]byte(msgObj.Msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sendWebsocketInputCommandToSshSessionStdinPipe
|
||||
func (sws *LogicSshWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) {
|
||||
if _, err := sws.stdinPipe.Write(cmdBytes); err != nil {
|
||||
global.Log.Error("ws cmd bytes write to ssh.stdin pipe failed")
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) {
|
||||
wsConn := sws.wsConn
|
||||
//todo 优化成一个方法
|
||||
//tells other go routine quit
|
||||
defer setQuit(exitCh)
|
||||
|
||||
//every 120ms write combine output bytes into websocket response
|
||||
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
||||
//for range time.Tick(120 * time.Millisecond){}
|
||||
defer tick.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
if sws.comboOutput == nil {
|
||||
return
|
||||
}
|
||||
bs := sws.comboOutput.Bytes()
|
||||
if len(bs) > 0 {
|
||||
err := wsConn.WriteMessage(websocket.TextMessage, bs)
|
||||
if err != nil {
|
||||
global.Log.Error("ssh sending combo output to webSocket failed")
|
||||
}
|
||||
sws.comboOutput.buffer.Reset()
|
||||
}
|
||||
|
||||
case <-exitCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) Wait(quitChan chan bool) {
|
||||
if err := sws.session.Wait(); err != nil {
|
||||
setQuit(quitChan)
|
||||
}
|
||||
}
|
||||
|
||||
func setQuit(ch chan bool) {
|
||||
ch <- true
|
||||
}
|
||||
67
server/internal/devops/infrastructure/persistence/db_repo.go
Normal file
67
server/internal/devops/infrastructure/persistence/db_repo.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type dbRepo struct{}
|
||||
|
||||
var DbDao repository.Db = &dbRepo{}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *dbRepo) GetDbList(condition *entity.Db, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT d.* FROM t_db d JOIN t_project_member pm ON d.project_id = pm.project_id WHERE 1 = 1 "
|
||||
if condition.CreatorId != 0 {
|
||||
// 使用创建者id模拟项目成员id
|
||||
sql = fmt.Sprintf("%s AND pm.account_id = %d", sql, condition.CreatorId)
|
||||
}
|
||||
if condition.ProjectId != 0 {
|
||||
sql = fmt.Sprintf("%s AND d.project_id = %d", sql, condition.ProjectId)
|
||||
}
|
||||
if condition.EnvId != 0 {
|
||||
sql = fmt.Sprintf("%s AND d.env_id = %d", sql, condition.EnvId)
|
||||
}
|
||||
if condition.Host != "" {
|
||||
sql = sql + " AND d.host LIKE '%" + condition.Host + "%'"
|
||||
}
|
||||
if condition.Database != "" {
|
||||
sql = sql + " AND d.database LIKE '%" + condition.Database + "%'"
|
||||
}
|
||||
sql = sql + " ORDER BY d.create_time DESC"
|
||||
return model.GetPageBySql(sql, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (d *dbRepo) Count(condition *entity.Db) int64 {
|
||||
return model.CountBy(condition)
|
||||
}
|
||||
|
||||
// 根据条件获取账号信息
|
||||
func (d *dbRepo) GetDb(condition *entity.Db, cols ...string) error {
|
||||
return model.GetBy(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (d *dbRepo) GetById(id uint64, cols ...string) *entity.Db {
|
||||
db := new(entity.Db)
|
||||
if err := model.GetById(db, id, cols...); err != nil {
|
||||
return nil
|
||||
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (d *dbRepo) Insert(db *entity.Db) {
|
||||
biz.ErrIsNil(model.Insert(db), "新增数据库信息失败")
|
||||
}
|
||||
|
||||
func (d *dbRepo) Update(db *entity.Db) {
|
||||
biz.ErrIsNil(model.UpdateById(db), "更新数据库信息失败")
|
||||
}
|
||||
|
||||
func (d *dbRepo) Delete(id uint64) {
|
||||
model.DeleteById(new(entity.Db), id)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type dbSqlRepo struct{}
|
||||
|
||||
var DbSqlDao repository.DbSql = &dbSqlRepo{}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *dbSqlRepo) DeleteBy(condition *entity.DbSql) {
|
||||
biz.ErrIsNil(model.DeleteByCondition(condition), "删除sql失败")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type machineFileRepo struct{}
|
||||
|
||||
var MachineFileDao repository.MachineFile = &machineFileRepo{}
|
||||
|
||||
// 分页获取机器文件信息列表
|
||||
func (m *machineFileRepo) GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
// 根据条件获取账号信息
|
||||
func (m *machineFileRepo) GetMachineFile(condition *entity.MachineFile, cols ...string) error {
|
||||
return model.GetBy(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (m *machineFileRepo) GetById(id uint64, cols ...string) *entity.MachineFile {
|
||||
ms := new(entity.MachineFile)
|
||||
if err := model.GetById(ms, id, cols...); err != nil {
|
||||
return nil
|
||||
|
||||
}
|
||||
return ms
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (m *machineFileRepo) Delete(id uint64) {
|
||||
biz.ErrIsNil(model.DeleteById(new(entity.MachineFile), id), "删除失败")
|
||||
}
|
||||
|
||||
func (m *machineFileRepo) Create(entity *entity.MachineFile) {
|
||||
model.Insert(entity)
|
||||
}
|
||||
|
||||
func (m *machineFileRepo) UpdateById(entity *entity.MachineFile) {
|
||||
model.UpdateById(entity)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type machineRepo struct{}
|
||||
|
||||
var MachineDao repository.Machine = &machineRepo{}
|
||||
|
||||
// 分页获取机器信息列表
|
||||
func (m *machineRepo) GetMachineList(condition *entity.Machine, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT m.* FROM t_machine m JOIN t_project_member pm ON m.project_id = pm.project_id WHERE 1 = 1 "
|
||||
if condition.CreatorId != 0 {
|
||||
// 使用创建者id模拟项目成员id
|
||||
sql = fmt.Sprintf("%s AND pm.account_id = %d", sql, condition.CreatorId)
|
||||
}
|
||||
if condition.ProjectId != 0 {
|
||||
sql = fmt.Sprintf("%s AND m.project_id = %d", sql, condition.ProjectId)
|
||||
}
|
||||
if condition.Ip != "" {
|
||||
sql = sql + " AND m.ip LIKE '%" + condition.Ip + "%'"
|
||||
}
|
||||
if condition.Name != "" {
|
||||
sql = sql + " AND m.name LIKE '%" + condition.Name + "%'"
|
||||
}
|
||||
sql = sql + " ORDER BY m.project_id, m.create_time DESC"
|
||||
return model.GetPageBySql(sql, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (m *machineRepo) Count(condition *entity.Machine) int64 {
|
||||
return model.CountBy(condition)
|
||||
}
|
||||
|
||||
// 根据条件获取账号信息
|
||||
func (m *machineRepo) GetMachine(condition *entity.Machine, cols ...string) error {
|
||||
return model.GetBy(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (m *machineRepo) GetById(id uint64, cols ...string) *entity.Machine {
|
||||
machine := new(entity.Machine)
|
||||
if err := model.GetById(machine, id, cols...); err != nil {
|
||||
return nil
|
||||
|
||||
}
|
||||
return machine
|
||||
}
|
||||
|
||||
func (m *machineRepo) Create(entity *entity.Machine) {
|
||||
model.Insert(entity)
|
||||
}
|
||||
|
||||
func (m *machineRepo) UpdateById(entity *entity.Machine) {
|
||||
model.UpdateById(entity)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type machineScriptRepo struct{}
|
||||
|
||||
var MachineScriptDao repository.MachineScript = &machineScriptRepo{}
|
||||
|
||||
// 分页获取机器信息列表
|
||||
func (m *machineScriptRepo) GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
// 根据条件获取账号信息
|
||||
func (m *machineScriptRepo) GetMachineScript(condition *entity.MachineScript, cols ...string) error {
|
||||
return model.GetBy(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (m *machineScriptRepo) GetById(id uint64, cols ...string) *entity.MachineScript {
|
||||
ms := new(entity.MachineScript)
|
||||
if err := model.GetById(ms, id, cols...); err != nil {
|
||||
return nil
|
||||
|
||||
}
|
||||
return ms
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (m *machineScriptRepo) Delete(id uint64) {
|
||||
biz.ErrIsNil(model.DeleteById(new(entity.MachineScript), id), "删除失败")
|
||||
}
|
||||
|
||||
func (m *machineScriptRepo) Create(entity *entity.MachineScript) {
|
||||
model.Insert(entity)
|
||||
}
|
||||
|
||||
func (m *machineScriptRepo) UpdateById(entity *entity.MachineScript) {
|
||||
model.UpdateById(entity)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type mongoRepo struct{}
|
||||
|
||||
var MongoDao repository.Mongo = &mongoRepo{}
|
||||
|
||||
// 分页获取数据库信息列表
|
||||
func (d *mongoRepo) GetList(condition *entity.Mongo, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT d.* FROM t_mongo d JOIN t_project_member pm ON d.project_id = pm.project_id WHERE 1 = 1 "
|
||||
if condition.CreatorId != 0 {
|
||||
// 使用创建者id模拟项目成员id
|
||||
sql = fmt.Sprintf("%s AND pm.account_id = %d", sql, condition.CreatorId)
|
||||
}
|
||||
if condition.ProjectId != 0 {
|
||||
sql = fmt.Sprintf("%s AND d.project_id = %d", sql, condition.ProjectId)
|
||||
}
|
||||
if condition.EnvId != 0 {
|
||||
sql = fmt.Sprintf("%s AND d.env_id = %d", sql, condition.EnvId)
|
||||
}
|
||||
sql = sql + " ORDER BY d.create_time DESC"
|
||||
return model.GetPageBySql(sql, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (d *mongoRepo) Count(condition *entity.Mongo) int64 {
|
||||
return model.CountBy(condition)
|
||||
}
|
||||
|
||||
// 根据条件获取
|
||||
func (d *mongoRepo) Get(condition *entity.Mongo, cols ...string) error {
|
||||
return model.GetBy(condition, cols...)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (d *mongoRepo) GetById(id uint64, cols ...string) *entity.Mongo {
|
||||
db := new(entity.Mongo)
|
||||
if err := model.GetById(db, id, cols...); err != nil {
|
||||
return nil
|
||||
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (d *mongoRepo) Insert(db *entity.Mongo) {
|
||||
biz.ErrIsNil(model.Insert(db), "新增mongo信息失败")
|
||||
}
|
||||
|
||||
func (d *mongoRepo) Update(db *entity.Mongo) {
|
||||
biz.ErrIsNil(model.UpdateById(db), "更新mongo信息失败")
|
||||
}
|
||||
|
||||
func (d *mongoRepo) Delete(id uint64) {
|
||||
model.DeleteById(new(entity.Mongo), id)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type projectEnvRepo struct{}
|
||||
|
||||
var ProjectEnvRepo repository.ProjectEnv = &projectEnvRepo{}
|
||||
|
||||
func (p *projectEnvRepo) ListEnv(condition *entity.ProjectEnv, toEntity interface{}, orderBy ...string) {
|
||||
model.ListByOrder(condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectEnvRepo) Save(entity *entity.ProjectEnv) {
|
||||
biz.ErrIsNilAppendErr(model.Insert(entity), "保存环境失败:%s")
|
||||
}
|
||||
|
||||
func (p *projectEnvRepo) DeleteEnvs(projectId uint64) {
|
||||
model.DeleteByCondition(&entity.ProjectEnv{ProjectId: projectId})
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type projectMemeberRepo struct{}
|
||||
|
||||
var ProjectMemberRepo repository.ProjectMemeber = &projectMemeberRepo{}
|
||||
|
||||
func (p *projectMemeberRepo) ListMemeber(condition *entity.ProjectMember, toEntity interface{}, orderBy ...string) {
|
||||
model.ListByOrder(condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectMemeberRepo) Save(pm *entity.ProjectMember) {
|
||||
biz.ErrIsNilAppendErr(model.Insert(pm), "保存项目成员失败:%s")
|
||||
}
|
||||
|
||||
func (p *projectMemeberRepo) GetPageList(condition *entity.ProjectMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectMemeberRepo) DeleteByPidMid(projectId, accountId uint64) {
|
||||
model.DeleteByCondition(&entity.ProjectMember{ProjectId: projectId, AccountId: accountId})
|
||||
}
|
||||
|
||||
func (p *projectMemeberRepo) DeleteMems(projectId uint64) {
|
||||
model.DeleteByCondition(&entity.ProjectMember{ProjectId: projectId})
|
||||
}
|
||||
|
||||
func (p *projectMemeberRepo) IsExist(projectId, accountId uint64) bool {
|
||||
return model.CountBy(&entity.ProjectMember{ProjectId: projectId, AccountId: accountId}) > 0
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type projectRepo struct{}
|
||||
|
||||
var ProjectRepo repository.Project = &projectRepo{}
|
||||
|
||||
func (p *projectRepo) GetPageList(condition *entity.Project, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
return model.GetPage(pageParam, condition, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectRepo) Count(condition *entity.Project) int64 {
|
||||
return model.CountBy(condition)
|
||||
}
|
||||
|
||||
func (p *projectRepo) GetByIdIn(ids []uint64, toEntity interface{}, orderBy ...string) {
|
||||
model.GetByIdIn(new(entity.Project), toEntity, ids, orderBy...)
|
||||
}
|
||||
|
||||
func (p *projectRepo) Save(project *entity.Project) {
|
||||
biz.ErrIsNil(model.Insert(project), "保存项目失败")
|
||||
}
|
||||
|
||||
func (p *projectRepo) Update(project *entity.Project) {
|
||||
biz.ErrIsNil(model.UpdateById(project), "更新项目信息")
|
||||
}
|
||||
|
||||
func (p *projectRepo) Delete(id uint64) {
|
||||
model.DeleteById(new(entity.Project), id)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/devops/domain/entity"
|
||||
"mayfly-go/internal/devops/domain/repository"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type redisRepo struct{}
|
||||
|
||||
var RedisDao repository.Redis = &redisRepo{}
|
||||
|
||||
// 分页获取机器信息列表
|
||||
func (r *redisRepo) GetRedisList(condition *entity.Redis, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
|
||||
sql := "SELECT d.* FROM t_redis d JOIN t_project_member pm ON d.project_id = pm.project_id WHERE 1 = 1 "
|
||||
if condition.CreatorId != 0 {
|
||||
// 使用创建者id模拟项目成员id
|
||||
sql = fmt.Sprintf("%s AND pm.account_id = %d", sql, condition.CreatorId)
|
||||
}
|
||||
if condition.ProjectId != 0 {
|
||||
sql = fmt.Sprintf("%s AND d.project_id = %d", sql, condition.ProjectId)
|
||||
}
|
||||
if condition.EnvId != 0 {
|
||||
sql = fmt.Sprintf("%s AND d.env_id = %d", sql, condition.EnvId)
|
||||
}
|
||||
if condition.Host != "" {
|
||||
sql = sql + " AND d.host LIKE '%" + condition.Host + "%'"
|
||||
}
|
||||
sql = sql + " ORDER BY d.create_time DESC"
|
||||
return model.GetPageBySql(sql, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (r *redisRepo) Count(condition *entity.Redis) int64 {
|
||||
return model.CountBy(condition)
|
||||
}
|
||||
|
||||
// 根据id获取
|
||||
func (r *redisRepo) GetById(id uint64, cols ...string) *entity.Redis {
|
||||
rd := new(entity.Redis)
|
||||
if err := model.GetById(rd, id, cols...); err != nil {
|
||||
return nil
|
||||
}
|
||||
return rd
|
||||
}
|
||||
|
||||
func (r *redisRepo) GetRedis(condition *entity.Redis, cols ...string) error {
|
||||
return model.GetBy(condition, cols...)
|
||||
}
|
||||
|
||||
func (r *redisRepo) Insert(redis *entity.Redis) {
|
||||
biz.ErrIsNilAppendErr(model.Insert(redis), "新增失败: %s")
|
||||
}
|
||||
|
||||
func (r *redisRepo) Update(redis *entity.Redis) {
|
||||
biz.ErrIsNilAppendErr(model.UpdateById(redis), "更新失败: %s")
|
||||
}
|
||||
|
||||
func (r *redisRepo) Delete(id uint64) {
|
||||
biz.ErrIsNilAppendErr(model.DeleteById(new(entity.Redis), id), "删除失败: %s")
|
||||
}
|
||||
27
server/internal/devops/infrastructure/scheudler/mytask.go
Normal file
27
server/internal/devops/infrastructure/scheudler/mytask.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package scheduler
|
||||
|
||||
func init() {
|
||||
SaveMachineMonitor()
|
||||
}
|
||||
|
||||
func SaveMachineMonitor() {
|
||||
AddFun("@every 60s", func() {
|
||||
// for _, m := range models.GetNeedMonitorMachine() {
|
||||
// m := m
|
||||
// go func() {
|
||||
// cli, err := machine.GetCli(uint64(utils.GetInt4Map(m, "id")))
|
||||
// if err != nil {
|
||||
// mlog.Log.Error("获取客户端失败:", err.Error())
|
||||
// return
|
||||
// }
|
||||
// mm := cli.GetMonitorInfo()
|
||||
// if mm != nil {
|
||||
// err := model.Insert(mm)
|
||||
// if err != nil {
|
||||
// mlog.Log.Error("保存机器监控信息失败: ", err.Error())
|
||||
// }
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
})
|
||||
}
|
||||
33
server/internal/devops/infrastructure/scheudler/scheduler.go
Normal file
33
server/internal/devops/infrastructure/scheudler/scheduler.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/biz"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
var cronService = cron.New()
|
||||
|
||||
func Start() {
|
||||
cronService.Start()
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
cronService.Stop()
|
||||
}
|
||||
|
||||
func Remove(id cron.EntryID) {
|
||||
cronService.Remove(id)
|
||||
}
|
||||
|
||||
func GetCron() *cron.Cron {
|
||||
return cronService
|
||||
}
|
||||
|
||||
func AddFun(spec string, cmd func()) cron.EntryID {
|
||||
id, err := cronService.AddFunc(spec, cmd)
|
||||
if err != nil {
|
||||
panic(biz.NewBizErr("添加任务失败:" + err.Error()))
|
||||
}
|
||||
return id
|
||||
}
|
||||
94
server/internal/devops/router/db.go
Normal file
94
server/internal/devops/router/db.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/api"
|
||||
"mayfly-go/internal/devops/application"
|
||||
sysApplication "mayfly-go/internal/sys/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitDbRouter(router *gin.RouterGroup) {
|
||||
db := router.Group("dbs")
|
||||
{
|
||||
d := &api.Db{
|
||||
DbApp: application.DbApp,
|
||||
MsgApp: sysApplication.MsgApp,
|
||||
ProjectApp: application.ProjectApp,
|
||||
}
|
||||
// 获取所有数据库列表
|
||||
db.GET("", func(c *gin.Context) {
|
||||
rc := ctx.NewReqCtxWithGin(c)
|
||||
rc.Handle(d.Dbs)
|
||||
})
|
||||
|
||||
saveDb := ctx.NewLogInfo("保存数据库信息")
|
||||
db.POST("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(saveDb).
|
||||
Handle(d.Save)
|
||||
})
|
||||
|
||||
deleteDb := ctx.NewLogInfo("删除数据库信息")
|
||||
db.DELETE(":dbId", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(deleteDb).
|
||||
Handle(d.DeleteDb)
|
||||
})
|
||||
|
||||
db.GET(":dbId/t-infos", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.TableInfos)
|
||||
})
|
||||
|
||||
db.GET(":dbId/t-index", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.TableIndex)
|
||||
})
|
||||
|
||||
db.GET(":dbId/t-create-ddl", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.GetCreateTableDdl)
|
||||
})
|
||||
|
||||
// db.GET(":dbId/exec-sql", controllers.SelectData)
|
||||
db.GET(":dbId/exec-sql", func(g *gin.Context) {
|
||||
rc := ctx.NewReqCtxWithGin(g).WithLog(ctx.NewLogInfo("执行Sql语句"))
|
||||
rc.Handle(d.ExecSql)
|
||||
})
|
||||
|
||||
db.POST(":dbId/exec-sql-file", func(g *gin.Context) {
|
||||
rc := ctx.NewReqCtxWithGin(g).WithLog(ctx.NewLogInfo("执行Sql文件"))
|
||||
rc.Handle(d.ExecSqlFile)
|
||||
})
|
||||
|
||||
db.GET(":dbId/t-metadata", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.TableMA)
|
||||
})
|
||||
|
||||
db.GET(":dbId/c-metadata", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.ColumnMA)
|
||||
})
|
||||
|
||||
db.GET(":dbId/hint-tables", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.HintTables)
|
||||
})
|
||||
|
||||
/** db sql相关接口 */
|
||||
|
||||
db.POST(":dbId/sql", func(c *gin.Context) {
|
||||
rc := ctx.NewReqCtxWithGin(c)
|
||||
rc.Handle(d.SaveSql)
|
||||
})
|
||||
|
||||
db.GET(":dbId/sql", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.GetSql)
|
||||
})
|
||||
|
||||
db.DELETE(":dbId/sql", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.DeleteSql)
|
||||
})
|
||||
|
||||
db.GET(":dbId/sql-names", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(d.GetSqlNames)
|
||||
})
|
||||
}
|
||||
}
|
||||
69
server/internal/devops/router/machine.go
Normal file
69
server/internal/devops/router/machine.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/api"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitMachineRouter(router *gin.RouterGroup) {
|
||||
m := &api.Machine{
|
||||
MachineApp: application.MachineApp,
|
||||
ProjectApp: application.ProjectApp,
|
||||
}
|
||||
|
||||
machines := router.Group("machines")
|
||||
{
|
||||
machines.GET("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.Machines)
|
||||
})
|
||||
|
||||
machines.GET(":machineId/stats", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.MachineStats)
|
||||
})
|
||||
|
||||
machines.GET(":machineId/process", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProcess)
|
||||
})
|
||||
|
||||
// 终止进程
|
||||
killProcessL := ctx.NewLogInfo("终止进程")
|
||||
killProcessP := ctx.NewPermission("machine:killprocess")
|
||||
machines.DELETE(":machineId/process", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(killProcessL).
|
||||
WithRequiredPermission(killProcessP).
|
||||
Handle(m.KillProcess)
|
||||
})
|
||||
|
||||
saveMachine := ctx.NewLogInfo("保存机器信息")
|
||||
machines.POST("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(saveMachine).
|
||||
Handle(m.SaveMachine)
|
||||
})
|
||||
|
||||
changeStatus := ctx.NewLogInfo("调整机器状态")
|
||||
machines.PUT(":machineId/:status", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(changeStatus).
|
||||
Handle(m.ChangeStatus)
|
||||
})
|
||||
|
||||
delMachine := ctx.NewLogInfo("删除机器")
|
||||
machines.DELETE(":machineId", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(delMachine).
|
||||
Handle(m.DeleteMachine)
|
||||
})
|
||||
|
||||
closeCli := ctx.NewLogInfo("关闭机器客户端")
|
||||
machines.DELETE(":machineId/close-cli", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(closeCli).Handle(m.CloseCli)
|
||||
})
|
||||
|
||||
machines.GET(":machineId/terminal", m.WsSSH)
|
||||
}
|
||||
}
|
||||
80
server/internal/devops/router/machine_file.go
Normal file
80
server/internal/devops/router/machine_file.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/api"
|
||||
"mayfly-go/internal/devops/application"
|
||||
sysApplication "mayfly-go/internal/sys/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitMachineFileRouter(router *gin.RouterGroup) {
|
||||
machineFile := router.Group("machines")
|
||||
{
|
||||
mf := &api.MachineFile{
|
||||
MachineFileApp: application.MachineFileApp,
|
||||
MachineApp: application.MachineApp,
|
||||
MsgApp: sysApplication.MsgApp,
|
||||
}
|
||||
|
||||
// 获取指定机器文件列表
|
||||
machineFile.GET(":machineId/files", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(mf.MachineFiles)
|
||||
})
|
||||
|
||||
// 新增修改机器文件
|
||||
addFileConf := ctx.NewLogInfo("新增机器文件配置")
|
||||
afcP := ctx.NewPermission("machine:file:add")
|
||||
machineFile.POST(":machineId/files", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(addFileConf).
|
||||
WithRequiredPermission(afcP).
|
||||
Handle(mf.SaveMachineFiles)
|
||||
})
|
||||
|
||||
// 删除机器文件
|
||||
delFileConf := ctx.NewLogInfo("删除机器文件配置")
|
||||
dfcP := ctx.NewPermission("machine:file:del")
|
||||
machineFile.DELETE(":machineId/files/:fileId", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(delFileConf).
|
||||
WithRequiredPermission(dfcP).
|
||||
Handle(mf.DeleteFile)
|
||||
})
|
||||
|
||||
getContent := ctx.NewLogInfo("读取机器文件内容")
|
||||
machineFile.GET(":machineId/files/:fileId/read", func(c *gin.Context) {
|
||||
rc := ctx.NewReqCtxWithGin(c).WithLog(getContent)
|
||||
rc.Handle(mf.ReadFileContent)
|
||||
})
|
||||
|
||||
getDir := ctx.NewLogInfo("读取机器目录")
|
||||
machineFile.GET(":machineId/files/:fileId/read-dir", func(c *gin.Context) {
|
||||
rc := ctx.NewReqCtxWithGin(c).WithLog(getDir)
|
||||
rc.Handle(mf.GetDirEntry)
|
||||
})
|
||||
|
||||
writeFile := ctx.NewLogInfo("写入or下载文件内容")
|
||||
wfP := ctx.NewPermission("machine:file:write")
|
||||
machineFile.POST(":machineId/files/:fileId/write", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(writeFile).
|
||||
WithRequiredPermission(wfP).
|
||||
Handle(mf.WriteFileContent)
|
||||
})
|
||||
|
||||
uploadFile := ctx.NewLogInfo("文件上传")
|
||||
ufP := ctx.NewPermission("machine:file:upload")
|
||||
machineFile.POST(":machineId/files/:fileId/upload", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(uploadFile).
|
||||
WithRequiredPermission(ufP).
|
||||
Handle(mf.UploadFile)
|
||||
})
|
||||
|
||||
removeFile := ctx.NewLogInfo("删除文件or文件夹")
|
||||
rfP := ctx.NewPermission("machine:file:rm")
|
||||
machineFile.DELETE(":machineId/files/:fileId/remove", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(removeFile).
|
||||
WithRequiredPermission(rfP).
|
||||
Handle(mf.RemoveFile)
|
||||
})
|
||||
}
|
||||
}
|
||||
52
server/internal/devops/router/machine_script.go
Normal file
52
server/internal/devops/router/machine_script.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/api"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitMachineScriptRouter(router *gin.RouterGroup) {
|
||||
machines := router.Group("machines")
|
||||
{
|
||||
ms := &api.MachineScript{
|
||||
MachineScriptApp: application.MachineScriptApp,
|
||||
MachineApp: application.MachineApp,
|
||||
ProjectApp: application.ProjectApp,
|
||||
}
|
||||
|
||||
// 获取指定机器脚本列表
|
||||
machines.GET(":machineId/scripts", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(ms.MachineScripts)
|
||||
})
|
||||
|
||||
saveMachienScriptLog := ctx.NewLogInfo("保存脚本")
|
||||
smsP := ctx.NewPermission("machine:script:save")
|
||||
// 保存脚本
|
||||
machines.POST(":machineId/scripts", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(saveMachienScriptLog).
|
||||
WithRequiredPermission(smsP).
|
||||
Handle(ms.SaveMachineScript)
|
||||
})
|
||||
|
||||
deleteLog := ctx.NewLogInfo("删除脚本")
|
||||
dP := ctx.NewPermission("machine:script:del")
|
||||
// 保存脚本
|
||||
machines.DELETE(":machineId/scripts/:scriptId", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(deleteLog).
|
||||
WithRequiredPermission(dP).
|
||||
Handle(ms.DeleteMachineScript)
|
||||
})
|
||||
|
||||
runLog := ctx.NewLogInfo("执行机器脚本")
|
||||
rP := ctx.NewPermission("machine:script:run")
|
||||
// 运行脚本
|
||||
machines.GET(":machineId/scripts/:scriptId/run", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(runLog).
|
||||
WithRequiredPermission(rP).
|
||||
Handle(ms.RunMachineScript)
|
||||
})
|
||||
}
|
||||
}
|
||||
86
server/internal/devops/router/mongo.go
Normal file
86
server/internal/devops/router/mongo.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/api"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitMongoRouter(router *gin.RouterGroup) {
|
||||
m := router.Group("mongos")
|
||||
{
|
||||
ma := &api.Mongo{
|
||||
MongoApp: application.MongoApp,
|
||||
}
|
||||
|
||||
// 获取所有mongo列表
|
||||
m.GET("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.Mongos)
|
||||
})
|
||||
|
||||
saveMongo := ctx.NewLogInfo("保存mongo信息")
|
||||
m.POST("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(saveMongo).
|
||||
Handle(ma.Save)
|
||||
})
|
||||
|
||||
deleteMongo := ctx.NewLogInfo("删除mongo信息")
|
||||
m.DELETE(":id", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(deleteMongo).
|
||||
Handle(ma.DeleteMongo)
|
||||
})
|
||||
|
||||
// 获取mongo下的所有数据库
|
||||
m.GET(":id/databases", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.Databases)
|
||||
})
|
||||
|
||||
// 获取mongo指定库的所有集合
|
||||
m.GET(":id/collections", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.Collections)
|
||||
})
|
||||
|
||||
// 获取mongo runCommand
|
||||
m.POST(":id/run-command", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.RunCommand)
|
||||
})
|
||||
|
||||
// 执行mongo find命令
|
||||
m.POST(":id/command/find", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
Handle(ma.FindCommand)
|
||||
})
|
||||
|
||||
// 执行mongo update by id命令
|
||||
updateDocById := ctx.NewLogInfo("mongo-更新文档")
|
||||
m.POST(":id/command/update-by-id", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(updateDocById).
|
||||
Handle(ma.UpdateByIdCommand)
|
||||
})
|
||||
|
||||
// 执行mongo delete by id命令
|
||||
deleteDoc := ctx.NewLogInfo("mongo-删除文档")
|
||||
m.POST(":id/command/delete-by-id", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(deleteDoc).
|
||||
Handle(ma.DeleteByIdCommand)
|
||||
})
|
||||
|
||||
// 执行mongo insert 命令
|
||||
insertDoc := ctx.NewLogInfo("mongo-新增文档")
|
||||
m.POST(":id/command/insert", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).
|
||||
WithLog(insertDoc).
|
||||
Handle(ma.InsertOneCommand)
|
||||
})
|
||||
}
|
||||
}
|
||||
83
server/internal/devops/router/project.go
Normal file
83
server/internal/devops/router/project.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/api"
|
||||
"mayfly-go/internal/devops/application"
|
||||
sys_applicaiton "mayfly-go/internal/sys/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitProjectRouter(router *gin.RouterGroup) {
|
||||
m := &api.Project{
|
||||
ProjectApp: application.ProjectApp,
|
||||
AccountApp: sys_applicaiton.AccountApp}
|
||||
|
||||
project := router.Group("/projects")
|
||||
{
|
||||
router.GET("/accounts/projects", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProjectsByLoginAccount)
|
||||
})
|
||||
|
||||
// 获取项目列表
|
||||
project.GET("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProjects)
|
||||
})
|
||||
|
||||
saveProjectLog := ctx.NewLogInfo("保存项目信息")
|
||||
savePP := ctx.NewPermission("project:save")
|
||||
// 保存项目下的环境信息
|
||||
project.POST("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(saveProjectLog).
|
||||
WithRequiredPermission(savePP).
|
||||
Handle(m.SaveProject)
|
||||
})
|
||||
|
||||
delProjectLog := ctx.NewLogInfo("删除项目信息")
|
||||
delPP := ctx.NewPermission("project:del")
|
||||
// 删除项目
|
||||
project.DELETE("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(delProjectLog).
|
||||
WithRequiredPermission(delPP).
|
||||
Handle(m.DelProject)
|
||||
})
|
||||
|
||||
// 获取项目下的环境信息列表
|
||||
project.GET("/:projectId/envs", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProjectEnvs)
|
||||
})
|
||||
|
||||
saveProjectEnvLog := ctx.NewLogInfo("新增项目环境信息")
|
||||
savePeP := ctx.NewPermission("project:env:add")
|
||||
// 保存项目下的环境信息
|
||||
project.POST("/:projectId/envs", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(saveProjectEnvLog).
|
||||
WithRequiredPermission(savePeP).
|
||||
Handle(m.SaveProjectEnvs)
|
||||
})
|
||||
|
||||
// 获取项目下的成员信息列表
|
||||
project.GET("/:projectId/members", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(m.GetProjectMembers)
|
||||
})
|
||||
|
||||
// 保存项目成员
|
||||
saveProjectMemLog := ctx.NewLogInfo("新增项目成员")
|
||||
savePmP := ctx.NewPermission("project:member:add")
|
||||
project.POST("/:projectId/members", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(saveProjectMemLog).
|
||||
WithRequiredPermission(savePmP).
|
||||
Handle(m.SaveProjectMember)
|
||||
})
|
||||
|
||||
// 删除项目成员
|
||||
delProjectMemLog := ctx.NewLogInfo("删除项目成员")
|
||||
savePmdP := ctx.NewPermission("project:member:del")
|
||||
project.DELETE("/:projectId/members/:accountId", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(delProjectMemLog).
|
||||
WithRequiredPermission(savePmdP).
|
||||
Handle(m.DelProjectMember)
|
||||
})
|
||||
}
|
||||
}
|
||||
80
server/internal/devops/router/redis.go
Normal file
80
server/internal/devops/router/redis.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/devops/api"
|
||||
"mayfly-go/internal/devops/application"
|
||||
"mayfly-go/pkg/ctx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitRedisRouter(router *gin.RouterGroup) {
|
||||
redis := router.Group("redis")
|
||||
{
|
||||
rs := &api.Redis{
|
||||
RedisApp: application.RedisApp,
|
||||
ProjectApp: application.ProjectApp,
|
||||
}
|
||||
|
||||
// 获取redis list
|
||||
redis.GET("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(rs.RedisList)
|
||||
})
|
||||
|
||||
save := ctx.NewLogInfo("保存redis信息")
|
||||
redis.POST("", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(save).Handle(rs.Save)
|
||||
})
|
||||
|
||||
delRedis := ctx.NewLogInfo("删除redis信息")
|
||||
redis.DELETE(":id", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(delRedis).Handle(rs.DeleteRedis)
|
||||
})
|
||||
|
||||
redis.GET(":id/info", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(rs.RedisInfo)
|
||||
})
|
||||
|
||||
// 获取指定redis keys
|
||||
redis.GET(":id/scan/:cursor/:count", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(rs.Scan)
|
||||
})
|
||||
|
||||
// 删除key
|
||||
deleteKeyL := ctx.NewLogInfo("redis删除key")
|
||||
// deleteKey := ctx.NewPermission("project:save")
|
||||
redis.DELETE(":id/key", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).WithLog(deleteKeyL).Handle(rs.DeleteKey)
|
||||
})
|
||||
|
||||
// 获取string类型值
|
||||
redis.GET(":id/string-value", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(rs.GetStringValue)
|
||||
})
|
||||
|
||||
// 设置string类型值
|
||||
redis.POST(":id/string-value", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(rs.SetStringValue)
|
||||
})
|
||||
|
||||
// 获取hash类型值
|
||||
redis.GET(":id/hash-value", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(rs.GetHashValue)
|
||||
})
|
||||
|
||||
// 设置hash类型值
|
||||
redis.POST(":id/hash-value", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(rs.SetHashValue)
|
||||
})
|
||||
|
||||
// 获取set类型值
|
||||
redis.GET(":id/set-value", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(rs.GetSetValue)
|
||||
})
|
||||
|
||||
// 设置set类型值
|
||||
redis.POST(":id/set-value", func(c *gin.Context) {
|
||||
ctx.NewReqCtxWithGin(c).Handle(rs.SetSetValue)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user