mirror of
https://gitee.com/dromara/mayfly-go
synced 2026-02-20 19:25:37 +08:00
refactor: 初步提交全局授权凭证-资源多账号改造
This commit is contained in:
@@ -16,10 +16,10 @@ const (
|
||||
// RedisConnExpireTime = 2 * time.Minute
|
||||
// MongoConnExpireTime = 2 * time.Minute
|
||||
|
||||
TagResourceTypeMachine = 1
|
||||
TagResourceTypeDb = 2
|
||||
TagResourceTypeRedis = 3
|
||||
TagResourceTypeMongo = 4
|
||||
TagResourceTypeMachine int8 = 1
|
||||
TagResourceTypeDb int8 = 2
|
||||
TagResourceTypeRedis int8 = 3
|
||||
TagResourceTypeMongo int8 = 4
|
||||
|
||||
// 删除机器的事件主题名
|
||||
DeleteMachineEventTopic = "machine:delete"
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
"mayfly-go/internal/db/domain/repository"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/errorx"
|
||||
@@ -87,7 +88,7 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db, tagIds ...u
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceCode: dbEntity.Code,
|
||||
ResourceType: consts.TagResourceTypeDb,
|
||||
ResourceType: tagentity.TagTypeDb,
|
||||
TagIds: tagIds,
|
||||
})
|
||||
})
|
||||
@@ -127,7 +128,7 @@ func (d *dbAppImpl) SaveDb(ctx context.Context, dbEntity *entity.Db, tagIds ...u
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceCode: old.Code,
|
||||
ResourceType: consts.TagResourceTypeDb,
|
||||
ResourceType: tagentity.TagTypeDb,
|
||||
TagIds: tagIds,
|
||||
})
|
||||
})
|
||||
@@ -154,7 +155,7 @@ func (d *dbAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceCode: db.Code,
|
||||
ResourceType: consts.TagResourceTypeDb,
|
||||
ResourceType: tagentity.TagTypeDb,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/machine/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
|
||||
type Dashbord struct {
|
||||
TagTreeApp tagapp.TagTree `inject:""`
|
||||
MachineApp application.Machine `inject:""`
|
||||
ResourceAuthCertApp tagapp.ResourceAuthCert `inject:""`
|
||||
MachineApp application.Machine `inject:""`
|
||||
}
|
||||
|
||||
func (m *Dashbord) Dashbord(rc *req.Ctx) {
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
machienNum := len(m.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMachine, ""))
|
||||
|
||||
machienAuthCerts := m.ResourceAuthCertApp.GetAccountAuthCert(accountId, tagentity.TagTypeMachineAuthCert)
|
||||
machineCodes := collx.ArrayMap(machienAuthCerts, func(ac *tagentity.ResourceAuthCert) string {
|
||||
return ac.ResourceCode
|
||||
})
|
||||
|
||||
machienNum := len(collx.ArrayDeduplicate(machineCodes))
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"machineNum": machienNum,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package form
|
||||
|
||||
import tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
|
||||
type MachineForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Protocol int `json:"protocol" binding:"required"`
|
||||
@@ -8,11 +10,8 @@ type MachineForm struct {
|
||||
Ip string `json:"ip" binding:"required"` // IP地址
|
||||
Port int `json:"port" binding:"required"` // 端口号
|
||||
|
||||
// 资产授权凭证信息列表
|
||||
AuthCertId int `json:"authCertId"`
|
||||
TagId []uint64 `json:"tagId" binding:"required"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
TagId []uint64 `json:"tagId" binding:"required"`
|
||||
AuthCerts []*tagentity.ResourceAuthCert // 资产授权凭证信息列表
|
||||
|
||||
Remark string `json:"remark"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
@@ -24,14 +23,6 @@ type MachineRunForm struct {
|
||||
Cmd string `json:"cmd" binding:"required"`
|
||||
}
|
||||
|
||||
type MachineFileForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
MachineId uint64 `json:"machineId" binding:"required"`
|
||||
Type int `json:"type" binding:"required"`
|
||||
Path string `json:"path" binding:"required"`
|
||||
}
|
||||
|
||||
type MachineScriptForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
@@ -42,39 +33,6 @@ type MachineScriptForm struct {
|
||||
Script string `json:"script" binding:"required"`
|
||||
}
|
||||
|
||||
type ServerFileOptionForm struct {
|
||||
MachineId uint64 `form:"machineId"`
|
||||
Protocol int `form:"protocol"`
|
||||
Path string `form:"path"`
|
||||
Type string `form:"type"`
|
||||
Content string `form:"content"`
|
||||
Id uint64 `form:"id"`
|
||||
FileId uint64 `form:"fileId"`
|
||||
}
|
||||
|
||||
type MachineFileUpdateForm struct {
|
||||
Content string `json:"content" binding:"required"`
|
||||
Id uint64 `json:"id" binding:"required"`
|
||||
Path string `json:"path" binding:"required"`
|
||||
}
|
||||
|
||||
type MachineFileOpForm struct {
|
||||
Path []string `json:"path" binding:"required"`
|
||||
ToPath string `json:"toPath"`
|
||||
MachineId uint64 `json:"machineId" binding:"required"`
|
||||
Protocol int `json:"protocol" binding:"required"`
|
||||
FileId uint64 `json:"fileId" binding:"required"`
|
||||
}
|
||||
|
||||
type MachineFileRename struct {
|
||||
MachineId uint64 `json:"machineId" binding:"required"`
|
||||
Protocol int `json:"protocol" binding:"required"`
|
||||
FileId uint64 `json:"fileId" binding:"required"`
|
||||
|
||||
Oldname string `json:"oldname" binding:"required"`
|
||||
Newname string `json:"newname" binding:"required"`
|
||||
}
|
||||
|
||||
// 授权凭证
|
||||
type AuthCertForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
|
||||
48
server/internal/machine/api/form/machine_file.go
Normal file
48
server/internal/machine/api/form/machine_file.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package form
|
||||
|
||||
import "mayfly-go/internal/machine/application"
|
||||
|
||||
type MachineFileForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
MachineId uint64 `json:"machineId" binding:"required"`
|
||||
Type int `json:"type" binding:"required"`
|
||||
Path string `json:"path" binding:"required"`
|
||||
}
|
||||
|
||||
type MachineFileUpdateForm struct {
|
||||
Content string `json:"content" binding:"required"`
|
||||
Id uint64 `json:"id" binding:"required"`
|
||||
Path string `json:"path" binding:"required"`
|
||||
}
|
||||
|
||||
type CreateFileForm struct {
|
||||
*application.MachineFileOpParam
|
||||
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type WriteFileContentForm struct {
|
||||
*application.MachineFileOpParam
|
||||
|
||||
Content string `json:"content" binding:"required"`
|
||||
}
|
||||
|
||||
type RemoveFileForm struct {
|
||||
*application.MachineFileOpParam
|
||||
|
||||
Paths []string `json:"paths" binding:"required"`
|
||||
}
|
||||
|
||||
type CopyFileForm struct {
|
||||
*application.MachineFileOpParam
|
||||
|
||||
Paths []string `json:"paths" binding:"required"`
|
||||
ToPath string `json:"toPath" binding:"required"`
|
||||
}
|
||||
|
||||
type RenameForm struct {
|
||||
*application.MachineFileOpParam
|
||||
|
||||
Newname string `json:"newname" binding:"required"`
|
||||
}
|
||||
@@ -3,10 +3,6 @@ package api
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/may-fly/cast"
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/internal/machine/api/form"
|
||||
"mayfly-go/internal/machine/api/vo"
|
||||
"mayfly-go/internal/machine/application"
|
||||
@@ -29,24 +25,33 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
MachineApp application.Machine `inject:""`
|
||||
MachineTermOpApp application.MachineTermOp `inject:""`
|
||||
TagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
MachineApp application.Machine `inject:""`
|
||||
MachineTermOpApp application.MachineTermOp `inject:""`
|
||||
TagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
ResourceAuthCertApp tagapp.ResourceAuthCert `inject:""`
|
||||
}
|
||||
|
||||
func (m *Machine) Machines(rc *req.Ctx) {
|
||||
condition, pageParam := req.BindQueryAndPage(rc, new(entity.MachineQuery))
|
||||
|
||||
// 不存在可访问标签id,即没有可操作数据
|
||||
codes := m.TagApp.GetAccountResourceCodes(rc.GetLoginAccount().Id, consts.TagResourceTypeMachine, condition.TagPath)
|
||||
if len(codes) == 0 {
|
||||
authCerts := m.ResourceAuthCertApp.GetAccountAuthCert(rc.GetLoginAccount().Id, tagentity.TagTypeMachineAuthCert, condition.TagPath)
|
||||
// 不存在可操作的授权凭证,即没有可操作数据
|
||||
if len(authCerts) == 0 {
|
||||
rc.ResData = model.EmptyPageResult[any]()
|
||||
return
|
||||
}
|
||||
condition.Codes = codes
|
||||
|
||||
machineCodes := collx.ArrayMap(authCerts, func(ac *tagentity.ResourceAuthCert) string {
|
||||
return ac.ResourceCode
|
||||
})
|
||||
condition.Codes = collx.ArrayDeduplicate(machineCodes)
|
||||
|
||||
var machinevos []*vo.MachineVO
|
||||
res, err := m.MachineApp.GetMachineList(condition, pageParam, &machinevos)
|
||||
@@ -61,6 +66,11 @@ func (m *Machine) Machines(rc *req.Ctx) {
|
||||
return mvo
|
||||
})...)
|
||||
|
||||
// 填充授权凭证信息
|
||||
m.ResourceAuthCertApp.FillAuthCert(authCerts, collx.ArrayMap(machinevos, func(mvo *vo.MachineVO) tagentity.IAuthCert {
|
||||
return mvo
|
||||
})...)
|
||||
|
||||
for _, mv := range machinevos {
|
||||
if machineStats, err := m.MachineApp.GetMachineStats(mv.Id); err == nil {
|
||||
mv.Stat = collx.M{
|
||||
@@ -85,16 +95,20 @@ func (m *Machine) SaveMachine(rc *req.Ctx) {
|
||||
machineForm := new(form.MachineForm)
|
||||
me := req.BindJsonAndCopyTo(rc, machineForm, new(entity.Machine))
|
||||
|
||||
machineForm.Password = "******"
|
||||
rc.ReqParam = machineForm
|
||||
|
||||
biz.ErrIsNil(m.MachineApp.SaveMachine(rc.MetaCtx, me, machineForm.TagId...))
|
||||
biz.ErrIsNil(m.MachineApp.SaveMachine(rc.MetaCtx, &application.SaveMachineParam{
|
||||
Machine: me,
|
||||
TagIds: machineForm.TagId,
|
||||
AuthCerts: machineForm.AuthCerts,
|
||||
}))
|
||||
}
|
||||
|
||||
func (m *Machine) TestConn(rc *req.Ctx) {
|
||||
me := req.BindJsonAndCopyTo(rc, new(form.MachineForm), new(entity.Machine))
|
||||
machineForm := new(form.MachineForm)
|
||||
me := req.BindJsonAndCopyTo(rc, machineForm, new(entity.Machine))
|
||||
// 测试连接
|
||||
biz.ErrIsNilAppendErr(m.MachineApp.TestConn(me), "该机器无法连接: %s")
|
||||
biz.ErrIsNilAppendErr(m.MachineApp.TestConn(me, machineForm.AuthCerts[0]), "该机器无法连接: %s")
|
||||
}
|
||||
|
||||
func (m *Machine) ChangeStatus(rc *req.Ctx) {
|
||||
@@ -175,7 +189,7 @@ func (m *Machine) WsSSH(g *gin.Context) {
|
||||
panic(errorx.NewBiz("\033[1;31m您没有权限操作该机器终端,请重新登录后再试~\033[0m"))
|
||||
}
|
||||
|
||||
cli, err := m.MachineApp.NewCli(GetMachineId(rc))
|
||||
cli, err := m.MachineApp.NewCli(GetMachineAc(rc))
|
||||
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")
|
||||
defer cli.Close()
|
||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
||||
@@ -231,9 +245,9 @@ func (m *Machine) WsGuacamole(g *gin.Context) {
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
rc := req.NewCtxWithGin(g).WithRequiredPermission(req.NewPermission("machine:terminal"))
|
||||
machineId := GetMachineId(rc)
|
||||
ac := GetMachineAc(rc)
|
||||
|
||||
mi, err := m.MachineApp.ToMachineInfoById(machineId)
|
||||
mi, err := m.MachineApp.ToMachineInfoByAc(ac)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -258,7 +272,7 @@ func (m *Machine) WsGuacamole(g *gin.Context) {
|
||||
|
||||
if mi.EnableRecorder == 1 {
|
||||
// 操作记录 查看文档:https://guacamole.apache.org/doc/gug/configuring-guacamole.html#graphical-recording
|
||||
params["recording-path"] = fmt.Sprintf("/rdp-rec/%d", machineId)
|
||||
params["recording-path"] = fmt.Sprintf("/rdp-rec/%s", ac)
|
||||
params["create-recording-path"] = "true"
|
||||
params["recording-include-keys"] = "true"
|
||||
}
|
||||
@@ -273,14 +287,14 @@ func (m *Machine) WsGuacamole(g *gin.Context) {
|
||||
if query.Get("force") != "" {
|
||||
// 判断是否强制连接,是的话,查询是否有正在连接的会话,有的话强制关闭
|
||||
if cast.ToBool(query.Get("force")) {
|
||||
tn := sessions.Get(machineId)
|
||||
tn := sessions.Get(ac)
|
||||
if tn != nil {
|
||||
_ = tn.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tunnel, err := guac.DoConnect(query, params, machineId)
|
||||
tunnel, err := guac.DoConnect(query, params, ac)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -290,9 +304,9 @@ func (m *Machine) WsGuacamole(g *gin.Context) {
|
||||
}
|
||||
}()
|
||||
|
||||
sessions.Add(machineId, wsConn, g.Request, tunnel)
|
||||
sessions.Add(ac, wsConn, g.Request, tunnel)
|
||||
|
||||
defer sessions.Delete(machineId, wsConn, g.Request, tunnel)
|
||||
defer sessions.Delete(ac, wsConn, g.Request, tunnel)
|
||||
|
||||
writer := tunnel.AcquireWriter()
|
||||
reader := tunnel.AcquireReader()
|
||||
@@ -312,3 +326,9 @@ func GetMachineId(rc *req.Ctx) uint64 {
|
||||
biz.IsTrue(machineId != 0, "machineId错误")
|
||||
return uint64(machineId)
|
||||
}
|
||||
|
||||
func GetMachineAc(rc *req.Ctx) string {
|
||||
ac := rc.PathParam("ac")
|
||||
biz.IsTrue(ac != "", "authCertName错误")
|
||||
return ac
|
||||
}
|
||||
|
||||
@@ -23,9 +23,10 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type MachineFile struct {
|
||||
@@ -62,8 +63,7 @@ func (m *MachineFile) DeleteFile(rc *req.Ctx) {
|
||||
/*** sftp相关操作 */
|
||||
|
||||
func (m *MachineFile) CreateFile(rc *req.Ctx) {
|
||||
|
||||
opForm := req.BindJsonAndValid(rc, new(form.ServerFileOptionForm))
|
||||
opForm := req.BindJsonAndValid(rc, new(form.CreateFileForm))
|
||||
path := opForm.Path
|
||||
|
||||
attrs := collx.Kvs("path", path)
|
||||
@@ -71,10 +71,10 @@ func (m *MachineFile) CreateFile(rc *req.Ctx) {
|
||||
var err error
|
||||
if opForm.Type == dir {
|
||||
attrs["type"] = "目录"
|
||||
mi, err = m.MachineFileApp.MkDir(opForm.FileId, opForm.Path, opForm)
|
||||
mi, err = m.MachineFileApp.MkDir(opForm.MachineFileOpParam)
|
||||
} else {
|
||||
attrs["type"] = "文件"
|
||||
mi, err = m.MachineFileApp.CreateFile(opForm.FileId, opForm.Path, opForm)
|
||||
mi, err = m.MachineFileApp.CreateFile(opForm.MachineFileOpParam)
|
||||
}
|
||||
attrs["machine"] = mi
|
||||
rc.ReqParam = attrs
|
||||
@@ -82,7 +82,7 @@ func (m *MachineFile) CreateFile(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
func (m *MachineFile) ReadFileContent(rc *req.Ctx) {
|
||||
opForm := req.BindQuery(rc, new(form.ServerFileOptionForm))
|
||||
opForm := req.BindQuery(rc, new(application.MachineFileOpParam))
|
||||
readPath := opForm.Path
|
||||
// 特殊处理rdp文件
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
@@ -96,7 +96,7 @@ func (m *MachineFile) ReadFileContent(rc *req.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
sftpFile, mi, err := m.MachineFileApp.ReadFile(opForm.FileId, readPath)
|
||||
sftpFile, mi, err := m.MachineFileApp.ReadFile(opForm)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", readPath)
|
||||
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
|
||||
defer sftpFile.Close()
|
||||
@@ -112,7 +112,7 @@ func (m *MachineFile) ReadFileContent(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
func (m *MachineFile) DownloadFile(rc *req.Ctx) {
|
||||
opForm := req.BindQuery(rc, new(form.ServerFileOptionForm))
|
||||
opForm := req.BindQuery(rc, new(application.MachineFileOpParam))
|
||||
|
||||
readPath := opForm.Path
|
||||
|
||||
@@ -131,7 +131,7 @@ func (m *MachineFile) DownloadFile(rc *req.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
sftpFile, mi, err := m.MachineFileApp.ReadFile(opForm.FileId, readPath)
|
||||
sftpFile, mi, err := m.MachineFileApp.ReadFile(opForm)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", readPath)
|
||||
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
|
||||
defer sftpFile.Close()
|
||||
@@ -140,11 +140,11 @@ func (m *MachineFile) DownloadFile(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
func (m *MachineFile) GetDirEntry(rc *req.Ctx) {
|
||||
opForm := req.BindQuery(rc, new(form.ServerFileOptionForm))
|
||||
opForm := req.BindQuery(rc, new(application.MachineFileOpParam))
|
||||
readPath := opForm.Path
|
||||
rc.ReqParam = fmt.Sprintf("path: %s", readPath)
|
||||
|
||||
fis, err := m.MachineFileApp.ReadDir(opForm.FileId, opForm)
|
||||
fis, err := m.MachineFileApp.ReadDir(opForm)
|
||||
biz.ErrIsNilAppendErr(err, "读取目录失败: %s")
|
||||
|
||||
fisVO := make([]vo.MachineFileInfo, 0)
|
||||
@@ -173,34 +173,34 @@ func (m *MachineFile) GetDirEntry(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
func (m *MachineFile) GetDirSize(rc *req.Ctx) {
|
||||
opForm := req.BindQuery(rc, new(form.ServerFileOptionForm))
|
||||
opForm := req.BindQuery(rc, new(application.MachineFileOpParam))
|
||||
|
||||
size, err := m.MachineFileApp.GetDirSize(opForm.FileId, opForm)
|
||||
size, err := m.MachineFileApp.GetDirSize(opForm)
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = size
|
||||
}
|
||||
|
||||
func (m *MachineFile) GetFileStat(rc *req.Ctx) {
|
||||
opForm := req.BindQuery(rc, new(form.ServerFileOptionForm))
|
||||
opForm := req.BindQuery(rc, new(application.MachineFileOpParam))
|
||||
res, err := m.MachineFileApp.FileStat(opForm)
|
||||
biz.ErrIsNil(err, res)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (m *MachineFile) WriteFileContent(rc *req.Ctx) {
|
||||
opForm := req.BindQuery(rc, new(form.ServerFileOptionForm))
|
||||
opForm := req.BindJsonAndValid(rc, new(form.WriteFileContentForm))
|
||||
path := opForm.Path
|
||||
|
||||
mi, err := m.MachineFileApp.WriteFileContent(opForm.FileId, path, []byte(opForm.Content), opForm)
|
||||
mi, err := m.MachineFileApp.WriteFileContent(opForm.MachineFileOpParam, []byte(opForm.Content))
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", path)
|
||||
biz.ErrIsNilAppendErr(err, "打开文件失败: %s")
|
||||
}
|
||||
|
||||
func (m *MachineFile) UploadFile(rc *req.Ctx) {
|
||||
fid := GetMachineFileId(rc)
|
||||
path := rc.PostForm("path")
|
||||
protocol, err := strconv.Atoi(rc.PostForm("protocol"))
|
||||
machineId, err := strconv.Atoi(rc.PostForm("machineId"))
|
||||
protocol := cast.ToInt(rc.PostForm("protocol"))
|
||||
machineId := cast.ToUint64(rc.PostForm("machineId"))
|
||||
authCertName := rc.PostForm("authCertName")
|
||||
|
||||
fileheader, err := rc.FormFile("file")
|
||||
biz.ErrIsNilAppendErr(err, "读取文件失败: %s")
|
||||
@@ -219,14 +219,14 @@ func (m *MachineFile) UploadFile(rc *req.Ctx) {
|
||||
}
|
||||
}()
|
||||
|
||||
opForm := &form.ServerFileOptionForm{
|
||||
FileId: fid,
|
||||
MachineId: uint64(machineId),
|
||||
Protocol: protocol,
|
||||
Path: path,
|
||||
opForm := &application.MachineFileOpParam{
|
||||
MachineId: machineId,
|
||||
AuthCertName: authCertName,
|
||||
Protocol: protocol,
|
||||
Path: path,
|
||||
}
|
||||
|
||||
mi, err := m.MachineFileApp.UploadFile(fid, path, fileheader.Filename, file, opForm)
|
||||
mi, err := m.MachineFileApp.UploadFile(opForm, fileheader.Filename, file)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", fmt.Sprintf("%s/%s", path, fileheader.Filename))
|
||||
biz.ErrIsNilAppendErr(err, "创建文件失败: %s")
|
||||
// 保存消息并发送文件上传成功通知
|
||||
@@ -239,8 +239,6 @@ type FolderFile struct {
|
||||
}
|
||||
|
||||
func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
||||
fid := GetMachineFileId(rc)
|
||||
|
||||
mf, err := rc.MultipartForm()
|
||||
biz.ErrIsNilAppendErr(err, "获取表单信息失败: %s")
|
||||
basePath := mf.Value["basePath"][0]
|
||||
@@ -256,21 +254,24 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
||||
biz.IsTrue(allFileSize <= maxUploadFileSize, "文件夹总大小不能超过%d字节", maxUploadFileSize)
|
||||
|
||||
paths := mf.Value["paths"]
|
||||
|
||||
authCertName := mf.Value["authCertName"][0]
|
||||
machineId := cast.ToUint64(mf.Value["machineId"][0])
|
||||
// protocol
|
||||
protocol, err := strconv.Atoi(mf.Value["protocol"][0])
|
||||
protocol := cast.ToInt(mf.Value["protocol"][0])
|
||||
|
||||
opForm := &application.MachineFileOpParam{
|
||||
MachineId: machineId,
|
||||
Protocol: protocol,
|
||||
AuthCertName: authCertName,
|
||||
}
|
||||
|
||||
if protocol == entity.MachineProtocolRdp {
|
||||
machineId, _ := strconv.Atoi(mf.Value["machineId"][0])
|
||||
opForm := &form.ServerFileOptionForm{
|
||||
MachineId: uint64(machineId),
|
||||
Protocol: protocol,
|
||||
}
|
||||
m.MachineFileApp.UploadFiles(basePath, fileheaders, paths, opForm)
|
||||
m.MachineFileApp.UploadFiles(opForm, basePath, fileheaders, paths)
|
||||
return
|
||||
}
|
||||
|
||||
folderName := filepath.Dir(paths[0])
|
||||
mcli, err := m.MachineFileApp.GetMachineCli(fid, basePath+"/"+folderName)
|
||||
mcli, err := m.MachineFileApp.GetMachineCli(authCertName)
|
||||
biz.ErrIsNil(err)
|
||||
mi := mcli.Info
|
||||
|
||||
@@ -344,30 +345,30 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
func (m *MachineFile) RemoveFile(rc *req.Ctx) {
|
||||
opForm := req.BindJsonAndValid(rc, new(form.MachineFileOpForm))
|
||||
opForm := req.BindJsonAndValid(rc, new(form.RemoveFileForm))
|
||||
|
||||
mi, err := m.MachineFileApp.RemoveFile(opForm)
|
||||
mi, err := m.MachineFileApp.RemoveFile(opForm.MachineFileOpParam, opForm.Paths...)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "path", opForm)
|
||||
biz.ErrIsNilAppendErr(err, "删除文件失败: %s")
|
||||
}
|
||||
|
||||
func (m *MachineFile) CopyFile(rc *req.Ctx) {
|
||||
opForm := req.BindJsonAndValid(rc, new(form.MachineFileOpForm))
|
||||
mi, err := m.MachineFileApp.Copy(opForm)
|
||||
opForm := req.BindJsonAndValid(rc, new(form.CopyFileForm))
|
||||
mi, err := m.MachineFileApp.Copy(opForm.MachineFileOpParam, opForm.ToPath, opForm.Paths...)
|
||||
biz.ErrIsNilAppendErr(err, "文件拷贝失败: %s")
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "cp", opForm)
|
||||
}
|
||||
|
||||
func (m *MachineFile) MvFile(rc *req.Ctx) {
|
||||
opForm := req.BindJsonAndValid(rc, new(form.MachineFileOpForm))
|
||||
mi, err := m.MachineFileApp.Mv(opForm)
|
||||
opForm := req.BindJsonAndValid(rc, new(form.CopyFileForm))
|
||||
mi, err := m.MachineFileApp.Mv(opForm.MachineFileOpParam, opForm.ToPath, opForm.Paths...)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "mv", opForm)
|
||||
biz.ErrIsNilAppendErr(err, "文件移动失败: %s")
|
||||
}
|
||||
|
||||
func (m *MachineFile) Rename(rc *req.Ctx) {
|
||||
renameForm := req.BindJsonAndValid(rc, new(form.MachineFileRename))
|
||||
mi, err := m.MachineFileApp.Rename(renameForm)
|
||||
renameForm := req.BindJsonAndValid(rc, new(form.RenameForm))
|
||||
mi, err := m.MachineFileApp.Rename(renameForm.MachineFileOpParam, renameForm.Newname)
|
||||
rc.ReqParam = collx.Kvs("machine", mi, "rename", renameForm)
|
||||
biz.ErrIsNilAppendErr(err, "文件重命名失败: %s")
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ type AuthCertBaseVO struct {
|
||||
}
|
||||
|
||||
type MachineVO struct {
|
||||
tagentity.ResourceTags
|
||||
tagentity.ResourceTags // 标签信息
|
||||
tagentity.AuthCerts // 授权凭证信息
|
||||
|
||||
Id uint64 `json:"id"`
|
||||
Code string `json:"code"`
|
||||
@@ -21,8 +22,6 @@ type MachineVO struct {
|
||||
Protocol int `json:"protocol"`
|
||||
Ip string `json:"ip"`
|
||||
Port int `json:"port"`
|
||||
Username string `json:"username"`
|
||||
AuthCertId int `json:"authCertId"`
|
||||
Status *int8 `json:"status"`
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
@@ -33,8 +32,6 @@ type MachineVO struct {
|
||||
ModifierId *int64 `json:"modifierId"`
|
||||
Remark *string `json:"remark"`
|
||||
EnableRecorder int8 `json:"enableRecorder"`
|
||||
// TagId uint64 `json:"tagId"`
|
||||
// TagPath string `json:"tagPath"`
|
||||
|
||||
Stat map[string]any `json:"stat" gorm:"-"`
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"mayfly-go/internal/machine/infrastructure/cache"
|
||||
"mayfly-go/internal/machine/mcm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/global"
|
||||
@@ -17,15 +18,23 @@ import (
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/scheduler"
|
||||
"time"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
type SaveMachineParam struct {
|
||||
Machine *entity.Machine
|
||||
TagIds []uint64
|
||||
AuthCerts []*tagentity.ResourceAuthCert
|
||||
}
|
||||
|
||||
type Machine interface {
|
||||
base.App[*entity.Machine]
|
||||
|
||||
SaveMachine(ctx context.Context, m *entity.Machine, tagIds ...uint64) error
|
||||
SaveMachine(ctx context.Context, param *SaveMachineParam) error
|
||||
|
||||
// 测试机器连接
|
||||
TestConn(me *entity.Machine) error
|
||||
TestConn(me *entity.Machine, authCert *tagentity.ResourceAuthCert) error
|
||||
|
||||
// 调整机器状态
|
||||
ChangeStatus(ctx context.Context, id uint64, status int8) error
|
||||
@@ -36,11 +45,14 @@ type Machine interface {
|
||||
GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity *[]*vo.MachineVO, orderBy ...string) (*model.PageResult[*[]*vo.MachineVO], error)
|
||||
|
||||
// 新建机器客户端连接(需手动调用Close)
|
||||
NewCli(id uint64) (*mcm.Cli, error)
|
||||
NewCli(authCertName string) (*mcm.Cli, error)
|
||||
|
||||
// 获取已缓存的机器连接,若不存在则新建客户端连接并缓存,主要用于定时获取状态等(避免频繁创建连接)
|
||||
GetCli(id uint64) (*mcm.Cli, error)
|
||||
|
||||
// 根据授权凭证获取客户端连接
|
||||
GetCliByAc(authCertName string) (*mcm.Cli, error)
|
||||
|
||||
// 获取ssh隧道机器连接
|
||||
GetSshTunnelMachine(id int) (*mcm.SshTunnelMachine, error)
|
||||
|
||||
@@ -50,14 +62,15 @@ type Machine interface {
|
||||
// 获取机器运行时状态信息
|
||||
GetMachineStats(machineId uint64) (*mcm.Stats, error)
|
||||
|
||||
ToMachineInfoById(machineId uint64) (*mcm.MachineInfo, error)
|
||||
ToMachineInfoByAc(ac string) (*mcm.MachineInfo, error)
|
||||
}
|
||||
|
||||
type machineAppImpl struct {
|
||||
base.AppImpl[*entity.Machine, repository.Machine]
|
||||
|
||||
authCertApp AuthCert `inject:"AuthCertApp"`
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
// authCertApp AuthCert `inject:"AuthCertApp"`
|
||||
tagApp tagapp.TagTree `inject:"TagTreeApp"`
|
||||
resourceAuthCertApp tagapp.ResourceAuthCert `inject:"ResourceAuthCertApp"`
|
||||
}
|
||||
|
||||
// 注入MachineRepo
|
||||
@@ -70,19 +83,21 @@ func (m *machineAppImpl) GetMachineList(condition *entity.MachineQuery, pagePara
|
||||
return m.GetRepo().GetMachineList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) SaveMachine(ctx context.Context, me *entity.Machine, tagIds ...uint64) error {
|
||||
func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachineParam) error {
|
||||
me := param.Machine
|
||||
tagIds := param.TagIds
|
||||
authCerts := param.AuthCerts
|
||||
resourceType := tagentity.TagTypeMachine
|
||||
authCertTagType := tagentity.TagTypeMachineAuthCert
|
||||
|
||||
oldMachine := &entity.Machine{
|
||||
Ip: me.Ip,
|
||||
Port: me.Port,
|
||||
Username: me.Username,
|
||||
SshTunnelMachineId: me.SshTunnelMachineId,
|
||||
}
|
||||
|
||||
err := m.GetBy(oldMachine)
|
||||
|
||||
if errEnc := me.PwdEncrypt(); errEnc != nil {
|
||||
return errorx.NewBiz(errEnc.Error())
|
||||
}
|
||||
if me.Id == 0 {
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该机器信息已存在")
|
||||
@@ -94,14 +109,23 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, me *entity.Machine, ta
|
||||
// 新增机器,默认启用状态
|
||||
me.Status = entity.MachineStatusEnable
|
||||
|
||||
return m.Tx(ctx, func(ctx context.Context) error {
|
||||
if err := m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.Insert(ctx, me)
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceCode: me.Code,
|
||||
ResourceType: consts.TagResourceTypeMachine,
|
||||
ResourceType: resourceType,
|
||||
TagIds: tagIds,
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.resourceAuthCertApp.SaveAuthCert(ctx, &tagapp.SaveAuthCertParam{
|
||||
ResourceCode: me.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCertTagType: authCertTagType,
|
||||
AuthCerts: authCerts,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -118,20 +142,29 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, me *entity.Machine, ta
|
||||
mcm.DeleteCli(me.Id)
|
||||
// 防止误传修改
|
||||
me.Code = ""
|
||||
return m.Tx(ctx, func(ctx context.Context) error {
|
||||
if err := m.Tx(ctx, func(ctx context.Context) error {
|
||||
return m.UpdateById(ctx, me)
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceCode: oldMachine.Code,
|
||||
ResourceType: consts.TagResourceTypeMachine,
|
||||
ResourceType: resourceType,
|
||||
TagIds: tagIds,
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.resourceAuthCertApp.SaveAuthCert(ctx, &tagapp.SaveAuthCertParam{
|
||||
ResourceCode: oldMachine.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCertTagType: authCertTagType,
|
||||
AuthCerts: authCerts,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) TestConn(me *entity.Machine) error {
|
||||
func (m *machineAppImpl) TestConn(me *entity.Machine, authCert *tagentity.ResourceAuthCert) error {
|
||||
me.Id = 0
|
||||
mi, err := m.toMachineInfo(me)
|
||||
mi, err := m.toMi(me, authCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -165,31 +198,51 @@ func (m *machineAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
|
||||
// 发布机器删除事件
|
||||
global.EventBus.Publish(ctx, consts.DeleteMachineEventTopic, machine)
|
||||
|
||||
resourceType := tagentity.TagTypeMachine
|
||||
return m.Tx(ctx,
|
||||
func(ctx context.Context) error {
|
||||
return m.DeleteById(ctx, id)
|
||||
}, func(ctx context.Context) error {
|
||||
return m.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceCode: machine.Code,
|
||||
ResourceType: consts.TagResourceTypeMachine,
|
||||
ResourceType: resourceType,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return m.resourceAuthCertApp.SaveAuthCert(ctx, &tagapp.SaveAuthCertParam{
|
||||
ResourceCode: machine.Code,
|
||||
ResourceType: resourceType,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) NewCli(machineId uint64) (*mcm.Cli, error) {
|
||||
if mi, err := m.ToMachineInfoById(machineId); err != nil {
|
||||
func (m *machineAppImpl) NewCli(authCertName string) (*mcm.Cli, error) {
|
||||
if mi, err := m.ToMachineInfoByAc(authCertName); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return mi.Conn()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) GetCli(machineId uint64) (*mcm.Cli, error) {
|
||||
return mcm.GetMachineCli(machineId, func(mid uint64) (*mcm.MachineInfo, error) {
|
||||
return m.ToMachineInfoById(mid)
|
||||
func (m *machineAppImpl) GetCliByAc(authCertName string) (*mcm.Cli, error) {
|
||||
return mcm.GetMachineCli(authCertName, func(ac string) (*mcm.MachineInfo, error) {
|
||||
return m.ToMachineInfoByAc(ac)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) GetCli(machineId uint64) (*mcm.Cli, error) {
|
||||
cli, err := mcm.GetMachineCliById(machineId)
|
||||
if err == nil {
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
_, authCert, err := m.getMachineAndAuthCert(machineId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m.GetCliByAc(authCert.Name)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) GetSshTunnelMachine(machineId int) (*mcm.SshTunnelMachine, error) {
|
||||
return mcm.GetSshTunnelMachine(machineId, func(mid uint64) (*mcm.MachineInfo, error) {
|
||||
return m.ToMachineInfoById(mid)
|
||||
@@ -229,62 +282,68 @@ func (m *machineAppImpl) GetMachineStats(machineId uint64) (*mcm.Stats, error) {
|
||||
return cache.GetMachineStats(machineId)
|
||||
}
|
||||
|
||||
// 生成机器信息,根据授权凭证id填充用户密码等
|
||||
func (m *machineAppImpl) ToMachineInfoById(machineId uint64) (*mcm.MachineInfo, error) {
|
||||
me, err := m.GetById(new(entity.Machine), machineId)
|
||||
// 根据授权凭证,生成机器信息
|
||||
func (m *machineAppImpl) ToMachineInfoByAc(authCertName string) (*mcm.MachineInfo, error) {
|
||||
authCert, err := m.resourceAuthCertApp.GetAuthCert(authCertName)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("机器信息不存在")
|
||||
}
|
||||
if me.Status != entity.MachineStatusEnable && me.Protocol == 1 {
|
||||
return nil, errorx.NewBiz("该机器已被停用")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if mi, err := m.toMachineInfo(me); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return mi, nil
|
||||
machine := &entity.Machine{
|
||||
Code: authCert.ResourceCode,
|
||||
}
|
||||
if err := m.GetBy(machine); err != nil {
|
||||
return nil, errorx.NewBiz("该授权凭证关联的机器信息不存在")
|
||||
}
|
||||
|
||||
return m.toMi(machine, authCert)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) toMachineInfo(me *entity.Machine) (*mcm.MachineInfo, error) {
|
||||
// 生成机器信息,根据授权凭证id填充用户密码等
|
||||
func (m *machineAppImpl) ToMachineInfoById(machineId uint64) (*mcm.MachineInfo, error) {
|
||||
me, authCert, err := m.getMachineAndAuthCert(machineId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m.toMi(me, authCert)
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) getMachineAndAuthCert(machineId uint64) (*entity.Machine, *tagentity.ResourceAuthCert, error) {
|
||||
me, err := m.GetById(new(entity.Machine), machineId)
|
||||
if err != nil {
|
||||
return nil, nil, errorx.NewBiz("[%d]机器信息不存在", machineId)
|
||||
}
|
||||
if me.Status != entity.MachineStatusEnable && me.Protocol == 1 {
|
||||
return nil, nil, errorx.NewBiz("[%s]该机器已被停用", me.Code)
|
||||
}
|
||||
|
||||
authCert, err := m.resourceAuthCertApp.GetResourceAuthCert(tagentity.TagTypeMachine, me.Code)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return me, authCert, nil
|
||||
}
|
||||
|
||||
func (m *machineAppImpl) toMi(me *entity.Machine, authCert *tagentity.ResourceAuthCert) (*mcm.MachineInfo, error) {
|
||||
mi := new(mcm.MachineInfo)
|
||||
mi.Id = me.Id
|
||||
mi.Name = me.Name
|
||||
mi.Ip = me.Ip
|
||||
mi.Port = me.Port
|
||||
mi.Username = me.Username
|
||||
mi.TagPath = m.tagApp.ListTagPathByResource(consts.TagResourceTypeMachine, me.Code)
|
||||
mi.TagPath = m.tagApp.ListTagPathByResource(int8(tagentity.TagTypeMachineAuthCert), authCert.Name)
|
||||
mi.EnableRecorder = me.EnableRecorder
|
||||
mi.Protocol = me.Protocol
|
||||
|
||||
if me.UseAuthCert() {
|
||||
ac, err := m.authCertApp.GetById(new(entity.AuthCert), uint64(me.AuthCertId))
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("授权凭证信息已不存在,请重新关联")
|
||||
}
|
||||
mi.AuthMethod = ac.AuthMethod
|
||||
if err := ac.PwdDecrypt(); err != nil {
|
||||
return nil, errorx.NewBiz(err.Error())
|
||||
}
|
||||
mi.Password = ac.Password
|
||||
mi.Passphrase = ac.Passphrase
|
||||
} else {
|
||||
mi.AuthMethod = entity.AuthCertAuthMethodPassword
|
||||
if me.Id != 0 {
|
||||
if err := me.PwdDecrypt(); err != nil {
|
||||
return nil, errorx.NewBiz(err.Error())
|
||||
}
|
||||
}
|
||||
mi.Password = me.Password
|
||||
}
|
||||
mi.Username = authCert.Username
|
||||
mi.Password = authCert.Ciphertext
|
||||
mi.Passphrase = cast.ToString(authCert.Extra["passphrase"])
|
||||
mi.AuthMethod = int8(authCert.CiphertextType)
|
||||
|
||||
// 使用了ssh隧道,则将隧道机器信息也附上
|
||||
if me.SshTunnelMachineId > 0 {
|
||||
sshTunnelMe, err := m.GetById(new(entity.Machine), uint64(me.SshTunnelMachineId))
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("隧道机器信息不存在")
|
||||
}
|
||||
sshTunnelMi, err := m.toMachineInfo(sshTunnelMe)
|
||||
sshTunnelMi, err := m.ToMachineInfoById(uint64(me.SshTunnelMachineId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"mayfly-go/internal/machine/api/form"
|
||||
"mayfly-go/internal/machine/config"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
"mayfly-go/internal/machine/domain/repository"
|
||||
@@ -17,6 +15,7 @@ import (
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/bytex"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -25,6 +24,13 @@ import (
|
||||
"github.com/pkg/sftp"
|
||||
)
|
||||
|
||||
type MachineFileOpParam struct {
|
||||
MachineId uint64 `json:"machineId" binding:"required" form:"machineId"`
|
||||
Protocol int `json:"protocol" binding:"required" form:"protocol"`
|
||||
AuthCertName string `json:"authCertName" binding:"required" form:"authCertName"` // 授权凭证
|
||||
Path string `json:"path" form:"path"` // 文件路径
|
||||
}
|
||||
|
||||
type MachineFile interface {
|
||||
base.App[*entity.MachineFile]
|
||||
|
||||
@@ -36,50 +42,47 @@ type MachineFile interface {
|
||||
|
||||
Save(ctx context.Context, entity *entity.MachineFile) error
|
||||
|
||||
// 获取文件关联的机器信息,主要用于记录日志使用
|
||||
// GetMachine(fileId uint64) *mcm.Info
|
||||
|
||||
// 检查文件路径,并返回机器id
|
||||
GetMachineCli(fileId uint64, path ...string) (*mcm.Cli, error)
|
||||
// 获取机器cli
|
||||
GetMachineCli(authCertName string) (*mcm.Cli, error)
|
||||
|
||||
GetRdpFilePath(MachineId uint64, path string) string
|
||||
|
||||
/** sftp 相关操作 **/
|
||||
|
||||
// 创建目录
|
||||
MkDir(fid uint64, path string, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error)
|
||||
MkDir(opParam *MachineFileOpParam) (*mcm.MachineInfo, error)
|
||||
|
||||
// 创建文件
|
||||
CreateFile(fid uint64, path string, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error)
|
||||
CreateFile(opParam *MachineFileOpParam) (*mcm.MachineInfo, error)
|
||||
|
||||
// 读取目录
|
||||
ReadDir(fid uint64, opForm *form.ServerFileOptionForm) ([]fs.FileInfo, error)
|
||||
ReadDir(opParam *MachineFileOpParam) ([]fs.FileInfo, error)
|
||||
|
||||
// 获取指定目录内容大小
|
||||
GetDirSize(fid uint64, opForm *form.ServerFileOptionForm) (string, error)
|
||||
GetDirSize(opParam *MachineFileOpParam) (string, error)
|
||||
|
||||
// 获取文件stat
|
||||
FileStat(opForm *form.ServerFileOptionForm) (string, error)
|
||||
FileStat(opParam *MachineFileOpParam) (string, error)
|
||||
|
||||
// 读取文件内容
|
||||
ReadFile(fileId uint64, path string) (*sftp.File, *mcm.MachineInfo, error)
|
||||
ReadFile(opParam *MachineFileOpParam) (*sftp.File, *mcm.MachineInfo, error)
|
||||
|
||||
// 写文件
|
||||
WriteFileContent(fileId uint64, path string, content []byte, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error)
|
||||
WriteFileContent(opParam *MachineFileOpParam, content []byte) (*mcm.MachineInfo, error)
|
||||
|
||||
// 文件上传
|
||||
UploadFile(fileId uint64, path, filename string, reader io.Reader, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error)
|
||||
UploadFile(opParam *MachineFileOpParam, filename string, reader io.Reader) (*mcm.MachineInfo, error)
|
||||
|
||||
UploadFiles(basePath string, fileHeaders []*multipart.FileHeader, paths []string, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error)
|
||||
UploadFiles(opParam *MachineFileOpParam, basePath string, fileHeaders []*multipart.FileHeader, paths []string) (*mcm.MachineInfo, error)
|
||||
|
||||
// 移除文件
|
||||
RemoveFile(opForm *form.MachineFileOpForm) (*mcm.MachineInfo, error)
|
||||
RemoveFile(opParam *MachineFileOpParam, path ...string) (*mcm.MachineInfo, error)
|
||||
|
||||
Copy(opForm *form.MachineFileOpForm) (*mcm.MachineInfo, error)
|
||||
Copy(opParam *MachineFileOpParam, toPath string, path ...string) (*mcm.MachineInfo, error)
|
||||
|
||||
Mv(opForm *form.MachineFileOpForm) (*mcm.MachineInfo, error)
|
||||
Mv(opParam *MachineFileOpParam, toPath string, path ...string) (*mcm.MachineInfo, error)
|
||||
|
||||
Rename(renameForm *form.MachineFileRename) (*mcm.MachineInfo, error)
|
||||
Rename(opParam *MachineFileOpParam, newname string) (*mcm.MachineInfo, error)
|
||||
}
|
||||
|
||||
type machineFileAppImpl struct {
|
||||
@@ -117,29 +120,37 @@ func (m *machineFileAppImpl) Save(ctx context.Context, mf *entity.MachineFile) e
|
||||
return m.Insert(ctx, mf)
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) ReadDir(fid uint64, opForm *form.ServerFileOptionForm) ([]fs.FileInfo, error) {
|
||||
if !strings.HasSuffix(opForm.Path, "/") {
|
||||
opForm.Path = opForm.Path + "/"
|
||||
func (m *machineFileAppImpl) ReadDir(opParam *MachineFileOpParam) ([]fs.FileInfo, error) {
|
||||
path := opParam.Path
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path = path + "/"
|
||||
}
|
||||
|
||||
// 如果是rdp,则直接读取本地文件
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
opForm.Path = m.GetRdpFilePath(opForm.MachineId, opForm.Path)
|
||||
return ioutil.ReadDir(opForm.Path)
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opParam.MachineId, path)
|
||||
dirs, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return collx.ArrayMap[fs.DirEntry, fs.FileInfo](dirs, func(val fs.DirEntry) fs.FileInfo {
|
||||
fi, _ := val.Info()
|
||||
return fi
|
||||
}), nil
|
||||
}
|
||||
|
||||
_, sftpCli, err := m.GetMachineSftpCli(fid, opForm.Path)
|
||||
_, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sftpCli.ReadDir(opForm.Path)
|
||||
return sftpCli.ReadDir(path)
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) GetDirSize(fid uint64, opForm *form.ServerFileOptionForm) (string, error) {
|
||||
path := opForm.Path
|
||||
func (m *machineFileAppImpl) GetDirSize(opParam *MachineFileOpParam) (string, error) {
|
||||
path := opParam.Path
|
||||
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
dirPath := m.GetRdpFilePath(opForm.MachineId, path)
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
dirPath := m.GetRdpFilePath(opParam.MachineId, path)
|
||||
|
||||
// 递归计算目录下文件大小
|
||||
var totalSize int64
|
||||
@@ -160,7 +171,7 @@ func (m *machineFileAppImpl) GetDirSize(fid uint64, opForm *form.ServerFileOptio
|
||||
return bytex.FormatSize(totalSize), nil
|
||||
}
|
||||
|
||||
mcli, err := m.GetMachineCli(fid, path)
|
||||
mcli, err := m.GetMachineCli(opParam.AuthCertName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -184,32 +195,34 @@ func (m *machineFileAppImpl) GetDirSize(fid uint64, opForm *form.ServerFileOptio
|
||||
return strings.Split(res, "\t")[0], nil
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) FileStat(opForm *form.ServerFileOptionForm) (string, error) {
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
path := m.GetRdpFilePath(opForm.MachineId, opForm.Path)
|
||||
func (m *machineFileAppImpl) FileStat(opParam *MachineFileOpParam) (string, error) {
|
||||
path := opParam.Path
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opParam.MachineId, path)
|
||||
stat, err := os.Stat(path)
|
||||
return fmt.Sprintf("%v", stat), err
|
||||
}
|
||||
|
||||
mcli, err := m.GetMachineCli(opForm.FileId, opForm.Path)
|
||||
mcli, err := m.GetMachineCli(opParam.AuthCertName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return mcli.Run(fmt.Sprintf("stat -L %s", opForm.Path))
|
||||
return mcli.Run(fmt.Sprintf("stat -L %s", path))
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) MkDir(fid uint64, path string, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error) {
|
||||
func (m *machineFileAppImpl) MkDir(opParam *MachineFileOpParam) (*mcm.MachineInfo, error) {
|
||||
path := opParam.Path
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path = path + "/"
|
||||
}
|
||||
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opForm.MachineId, path)
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opParam.MachineId, path)
|
||||
os.MkdirAll(path, os.ModePerm)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(fid, path)
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -218,14 +231,15 @@ func (m *machineFileAppImpl) MkDir(fid uint64, path string, opForm *form.ServerF
|
||||
return mi, err
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) CreateFile(fid uint64, path string, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error) {
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(fid, path)
|
||||
func (m *machineFileAppImpl) CreateFile(opParam *MachineFileOpParam) (*mcm.MachineInfo, error) {
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opForm.MachineId, path)
|
||||
path := opParam.Path
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opParam.MachineId, path)
|
||||
file, err := os.Create(path)
|
||||
defer file.Close()
|
||||
return nil, err
|
||||
@@ -239,22 +253,22 @@ func (m *machineFileAppImpl) CreateFile(fid uint64, path string, opForm *form.Se
|
||||
return mi, err
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) ReadFile(fileId uint64, path string) (*sftp.File, *mcm.MachineInfo, error) {
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(fileId, path)
|
||||
func (m *machineFileAppImpl) ReadFile(opParam *MachineFileOpParam) (*sftp.File, *mcm.MachineInfo, error) {
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
fc, err := sftpCli.Open(path)
|
||||
fc, err := sftpCli.Open(opParam.Path)
|
||||
return fc, mi, err
|
||||
}
|
||||
|
||||
// 写文件内容
|
||||
func (m *machineFileAppImpl) WriteFileContent(fileId uint64, path string, content []byte, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error) {
|
||||
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opForm.MachineId, path)
|
||||
func (m *machineFileAppImpl) WriteFileContent(opParam *MachineFileOpParam, content []byte) (*mcm.MachineInfo, error) {
|
||||
path := opParam.Path
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opParam.MachineId, path)
|
||||
file, err := os.Create(path)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
@@ -264,7 +278,7 @@ func (m *machineFileAppImpl) WriteFileContent(fileId uint64, path string, conten
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(fileId, path)
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -279,13 +293,14 @@ func (m *machineFileAppImpl) WriteFileContent(fileId uint64, path string, conten
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
func (m *machineFileAppImpl) UploadFile(fileId uint64, path, filename string, reader io.Reader, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error) {
|
||||
func (m *machineFileAppImpl) UploadFile(opParam *MachineFileOpParam, filename string, reader io.Reader) (*mcm.MachineInfo, error) {
|
||||
path := opParam.Path
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path = path + "/"
|
||||
}
|
||||
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opForm.MachineId, path)
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
path = m.GetRdpFilePath(opParam.MachineId, path)
|
||||
file, err := os.Create(path + filename)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
@@ -295,7 +310,7 @@ func (m *machineFileAppImpl) UploadFile(fileId uint64, path, filename string, re
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(fileId, path)
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -309,9 +324,9 @@ func (m *machineFileAppImpl) UploadFile(fileId uint64, path, filename string, re
|
||||
return mi, err
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) UploadFiles(basePath string, fileHeaders []*multipart.FileHeader, paths []string, opForm *form.ServerFileOptionForm) (*mcm.MachineInfo, error) {
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
baseFolder := m.GetRdpFilePath(opForm.MachineId, basePath)
|
||||
func (m *machineFileAppImpl) UploadFiles(opParam *MachineFileOpParam, basePath string, fileHeaders []*multipart.FileHeader, paths []string) (*mcm.MachineInfo, error) {
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
baseFolder := m.GetRdpFilePath(opParam.MachineId, basePath)
|
||||
|
||||
for i, fileHeader := range fileHeaders {
|
||||
file, err := fileHeader.Open()
|
||||
@@ -326,7 +341,11 @@ func (m *machineFileAppImpl) UploadFiles(basePath string, fileHeaders []*multipa
|
||||
rdpBaseDir = rdpBaseDir + "/"
|
||||
}
|
||||
rdpDir := filepath.Dir(rdpBaseDir + paths[i])
|
||||
m.MkDir(0, rdpDir, opForm)
|
||||
m.MkDir(&MachineFileOpParam{
|
||||
MachineId: opParam.MachineId,
|
||||
Protocol: opParam.Protocol,
|
||||
Path: rdpDir,
|
||||
})
|
||||
|
||||
// 创建文件
|
||||
if !strings.HasSuffix(baseFolder, "/") {
|
||||
@@ -348,24 +367,23 @@ func (m *machineFileAppImpl) UploadFiles(basePath string, fileHeaders []*multipa
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
func (m *machineFileAppImpl) RemoveFile(opForm *form.MachineFileOpForm) (*mcm.MachineInfo, error) {
|
||||
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
for _, pt := range opForm.Path {
|
||||
pt = m.GetRdpFilePath(opForm.MachineId, pt)
|
||||
func (m *machineFileAppImpl) RemoveFile(opParam *MachineFileOpParam, path ...string) (*mcm.MachineInfo, error) {
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
for _, pt := range path {
|
||||
pt = m.GetRdpFilePath(opParam.MachineId, pt)
|
||||
os.RemoveAll(pt)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mcli, err := m.GetMachineCli(opForm.FileId, opForm.Path...)
|
||||
mcli, err := m.GetMachineCli(opParam.AuthCertName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
minfo := mcli.Info
|
||||
|
||||
// 优先使用命令删除(速度快),sftp需要递归遍历删除子文件等
|
||||
res, err := mcli.Run(fmt.Sprintf("rm -rf %s", strings.Join(opForm.Path, " ")))
|
||||
res, err := mcli.Run(fmt.Sprintf("rm -rf %s", strings.Join(path, " ")))
|
||||
if err == nil {
|
||||
return minfo, nil
|
||||
}
|
||||
@@ -376,7 +394,7 @@ func (m *machineFileAppImpl) RemoveFile(opForm *form.MachineFileOpForm) (*mcm.Ma
|
||||
return minfo, err
|
||||
}
|
||||
|
||||
for _, p := range opForm.Path {
|
||||
for _, p := range path {
|
||||
err = sftpCli.RemoveAll(p)
|
||||
if err != nil {
|
||||
break
|
||||
@@ -385,11 +403,11 @@ func (m *machineFileAppImpl) RemoveFile(opForm *form.MachineFileOpForm) (*mcm.Ma
|
||||
return minfo, err
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) Copy(opForm *form.MachineFileOpForm) (*mcm.MachineInfo, error) {
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
for _, pt := range opForm.Path {
|
||||
srcPath := m.GetRdpFilePath(opForm.MachineId, pt)
|
||||
targetPath := m.GetRdpFilePath(opForm.MachineId, opForm.ToPath+pt)
|
||||
func (m *machineFileAppImpl) Copy(opParam *MachineFileOpParam, toPath string, path ...string) (*mcm.MachineInfo, error) {
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
for _, pt := range path {
|
||||
srcPath := m.GetRdpFilePath(opParam.MachineId, pt)
|
||||
targetPath := m.GetRdpFilePath(opParam.MachineId, toPath+pt)
|
||||
|
||||
// 打开源文件
|
||||
srcFile, err := os.Open(srcPath)
|
||||
@@ -408,59 +426,57 @@ func (m *machineFileAppImpl) Copy(opForm *form.MachineFileOpForm) (*mcm.MachineI
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mcli, err := m.GetMachineCli(opForm.FileId, opForm.Path...)
|
||||
mcli, err := m.GetMachineCli(opParam.AuthCertName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi := mcli.Info
|
||||
res, err := mcli.Run(fmt.Sprintf("cp -r %s %s", strings.Join(opForm.Path, " "), opForm.ToPath))
|
||||
res, err := mcli.Run(fmt.Sprintf("cp -r %s %s", strings.Join(path, " "), toPath))
|
||||
if err != nil {
|
||||
return mi, errors.New(res)
|
||||
}
|
||||
return mi, err
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) Mv(opForm *form.MachineFileOpForm) (*mcm.MachineInfo, error) {
|
||||
if opForm.Protocol == entity.MachineProtocolRdp {
|
||||
for _, pt := range opForm.Path {
|
||||
func (m *machineFileAppImpl) Mv(opParam *MachineFileOpParam, toPath string, path ...string) (*mcm.MachineInfo, error) {
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
for _, pt := range path {
|
||||
// 获取文件名
|
||||
filename := filepath.Base(pt)
|
||||
topath := opForm.ToPath
|
||||
if !strings.HasSuffix(topath, "/") {
|
||||
topath += "/"
|
||||
if !strings.HasSuffix(toPath, "/") {
|
||||
toPath += "/"
|
||||
}
|
||||
|
||||
srcPath := m.GetRdpFilePath(opForm.MachineId, pt)
|
||||
targetPath := m.GetRdpFilePath(opForm.MachineId, topath+filename)
|
||||
srcPath := m.GetRdpFilePath(opParam.MachineId, pt)
|
||||
targetPath := m.GetRdpFilePath(opParam.MachineId, toPath+filename)
|
||||
os.Rename(srcPath, targetPath)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mcli, err := m.GetMachineCli(opForm.FileId, opForm.Path...)
|
||||
mcli, err := m.GetMachineCli(opParam.AuthCertName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mi := mcli.Info
|
||||
res, err := mcli.Run(fmt.Sprintf("mv %s %s", strings.Join(opForm.Path, " "), opForm.ToPath))
|
||||
res, err := mcli.Run(fmt.Sprintf("mv %s %s", strings.Join(path, " "), toPath))
|
||||
if err != nil {
|
||||
return mi, errorx.NewBiz(res)
|
||||
}
|
||||
return mi, err
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) Rename(renameForm *form.MachineFileRename) (*mcm.MachineInfo, error) {
|
||||
oldname := renameForm.Oldname
|
||||
newname := renameForm.Newname
|
||||
if renameForm.Protocol == entity.MachineProtocolRdp {
|
||||
oldname = m.GetRdpFilePath(renameForm.MachineId, renameForm.Oldname)
|
||||
newname = m.GetRdpFilePath(renameForm.MachineId, renameForm.Newname)
|
||||
func (m *machineFileAppImpl) Rename(opParam *MachineFileOpParam, newname string) (*mcm.MachineInfo, error) {
|
||||
oldname := opParam.Path
|
||||
if opParam.Protocol == entity.MachineProtocolRdp {
|
||||
oldname = m.GetRdpFilePath(opParam.MachineId, oldname)
|
||||
newname = m.GetRdpFilePath(opParam.MachineId, newname)
|
||||
return nil, os.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(renameForm.FileId, newname)
|
||||
mi, sftpCli, err := m.GetMachineSftpCli(opParam)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -468,24 +484,13 @@ func (m *machineFileAppImpl) Rename(renameForm *form.MachineFileRename) (*mcm.Ma
|
||||
}
|
||||
|
||||
// 获取文件机器cli
|
||||
func (m *machineFileAppImpl) GetMachineCli(fid uint64, inputPath ...string) (*mcm.Cli, error) {
|
||||
mf, err := m.GetById(new(entity.MachineFile), fid)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("文件不存在")
|
||||
}
|
||||
|
||||
for _, path := range inputPath {
|
||||
// 接口传入的地址需为配置路径的子路径
|
||||
if !strings.HasPrefix(path, mf.Path) {
|
||||
return nil, errorx.NewBiz("无权访问该目录或文件: %s", path)
|
||||
}
|
||||
}
|
||||
return m.machineApp.GetCli(mf.MachineId)
|
||||
func (m *machineFileAppImpl) GetMachineCli(authCertName string) (*mcm.Cli, error) {
|
||||
return m.machineApp.GetCliByAc(authCertName)
|
||||
}
|
||||
|
||||
// 获取文件机器 sftp cli
|
||||
func (m *machineFileAppImpl) GetMachineSftpCli(fid uint64, inputPath ...string) (*mcm.MachineInfo, *sftp.Client, error) {
|
||||
mcli, err := m.GetMachineCli(fid, inputPath...)
|
||||
func (m *machineFileAppImpl) GetMachineSftpCli(opParam *MachineFileOpParam) (*mcm.MachineInfo, *sftp.Client, error) {
|
||||
mcli, err := m.GetMachineCli(opParam.AuthCertName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -498,6 +503,6 @@ func (m *machineFileAppImpl) GetMachineSftpCli(fid uint64, inputPath ...string)
|
||||
return mcli.Info, sftpCli, nil
|
||||
}
|
||||
|
||||
func (m *machineFileAppImpl) GetRdpFilePath(MachineId uint64, path string) string {
|
||||
return fmt.Sprintf("%s/%d%s", config.GetMachine().GuacdFilePath, MachineId, path)
|
||||
func (m *machineFileAppImpl) GetRdpFilePath(machineId uint64, path string) string {
|
||||
return fmt.Sprintf("%s/%d%s", config.GetMachine().GuacdFilePath, machineId, path)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"mayfly-go/internal/common/utils"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
@@ -14,9 +12,6 @@ type Machine struct {
|
||||
Protocol int `json:"protocol"` // 连接协议 1.ssh 2.rdp
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Port int `json:"port"` // 端口号
|
||||
Username string `json:"username"` // 用户名
|
||||
Password string `json:"password"` // 密码
|
||||
AuthCertId int `json:"authCertId"` // 授权凭证id
|
||||
Status int8 `json:"status"` // 状态 1:启用;2:停用
|
||||
Remark string `json:"remark"` // 备注
|
||||
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
@@ -30,27 +25,3 @@ const (
|
||||
MachineProtocolSsh = 1
|
||||
MachineProtocolRdp = 2
|
||||
)
|
||||
|
||||
func (m *Machine) PwdEncrypt() error {
|
||||
// 密码替换为加密后的密码
|
||||
password, err := utils.PwdAesEncrypt(m.Password)
|
||||
if err != nil {
|
||||
return errors.New("加密主机密码失败")
|
||||
}
|
||||
m.Password = password
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Machine) PwdDecrypt() error {
|
||||
// 密码替换为解密后的密码
|
||||
password, err := utils.PwdAesDecrypt(m.Password)
|
||||
if err != nil {
|
||||
return errors.New("解密主机密码失败")
|
||||
}
|
||||
m.Password = password
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Machine) UseAuthCert() bool {
|
||||
return m.AuthCertId > 0
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// creates the tunnel to the remote machine (via guacd)
|
||||
func DoConnect(query url.Values, parameters map[string]string, machineId uint64) (Tunnel, error) {
|
||||
func DoConnect(query url.Values, parameters map[string]string, ac string) (Tunnel, error) {
|
||||
conf := NewGuacamoleConfiguration()
|
||||
|
||||
parameters["enable-wallpaper"] = "true" // 允许显示墙纸
|
||||
@@ -33,7 +33,7 @@ func DoConnect(query url.Values, parameters map[string]string, machineId uint64)
|
||||
parameters["enable-drive"] = "true"
|
||||
parameters["drive-name"] = "Filesystem"
|
||||
parameters["create-drive-path"] = "true"
|
||||
parameters["drive-path"] = fmt.Sprintf("/rdp-file/%d", machineId)
|
||||
parameters["drive-path"] = fmt.Sprintf("/rdp-file/%s", ac)
|
||||
|
||||
conf.Protocol = parameters["scheme"]
|
||||
conf.Parameters = parameters
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
package guac
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// MemorySessionStore is a simple in-memory store of connected sessions that is used by
|
||||
// the WebsocketServer to store active sessions.
|
||||
type MemorySessionStore struct {
|
||||
sync.RWMutex
|
||||
ConnIds map[uint64]Tunnel
|
||||
ConnIds map[string]Tunnel
|
||||
}
|
||||
|
||||
// NewMemorySessionStore creates a new store
|
||||
func NewMemorySessionStore() *MemorySessionStore {
|
||||
return &MemorySessionStore{
|
||||
ConnIds: map[uint64]Tunnel{},
|
||||
ConnIds: map[string]Tunnel{},
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns a connection by uuid
|
||||
func (s *MemorySessionStore) Get(id uint64) Tunnel {
|
||||
func (s *MemorySessionStore) Get(id string) Tunnel {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return s.ConnIds[id]
|
||||
}
|
||||
|
||||
// Add inserts a new connection by uuid
|
||||
func (s *MemorySessionStore) Add(id uint64, conn *websocket.Conn, req *http.Request, tunnel Tunnel) {
|
||||
func (s *MemorySessionStore) Add(id string, conn *websocket.Conn, req *http.Request, tunnel Tunnel) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
n, ok := s.ConnIds[id]
|
||||
@@ -41,7 +42,7 @@ func (s *MemorySessionStore) Add(id uint64, conn *websocket.Conn, req *http.Requ
|
||||
}
|
||||
|
||||
// Delete removes a connection by uuid
|
||||
func (s *MemorySessionStore) Delete(id uint64, conn *websocket.Conn, req *http.Request, tunnel Tunnel) {
|
||||
func (s *MemorySessionStore) Delete(id string, conn *websocket.Conn, req *http.Request, tunnel Tunnel) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
n, ok := s.ConnIds[id]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package mcm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"mayfly-go/internal/common/consts"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/logx"
|
||||
"time"
|
||||
@@ -29,29 +31,63 @@ func init() {
|
||||
go checkClientAvailability(3 * time.Minute)
|
||||
}
|
||||
|
||||
// 从缓存中获取客户端信息,不存在则回调获取机器信息函数,并新建
|
||||
func GetMachineCli(machineId uint64, getMachine func(uint64) (*MachineInfo, error)) (*Cli, error) {
|
||||
if load, ok := cliCache.Get(machineId); ok {
|
||||
// 从缓存中获取客户端信息,不存在则回调获取机器信息函数,并新建。
|
||||
// @param 机器的授权凭证名
|
||||
func GetMachineCli(authCertName string, getMachine func(string) (*MachineInfo, error)) (*Cli, error) {
|
||||
if load, ok := cliCache.Get(authCertName); ok {
|
||||
return load.(*Cli), nil
|
||||
}
|
||||
|
||||
me, err := getMachine(machineId)
|
||||
mi, err := getMachine(authCertName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mi.Key = authCertName
|
||||
c, err := mi.Conn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := me.Conn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cliCache.Put(machineId, c)
|
||||
cliCache.Put(authCertName, c)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// 根据机器id从已连接的机器客户端中获取特权账号连接, 若不存在特权账号,则随机返回一个
|
||||
func GetMachineCliById(machineId uint64) (*Cli, error) {
|
||||
// 遍历所有机器连接实例,删除指定机器id关联的连接...
|
||||
items := cliCache.Items()
|
||||
|
||||
var machineCli *Cli
|
||||
for _, v := range items {
|
||||
cli := v.Value.(*Cli)
|
||||
mi := cli.Info
|
||||
if mi.Id != machineId {
|
||||
continue
|
||||
}
|
||||
machineCli = cli
|
||||
|
||||
// 如果是特权账号,则跳出
|
||||
if mi.AuthCertType == tagentity.AuthCertTypePrivileged {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if machineCli != nil {
|
||||
return machineCli, nil
|
||||
}
|
||||
return nil, errors.New("不存在该机器id的连接")
|
||||
}
|
||||
|
||||
// 删除指定机器缓存客户端,并关闭客户端连接
|
||||
func DeleteCli(id uint64) {
|
||||
cliCache.Delete(id)
|
||||
// 遍历所有机器连接实例,删除指定机器id关联的连接...
|
||||
items := cliCache.Items()
|
||||
for _, v := range items {
|
||||
mi := v.Value.(*Cli).Info
|
||||
if mi.Id == id {
|
||||
cliCache.Delete(mi.Key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查缓存中的客户端是否可用,不可用则关闭客户端连接
|
||||
|
||||
@@ -2,7 +2,7 @@ package mcm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/machine/domain/entity"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"net"
|
||||
@@ -13,16 +13,20 @@ import (
|
||||
|
||||
// 机器信息
|
||||
type MachineInfo struct {
|
||||
Key string `json:"key"` // 缓存key
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Protocol int `json:"protocol"`
|
||||
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Port int `json:"-"` // 端口号
|
||||
AuthMethod int8 `json:"-"` // 授权认证方式
|
||||
Username string `json:"-"` // 用户名
|
||||
Password string `json:"-"`
|
||||
Passphrase string `json:"-"` // 私钥口令
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Port int `json:"-"` // 端口号
|
||||
|
||||
AuthCertName string `json:"authCertName"`
|
||||
AuthCertType tagentity.AuthCertType `json:"-"`
|
||||
AuthMethod int8 `json:"-"` // 授权认证方式
|
||||
Username string `json:"-"` // 用户名
|
||||
Password string `json:"-"`
|
||||
Passphrase string `json:"-"` // 私钥口令
|
||||
|
||||
SshTunnelMachine *MachineInfo `json:"-"` // ssh隧道机器
|
||||
TempSshMachineId uint64 `json:"-"` // ssh隧道机器id,用于记录隧道机器id,连接出错后关闭隧道
|
||||
@@ -118,9 +122,9 @@ func GetSshClient(m *MachineInfo, jumpClient *ssh.Client) (*ssh.Client, error) {
|
||||
},
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
if m.AuthMethod == entity.AuthCertAuthMethodPassword {
|
||||
if m.AuthMethod == int8(tagentity.AuthCertCiphertextTypePassword) {
|
||||
config.Auth = []ssh.AuthMethod{ssh.Password(m.Password)}
|
||||
} else if m.AuthMethod == entity.MachineAuthMethodPublicKey {
|
||||
} else if m.AuthMethod == int8(tagentity.AuthCertCiphertextTypePrivateKey) {
|
||||
var key ssh.Signer
|
||||
var err error
|
||||
|
||||
|
||||
@@ -49,9 +49,9 @@ func InitMachineRouter(router *gin.RouterGroup) {
|
||||
req.BatchSetGroup(machines, reqs[:])
|
||||
|
||||
// 终端连接
|
||||
machines.GET(":machineId/terminal", m.WsSSH)
|
||||
machines.GET("terminal/:ac", m.WsSSH)
|
||||
|
||||
// 终端连接
|
||||
machines.GET(":machineId/rdp", m.WsGuacamole)
|
||||
machines.GET("rdp/:ac", m.WsGuacamole)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"mayfly-go/internal/mongo/domain/repository"
|
||||
"mayfly-go/internal/mongo/mgm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
@@ -59,7 +60,7 @@ func (d *mongoAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
},
|
||||
func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceType: consts.TagResourceTypeMongo,
|
||||
ResourceType: tagentity.TagTypeMongo,
|
||||
ResourceCode: mongoEntity.Code,
|
||||
})
|
||||
})
|
||||
@@ -90,7 +91,7 @@ func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagIds ..
|
||||
return d.Insert(ctx, m)
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceType: consts.TagResourceTypeMongo,
|
||||
ResourceType: tagentity.TagTypeMongo,
|
||||
ResourceCode: m.Code,
|
||||
TagIds: tagIds,
|
||||
})
|
||||
@@ -113,7 +114,7 @@ func (d *mongoAppImpl) SaveMongo(ctx context.Context, m *entity.Mongo, tagIds ..
|
||||
return d.UpdateById(ctx, m)
|
||||
}, func(ctx context.Context) error {
|
||||
return d.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceType: consts.TagResourceTypeMongo,
|
||||
ResourceType: tagentity.TagTypeMongo,
|
||||
ResourceCode: oldMongo.Code,
|
||||
TagIds: tagIds,
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"mayfly-go/internal/redis/domain/repository"
|
||||
"mayfly-go/internal/redis/rdm"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
tagenttiy "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
@@ -107,7 +108,7 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, re *entity.Redis, tagIds .
|
||||
return r.Insert(ctx, re)
|
||||
}, func(ctx context.Context) error {
|
||||
return r.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceType: consts.TagResourceTypeRedis,
|
||||
ResourceType: tagenttiy.TagTypeRedis,
|
||||
ResourceCode: re.Code,
|
||||
TagIds: tagIds,
|
||||
})
|
||||
@@ -138,7 +139,7 @@ func (r *redisAppImpl) SaveRedis(ctx context.Context, re *entity.Redis, tagIds .
|
||||
return r.UpdateById(ctx, re)
|
||||
}, func(ctx context.Context) error {
|
||||
return r.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceType: consts.TagResourceTypeRedis,
|
||||
ResourceType: tagenttiy.TagTypeRedis,
|
||||
ResourceCode: oldRedis.Code,
|
||||
TagIds: tagIds,
|
||||
})
|
||||
@@ -161,7 +162,7 @@ func (r *redisAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
return r.DeleteById(ctx, id)
|
||||
}, func(ctx context.Context) error {
|
||||
return r.tagApp.SaveResource(ctx, &tagapp.SaveResourceTagParam{
|
||||
ResourceType: consts.TagResourceTypeRedis,
|
||||
ResourceType: tagenttiy.TagTypeRedis,
|
||||
ResourceCode: re.Code,
|
||||
})
|
||||
})
|
||||
|
||||
29
server/internal/tag/api/resource_auth_cert.go
Normal file
29
server/internal/tag/api/resource_auth_cert.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/application"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
)
|
||||
|
||||
type ResourceAuthCert struct {
|
||||
ResourceAuthCertApp application.ResourceAuthCert `inject:""`
|
||||
}
|
||||
|
||||
func (r *ResourceAuthCert) ListByQuery(rc *req.Ctx) {
|
||||
cond := new(entity.ResourceAuthCert)
|
||||
cond.ResourceCode = rc.Query("resourceCode")
|
||||
cond.ResourceType = int8(rc.QueryInt("resourceType"))
|
||||
cond.Type = entity.AuthCertType(rc.QueryInt("type"))
|
||||
cond.CiphertextType = entity.AuthCertCiphertextType(rc.QueryInt("ciphertextType"))
|
||||
cond.Name = rc.Query("name")
|
||||
|
||||
var racs []*entity.ResourceAuthCert
|
||||
res, err := r.ResourceAuthCertApp.PageQuery(cond, rc.GetPageParam(), &racs)
|
||||
biz.ErrIsNil(err)
|
||||
for _, rac := range racs {
|
||||
rac.CiphertextDecrypt()
|
||||
}
|
||||
rc.ResData = res
|
||||
}
|
||||
@@ -15,14 +15,16 @@ import (
|
||||
|
||||
type TagTree struct {
|
||||
TagTreeApp application.TagTree `inject:""`
|
||||
|
||||
ResourceAuthCertApp application.ResourceAuthCert `inject:""`
|
||||
}
|
||||
|
||||
func (p *TagTree) GetTagTree(rc *req.Ctx) {
|
||||
tagType := rc.QueryInt("type")
|
||||
tagType := entity.TagType(rc.QueryInt("type"))
|
||||
// 超管返回所有标签树
|
||||
if rc.GetLoginAccount().Id == consts.AdminId {
|
||||
var tagTrees vo.TagTreeVOS
|
||||
p.TagTreeApp.ListByQuery(&entity.TagTreeQuery{Type: int8(tagType)}, &tagTrees)
|
||||
p.TagTreeApp.ListByQuery(&entity.TagTreeQuery{Type: tagType}, &tagTrees)
|
||||
rc.ResData = tagTrees.ToTrees(0)
|
||||
return
|
||||
}
|
||||
@@ -40,7 +42,7 @@ func (p *TagTree) GetTagTree(rc *req.Ctx) {
|
||||
|
||||
// 获取所有以root标签开头的子标签
|
||||
var tags []*entity.TagTree
|
||||
p.TagTreeApp.ListByQuery(&entity.TagTreeQuery{CodePathLikes: collx.MapKeys(rootTag), Type: int8(tagType)}, &tags)
|
||||
p.TagTreeApp.ListByQuery(&entity.TagTreeQuery{CodePathLikes: collx.MapKeys(rootTag), Type: tagType}, &tags)
|
||||
|
||||
tagTrees := make(vo.TagTreeVOS, 0)
|
||||
for _, tag := range tags {
|
||||
@@ -81,12 +83,14 @@ func (p *TagTree) DelTagTree(rc *req.Ctx) {
|
||||
biz.ErrIsNil(p.TagTreeApp.Delete(rc.MetaCtx, uint64(rc.PathParamInt("id"))))
|
||||
}
|
||||
|
||||
// 获取用户可操作的资源标签路径
|
||||
// 获取用户可操作的标签路径
|
||||
func (p *TagTree) TagResources(rc *req.Ctx) {
|
||||
resourceType := int8(rc.PathParamInt("rtype"))
|
||||
tagResources := p.TagTreeApp.GetAccountTagResources(rc.GetLoginAccount().Id, resourceType, "")
|
||||
tagPath2Resource := collx.ArrayToMap[entity.TagTree, string](tagResources, func(tagResource entity.TagTree) string {
|
||||
return tagResource.GetParentPath()
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
tagResources := p.TagTreeApp.GetAccountTagResources(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType)})
|
||||
|
||||
tagPath2Resource := collx.ArrayToMap[*entity.TagTree, string](tagResources, func(tagResource *entity.TagTree) string {
|
||||
return tagResource.GetParentPath(1)
|
||||
})
|
||||
|
||||
tagPaths := collx.MapKeys(tagPath2Resource)
|
||||
|
||||
22
server/internal/tag/api/vo/resource_auth_cert.go
Normal file
22
server/internal/tag/api/vo/resource_auth_cert.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package vo
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ResourceAuthCert struct {
|
||||
Id uint64 `json:"id"`
|
||||
Name string `json:"name"` // 名称
|
||||
ResourceCode string `json:"resourceCode"` // 资源编号
|
||||
ResourceType int8 `json:"resourceType"` // 资源类型
|
||||
Username string `json:"username"` // 用户名
|
||||
Ciphertext string `json:"ciphertext"` // 密文
|
||||
CiphertextType entity.AuthCertCiphertextType `json:"ciphertextType"` // 密文类型
|
||||
Extra model.Map[string, any] `json:"extra"` // 账号需要的其他额外信息(如秘钥口令等)
|
||||
Type entity.AuthCertType `json:"type"` // 凭证类型
|
||||
Remark string `json:"remark"` // 备注
|
||||
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
}
|
||||
@@ -7,4 +7,5 @@ import (
|
||||
func InitIoc() {
|
||||
ioc.Register(new(tagTreeAppImpl), ioc.WithComponentName("TagTreeApp"))
|
||||
ioc.Register(new(teamAppImpl), ioc.WithComponentName("TeamApp"))
|
||||
ioc.Register(new(resourceAuthCertAppImpl), ioc.WithComponentName("ResourceAuthCertApp"))
|
||||
}
|
||||
|
||||
268
server/internal/tag/application/resouce_auth_cert.go
Normal file
268
server/internal/tag/application/resouce_auth_cert.go
Normal file
@@ -0,0 +1,268 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
|
||||
type SaveAuthCertParam struct {
|
||||
ResourceCode string
|
||||
// 资源标签类型
|
||||
ResourceType entity.TagType
|
||||
|
||||
// 授权凭证类型
|
||||
AuthCertTagType entity.TagType
|
||||
|
||||
// 空数组则为删除该资源绑定的授权凭证
|
||||
AuthCerts []*entity.ResourceAuthCert
|
||||
}
|
||||
|
||||
type ResourceAuthCert interface {
|
||||
base.App[*entity.ResourceAuthCert]
|
||||
|
||||
// SaveAuthCert 保存资源授权凭证信息,不可放于事务中
|
||||
SaveAuthCert(ctx context.Context, param *SaveAuthCertParam) error
|
||||
|
||||
// GetAuthCert 根据授权凭证名称获取授权凭证
|
||||
GetAuthCert(authCertName string) (*entity.ResourceAuthCert, error)
|
||||
|
||||
// GetResourceAuthCert 获取资源授权凭证,默认获取特权账号,若没有则返回第一个
|
||||
GetResourceAuthCert(resourceType entity.TagType, resourceCode string) (*entity.ResourceAuthCert, error)
|
||||
|
||||
// GetAccountAuthCert 获取账号有权限操作的授权凭证信息
|
||||
GetAccountAuthCert(accountId uint64, authCertTagType entity.TagType, tagPath ...string) []*entity.ResourceAuthCert
|
||||
|
||||
// FillAuthCert 填充资源的授权凭证信息
|
||||
// @param resources 实现了entity.IAuthCert接口的资源信息
|
||||
FillAuthCert(authCerts []*entity.ResourceAuthCert, resources ...entity.IAuthCert)
|
||||
}
|
||||
|
||||
type resourceAuthCertAppImpl struct {
|
||||
base.AppImpl[*entity.ResourceAuthCert, repository.ResourceAuthCert]
|
||||
|
||||
tagTreeApp TagTree `inject:"TagTreeApp"`
|
||||
}
|
||||
|
||||
// 注入Repo
|
||||
func (r *resourceAuthCertAppImpl) InjectResourceAuthCertRepo(resourceAuthCertRepo repository.ResourceAuthCert) {
|
||||
r.Repo = resourceAuthCertRepo
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *SaveAuthCertParam) error {
|
||||
resourceCode := params.ResourceCode
|
||||
resourceType := int8(params.ResourceType)
|
||||
resourceAuthCerts := params.AuthCerts
|
||||
authCertTagType := params.AuthCertTagType
|
||||
|
||||
if authCertTagType == 0 {
|
||||
return errorx.NewBiz("资源授权凭证所属标签类型不能为空")
|
||||
}
|
||||
|
||||
if resourceCode == "" {
|
||||
return errorx.NewBiz("资源授权凭证的资源编号不能为空")
|
||||
}
|
||||
|
||||
// 删除授权信息
|
||||
if len(resourceAuthCerts) == 0 {
|
||||
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除该资源下的所有授权凭证资源标签
|
||||
if err := r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{
|
||||
ResourceCode: resourceCode,
|
||||
ResourceType: params.ResourceType,
|
||||
ChildType: authCertTagType,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
name2AuthCert := make(map[string]*entity.ResourceAuthCert, 0)
|
||||
for _, resourceAuthCert := range resourceAuthCerts {
|
||||
resourceAuthCert.ResourceCode = resourceCode
|
||||
resourceAuthCert.ResourceType = int8(resourceType)
|
||||
name2AuthCert[resourceAuthCert.Name] = resourceAuthCert
|
||||
|
||||
existNameAc := &entity.ResourceAuthCert{Name: resourceAuthCert.Name}
|
||||
if r.GetBy(existNameAc) == nil && existNameAc.ResourceCode != resourceCode {
|
||||
return errorx.NewBiz("授权凭证的名称不能重复[%s]", resourceAuthCert.Name)
|
||||
}
|
||||
|
||||
// 公共授权凭证,则无需进行密文加密,密文即为公共授权凭证名
|
||||
if resourceAuthCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
||||
continue
|
||||
}
|
||||
|
||||
// 密文加密
|
||||
if err := resourceAuthCert.CiphertextEncrypt(); err != nil {
|
||||
return errorx.NewBiz(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
var oldAuthCert []*entity.ResourceAuthCert
|
||||
r.ListByCond(&entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType}, &oldAuthCert)
|
||||
|
||||
var adds, dels, unmodifys []string
|
||||
if len(oldAuthCert) == 0 {
|
||||
adds = collx.MapKeys(name2AuthCert)
|
||||
} else {
|
||||
oldNames := collx.ArrayMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
|
||||
return ac.Name
|
||||
})
|
||||
adds, dels, unmodifys = collx.ArrayCompare[string](collx.MapKeys(name2AuthCert), oldNames)
|
||||
}
|
||||
|
||||
addAuthCerts := make([]*entity.ResourceAuthCert, 0)
|
||||
for _, add := range adds {
|
||||
addAc := name2AuthCert[add]
|
||||
addAc.Id = 0
|
||||
addAuthCerts = append(addAuthCerts, addAc)
|
||||
}
|
||||
|
||||
// 处理新增的授权凭证
|
||||
if len(addAuthCerts) > 0 {
|
||||
if err := r.BatchInsert(ctx, addAuthCerts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取资源编号对应的资源标签信息
|
||||
var resourceTags []*entity.TagTree
|
||||
r.tagTreeApp.ListByCond(&entity.TagTree{Type: params.ResourceType, Code: resourceCode}, &resourceTags)
|
||||
// 资源标签id(相当于父tag id)
|
||||
resourceTagIds := collx.ArrayMap(resourceTags, func(tag *entity.TagTree) uint64 {
|
||||
return tag.Id
|
||||
})
|
||||
|
||||
// 保存授权凭证类型的资源标签
|
||||
for _, authCert := range addAuthCerts {
|
||||
if err := r.tagTreeApp.SaveResource(ctx, &SaveResourceTagParam{
|
||||
ResourceCode: authCert.Name,
|
||||
ResourceType: authCertTagType,
|
||||
ResourceName: authCert.Username,
|
||||
TagIds: resourceTagIds,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, del := range dels {
|
||||
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType, Name: del}); err != nil {
|
||||
return err
|
||||
}
|
||||
// 删除对应授权凭证资源标签
|
||||
if err := r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{
|
||||
ResourceCode: del,
|
||||
ResourceType: authCertTagType,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, unmodify := range unmodifys {
|
||||
unmodifyAc := name2AuthCert[unmodify]
|
||||
if unmodifyAc.Id == 0 {
|
||||
continue
|
||||
}
|
||||
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) GetAuthCert(authCertName string) (*entity.ResourceAuthCert, error) {
|
||||
authCert := &entity.ResourceAuthCert{Name: authCertName}
|
||||
if err := r.GetBy(authCert); err != nil {
|
||||
return nil, errorx.NewBiz("该授权凭证不存在")
|
||||
}
|
||||
|
||||
return r.decryptAuthCert(authCert)
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) GetResourceAuthCert(resourceType entity.TagType, resourceCode string) (*entity.ResourceAuthCert, error) {
|
||||
var resourceAuthCerts []*entity.ResourceAuthCert
|
||||
if err := r.ListByCond(&entity.ResourceAuthCert{
|
||||
ResourceType: int8(resourceType),
|
||||
ResourceCode: resourceCode,
|
||||
}, &resourceAuthCerts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(resourceAuthCerts) == 0 {
|
||||
return nil, errorx.NewBiz("该资源不存在授权凭证账号")
|
||||
}
|
||||
|
||||
for _, resourceAuthCert := range resourceAuthCerts {
|
||||
if resourceAuthCert.Type == entity.AuthCertTypePrivileged {
|
||||
return r.decryptAuthCert(resourceAuthCert)
|
||||
}
|
||||
}
|
||||
|
||||
return r.decryptAuthCert(resourceAuthCerts[0])
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) GetAccountAuthCert(accountId uint64, authCertTagType entity.TagType, tagPath ...string) []*entity.ResourceAuthCert {
|
||||
// 获取用户有权限操作的授权凭证资源标签
|
||||
tagQuery := &entity.TagTreeQuery{
|
||||
Type: authCertTagType,
|
||||
CodePathLikes: tagPath,
|
||||
}
|
||||
authCertTags := r.tagTreeApp.GetAccountTagResources(accountId, tagQuery)
|
||||
|
||||
// 获取所有授权凭证名称
|
||||
authCertNames := collx.ArrayMap(authCertTags, func(tag *entity.TagTree) string {
|
||||
return tag.Code
|
||||
})
|
||||
|
||||
var authCerts []*entity.ResourceAuthCert
|
||||
r.GetRepo().ListByWheres(collx.M{
|
||||
"name in ?": collx.ArrayDeduplicate(authCertNames),
|
||||
}, &authCerts)
|
||||
|
||||
return authCerts
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) FillAuthCert(authCerts []*entity.ResourceAuthCert, resources ...entity.IAuthCert) {
|
||||
if len(resources) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 资源编号 -> 资源
|
||||
resourceCode2Resouce := collx.ArrayToMap(resources, func(ac entity.IAuthCert) string {
|
||||
return ac.GetCode()
|
||||
})
|
||||
|
||||
for _, authCert := range authCerts {
|
||||
resourceCode2Resouce[authCert.ResourceCode].SetAuthCert(entity.AuthCert{
|
||||
Name: authCert.Name,
|
||||
Username: authCert.Username,
|
||||
Type: authCert.Type,
|
||||
CiphertextType: authCert.CiphertextType,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 解密授权凭证信息
|
||||
func (r *resourceAuthCertAppImpl) decryptAuthCert(authCert *entity.ResourceAuthCert) (*entity.ResourceAuthCert, error) {
|
||||
if authCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
||||
// 如果是公共授权凭证,则密文为公共授权凭证名称,需要使用该名称再去获取对应的授权凭证
|
||||
authCert = &entity.ResourceAuthCert{Name: authCert.Ciphertext}
|
||||
if err := r.GetBy(authCert); err != nil {
|
||||
return nil, errorx.NewBiz("该公共授权凭证[%s]不存在", authCert.Ciphertext)
|
||||
}
|
||||
}
|
||||
|
||||
if err := authCert.CiphertextDecrypt(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return authCert, nil
|
||||
}
|
||||
@@ -16,9 +16,19 @@ import (
|
||||
type SaveResourceTagParam struct {
|
||||
ResourceCode string
|
||||
ResourceName string
|
||||
ResourceType int8
|
||||
ResourceType entity.TagType
|
||||
|
||||
TagIds []uint64 // 关联标签,相当于父标签 pid
|
||||
TagIds []uint64 // 关联标签,相当于父标签 pid,空数组则为删除该资源绑定的标签
|
||||
}
|
||||
|
||||
type DelResourceTagParam struct {
|
||||
ResourceCode string
|
||||
ResourceType entity.TagType
|
||||
|
||||
Pid uint64 //父标签 pid
|
||||
|
||||
// 要删除的子节点类型,若存在值,则为删除资源标签下的指定类型的子标签
|
||||
ChildType entity.TagType
|
||||
}
|
||||
|
||||
type TagTree interface {
|
||||
@@ -34,7 +44,7 @@ type TagTree interface {
|
||||
// @param accountId 账号id
|
||||
// @param resourceType 资源类型
|
||||
// @param tagPath 访问指定的标签路径下关联的资源
|
||||
GetAccountTagResources(accountId uint64, resourceType int8, tagPath string) []entity.TagTree
|
||||
GetAccountTagResources(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree
|
||||
|
||||
// 获取指定账号有权限操作的资源codes
|
||||
GetAccountResourceCodes(accountId uint64, resourceType int8, tagPath string) []string
|
||||
@@ -42,6 +52,9 @@ type TagTree interface {
|
||||
// SaveResource 保存资源标签
|
||||
SaveResource(ctx context.Context, req *SaveResourceTagParam) error
|
||||
|
||||
// DeleteResource 删除资源标签,会删除该资源下所有子节点信息
|
||||
DeleteResource(ctx context.Context, param *DelResourceTagParam) error
|
||||
|
||||
// 根据资源信息获取对应的标签路径列表
|
||||
ListTagPathByResource(resourceType int8, resourceCode string) []string
|
||||
|
||||
@@ -115,12 +128,12 @@ func (p *tagTreeAppImpl) ListByQuery(condition *entity.TagTreeQuery, toEntity an
|
||||
p.GetRepo().SelectByCondition(condition, toEntity)
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) GetAccountTagResources(accountId uint64, resourceType int8, tagPath string) []entity.TagTree {
|
||||
func (p *tagTreeAppImpl) GetAccountTagResources(accountId uint64, query *entity.TagTreeQuery) []*entity.TagTree {
|
||||
tagResourceQuery := &entity.TagTreeQuery{
|
||||
Type: resourceType,
|
||||
Type: query.Type,
|
||||
}
|
||||
|
||||
var tagResources []entity.TagTree
|
||||
var tagResources []*entity.TagTree
|
||||
var accountTagPaths []string
|
||||
|
||||
if accountId != consts.AdminId {
|
||||
@@ -131,16 +144,37 @@ func (p *tagTreeAppImpl) GetAccountTagResources(accountId uint64, resourceType i
|
||||
}
|
||||
}
|
||||
|
||||
tagResourceQuery.CodePathLike = tagPath
|
||||
// 去除空字符串标签
|
||||
tagPaths := collx.ArrayRemoveBlank(query.CodePathLikes)
|
||||
// 如果需要查询指定标签下的资源标签,则需要与用户拥有的权限进行过滤,避免越权
|
||||
if len(tagPaths) > 0 {
|
||||
// admin 则直接赋值需要获取的标签
|
||||
if len(accountTagPaths) == 0 {
|
||||
accountTagPaths = tagPaths
|
||||
} else {
|
||||
accountTagPaths = collx.ArrayFilter[string](tagPaths, func(s string) bool {
|
||||
for _, v := range accountTagPaths {
|
||||
// 要过滤的权限需要在用户拥有的子标签下, accountTagPath: test/ tagPath: test/test1/ -> true
|
||||
if strings.HasPrefix(v, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// tagResourceQuery.CodePathLike = tagPath
|
||||
tagResourceQuery.Codes = query.Codes
|
||||
tagResourceQuery.CodePathLikes = accountTagPaths
|
||||
p.ListByQuery(tagResourceQuery, &tagResources)
|
||||
return tagResources
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) GetAccountResourceCodes(accountId uint64, resourceType int8, tagPath string) []string {
|
||||
tagResources := p.GetAccountTagResources(accountId, resourceType, tagPath)
|
||||
tagResources := p.GetAccountTagResources(accountId, &entity.TagTreeQuery{Type: entity.TagType(resourceType), CodePathLikes: []string{tagPath}})
|
||||
// resouce code去重
|
||||
code2Resource := collx.ArrayToMap[entity.TagTree, string](tagResources, func(val entity.TagTree) string {
|
||||
code2Resource := collx.ArrayToMap[*entity.TagTree, string](tagResources, func(val *entity.TagTree) string {
|
||||
return val.Code
|
||||
})
|
||||
|
||||
@@ -149,7 +183,7 @@ func (p *tagTreeAppImpl) GetAccountResourceCodes(accountId uint64, resourceType
|
||||
|
||||
func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagParam) error {
|
||||
resourceCode := req.ResourceCode
|
||||
resourceType := req.ResourceType
|
||||
resourceType := entity.TagType(req.ResourceType)
|
||||
resourceName := req.ResourceName
|
||||
tagIds := req.TagIds
|
||||
|
||||
@@ -162,7 +196,10 @@ func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagP
|
||||
|
||||
// 如果tagIds为空数组,则为删除该资源标签
|
||||
if len(tagIds) == 0 {
|
||||
return p.DeleteByCond(ctx, &entity.TagTree{Code: resourceCode, Type: resourceType})
|
||||
return p.DeleteResource(ctx, &DelResourceTagParam{
|
||||
ResourceType: resourceType,
|
||||
ResourceCode: resourceCode,
|
||||
})
|
||||
}
|
||||
|
||||
if resourceName == "" {
|
||||
@@ -205,10 +242,36 @@ func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagP
|
||||
|
||||
if len(delTagIds) > 0 {
|
||||
for _, tagId := range delTagIds {
|
||||
cond := &entity.TagTree{Code: resourceCode, Type: resourceType, Pid: tagId}
|
||||
if err := p.DeleteByCond(ctx, cond); err != nil {
|
||||
if err := p.DeleteResource(ctx, &DelResourceTagParam{
|
||||
ResourceType: resourceType,
|
||||
ResourceCode: resourceCode,
|
||||
Pid: tagId,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *tagTreeAppImpl) DeleteResource(ctx context.Context, param *DelResourceTagParam) error {
|
||||
// 获取资源编号对应的资源标签信息
|
||||
var resourceTags []*entity.TagTree
|
||||
p.ListByCond(&entity.TagTree{Type: param.ResourceType, Code: param.ResourceCode, Pid: param.Pid}, &resourceTags)
|
||||
if len(resourceTags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
delTagType := param.ChildType
|
||||
for _, resourceTag := range resourceTags {
|
||||
// 删除所有code_path下的子标签
|
||||
if err := p.DeleteByWheres(ctx, collx.M{
|
||||
"code_path LIKE ?": resourceTag.CodePath + "%",
|
||||
"type = ?": delTagType,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +280,7 @@ func (p *tagTreeAppImpl) SaveResource(ctx context.Context, req *SaveResourceTagP
|
||||
|
||||
func (p *tagTreeAppImpl) ListTagPathByResource(resourceType int8, resourceCode string) []string {
|
||||
var trs []*entity.TagTree
|
||||
p.ListByCond(&entity.TagTree{Type: resourceType, Code: resourceCode}, &trs)
|
||||
p.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &trs)
|
||||
return collx.ArrayMap(trs, func(tr *entity.TagTree) string {
|
||||
return tr.CodePath
|
||||
})
|
||||
@@ -260,7 +323,7 @@ func (p *tagTreeAppImpl) FillTagInfo(resources ...entity.ITagResource) {
|
||||
|
||||
for _, tr := range tagResources {
|
||||
// 赋值标签信息
|
||||
resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{TagId: tr.Pid, TagPath: tr.GetParentPath()})
|
||||
resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{TagId: tr.Pid, TagPath: tr.GetParentPath(0)})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ type TagTreeQuery struct {
|
||||
model.Model
|
||||
|
||||
Pid uint64
|
||||
Type int8 `json:"type"`
|
||||
Code string `json:"code"` // 标识
|
||||
Type TagType `json:"type"`
|
||||
Code string `json:"code"` // 标识
|
||||
Codes []string
|
||||
CodePath string `json:"codePath"` // 标识路径
|
||||
CodePaths []string
|
||||
|
||||
123
server/internal/tag/domain/entity/resource_auth_cert.go
Normal file
123
server/internal/tag/domain/entity/resource_auth_cert.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"mayfly-go/internal/common/utils"
|
||||
"mayfly-go/pkg/model"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
|
||||
// 资源授权凭证
|
||||
type ResourceAuthCert struct {
|
||||
model.Model
|
||||
|
||||
Name string `json:"name"` // 名称(全局唯一)
|
||||
|
||||
ResourceCode string `json:"resourceCode"` // 资源编号
|
||||
ResourceType int8 `json:"resourceType"` // 资源类型
|
||||
Username string `json:"username"` // 用户名
|
||||
Ciphertext string `json:"ciphertext"` // 密文
|
||||
CiphertextType AuthCertCiphertextType `json:"ciphertextType"` // 密文类型
|
||||
Extra model.Map[string, any] `json:"extra"` // 账号需要的其他额外信息(如秘钥口令等)
|
||||
Type AuthCertType `json:"type"` // 凭证类型
|
||||
Remark string `json:"remark"` // 备注
|
||||
}
|
||||
|
||||
func (m *ResourceAuthCert) CiphertextEncrypt() error {
|
||||
// 密码替换为加密后的密码
|
||||
password, err := utils.PwdAesEncrypt(m.Ciphertext)
|
||||
if err != nil {
|
||||
return errors.New("加密密文失败")
|
||||
}
|
||||
m.Ciphertext = password
|
||||
|
||||
// 加密秘钥口令
|
||||
if m.CiphertextType == AuthCertCiphertextTypePrivateKey {
|
||||
passphrase := cast.ToString(m.Extra["passphrase"])
|
||||
if passphrase != "" {
|
||||
passphrase, err := utils.PwdAesEncrypt(passphrase)
|
||||
if err != nil {
|
||||
return errors.New("加密秘钥口令失败")
|
||||
}
|
||||
m.Extra["passphrase"] = passphrase
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ResourceAuthCert) CiphertextDecrypt() error {
|
||||
// 密码替换为解密后的密码
|
||||
password, err := utils.PwdAesDecrypt(m.Ciphertext)
|
||||
if err != nil {
|
||||
return errors.New("解密密文失败")
|
||||
}
|
||||
m.Ciphertext = password
|
||||
|
||||
// 加密秘钥口令
|
||||
if m.CiphertextType == AuthCertCiphertextTypePrivateKey {
|
||||
passphrase := cast.ToString(m.Extra["passphrase"])
|
||||
if passphrase != "" {
|
||||
passphrase, err := utils.PwdAesDecrypt(passphrase)
|
||||
if err != nil {
|
||||
return errors.New("解密秘钥口令失败")
|
||||
}
|
||||
m.Extra["passphrase"] = passphrase
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 密文类型
|
||||
type AuthCertCiphertextType int8
|
||||
|
||||
// 凭证类型
|
||||
type AuthCertType int8
|
||||
|
||||
const (
|
||||
AuthCertCiphertextTypePublic AuthCertCiphertextType = -1 // 公共授权凭证
|
||||
AuthCertCiphertextTypePassword AuthCertCiphertextType = 1 // 密码
|
||||
AuthCertCiphertextTypePrivateKey AuthCertCiphertextType = 2 // 私钥
|
||||
|
||||
AuthCertTypePublic AuthCertType = 2 // 公共凭证(可多个资源共享该授权凭证)
|
||||
AuthCertTypePrivate AuthCertType = 1 // 普通私有凭证
|
||||
AuthCertTypePrivileged AuthCertType = 11 // 特权私有凭证
|
||||
AuthCertTypePrivateDefault AuthCertType = 12 // 默认私有凭证
|
||||
)
|
||||
|
||||
// 授权凭证接口,填充资源授权凭证信息
|
||||
type IAuthCert interface {
|
||||
// 获取资源code
|
||||
GetCode() string
|
||||
|
||||
// 设置授权信息
|
||||
SetAuthCert(ac AuthCert)
|
||||
}
|
||||
|
||||
// 资源关联的标签信息
|
||||
type AuthCert struct {
|
||||
Name string `json:"name" gorm:"-"` // 名称
|
||||
Username string `json:"username" gorm:"-"` // 用户名
|
||||
CiphertextType AuthCertCiphertextType `json:"ciphertextType" gorm:"-"` // 密文类型
|
||||
Type AuthCertType `json:"type" gorm:"-"` // 凭证类型
|
||||
}
|
||||
|
||||
func (r *AuthCert) SetAuthCert(ac AuthCert) {
|
||||
r.Name = ac.Name
|
||||
r.Username = ac.Username
|
||||
r.Type = ac.Type
|
||||
r.CiphertextType = ac.CiphertextType
|
||||
}
|
||||
|
||||
// 资源标签列表
|
||||
type AuthCerts struct {
|
||||
AuthCerts []AuthCert `json:"authCerts" gorm:"-"`
|
||||
}
|
||||
|
||||
func (r *AuthCerts) SetAuthCert(rt AuthCert) {
|
||||
if r.AuthCerts == nil {
|
||||
r.AuthCerts = make([]AuthCert, 0)
|
||||
}
|
||||
r.AuthCerts = append(r.AuthCerts, rt)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/common/consts"
|
||||
"mayfly-go/pkg/model"
|
||||
"strings"
|
||||
)
|
||||
@@ -9,17 +10,28 @@ import (
|
||||
type TagTree struct {
|
||||
model.Model
|
||||
|
||||
Pid uint64 `json:"pid"`
|
||||
Type int8 `json:"type"` // 类型: -1.普通标签; 其他值则为对应的资源类型
|
||||
Code string `json:"code"` // 标识编码, 若类型不为-1,则为对应资源编码
|
||||
CodePath string `json:"codePath"` // 标识路径
|
||||
Name string `json:"name"` // 名称
|
||||
Remark string `json:"remark"` // 备注说明
|
||||
Pid uint64 `json:"pid"`
|
||||
Type TagType `json:"type"` // 类型: -1.普通标签; 其他值则为对应的资源类型
|
||||
Code string `json:"code"` // 标识编码, 若类型不为-1,则为对应资源编码
|
||||
CodePath string `json:"codePath"` // 标识路径
|
||||
Name string `json:"name"` // 名称
|
||||
Remark string `json:"remark"` // 备注说明
|
||||
}
|
||||
|
||||
type TagType int8
|
||||
|
||||
const (
|
||||
// 标识路径分隔符
|
||||
CodePathSeparator = "/"
|
||||
|
||||
TagTypeTag TagType = -1
|
||||
TagTypeMachine TagType = TagType(consts.TagResourceTypeMachine)
|
||||
TagTypeDb TagType = TagType(consts.TagResourceTypeDb)
|
||||
TagTypeRedis TagType = TagType(consts.TagResourceTypeRedis)
|
||||
TagTypeMongo TagType = TagType(consts.TagResourceTypeMongo)
|
||||
|
||||
TagTypeMachineAuthCert TagType = 11 // 机器-授权凭证
|
||||
TagTypeDbAuthCert TagType = 21 // DB-授权凭证
|
||||
)
|
||||
|
||||
// GetRootCode 获取根路径信息
|
||||
@@ -27,19 +39,25 @@ func (pt *TagTree) GetRootCode() string {
|
||||
return strings.Split(pt.CodePath, CodePathSeparator)[0]
|
||||
}
|
||||
|
||||
// GetParentPath 获取父标签路径, 如CodePath = test/test1/test2/ -> test/test1/
|
||||
func (pt *TagTree) GetParentPath() string {
|
||||
// 去掉末尾的分隔符
|
||||
input := strings.TrimRight(pt.CodePath, CodePathSeparator)
|
||||
// GetParentPath 获取父标签路径, 如CodePath = test/test1/test2/ -> index = 0 => test/test1/ index = 1 => test/
|
||||
func (pt *TagTree) GetParentPath(index int) string {
|
||||
// 去除末尾的斜杠
|
||||
codePath := strings.TrimSuffix(pt.CodePath, "/")
|
||||
|
||||
// 查找倒数第二个连字符位置
|
||||
lastHyphenIndex := strings.LastIndex(input, CodePathSeparator)
|
||||
if lastHyphenIndex == -1 {
|
||||
return ""
|
||||
// 使用 Split 方法将路径按斜杠分割成切片
|
||||
paths := strings.Split(codePath, "/")
|
||||
|
||||
// 确保索引在有效范围内
|
||||
if index < 0 {
|
||||
index = 0
|
||||
} else if index > len(paths)-2 {
|
||||
index = len(paths) - 2
|
||||
}
|
||||
|
||||
// 截取字符串
|
||||
return input[:lastHyphenIndex+1]
|
||||
// 按索引拼接父标签路径
|
||||
parentPath := strings.Join(paths[:len(paths)-index-1], "/")
|
||||
|
||||
return parentPath + "/"
|
||||
}
|
||||
|
||||
// 标签接口资源,如果要实现资源结构体填充标签信息,则资源结构体需要实现该接口
|
||||
|
||||
10
server/internal/tag/domain/repository/resource_auth_cert.go
Normal file
10
server/internal/tag/domain/repository/resource_auth_cert.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
)
|
||||
|
||||
type ResourceAuthCert interface {
|
||||
base.Repo[*entity.ResourceAuthCert]
|
||||
}
|
||||
@@ -9,4 +9,5 @@ func InitIoc() {
|
||||
ioc.Register(newTagTreeTeamRepo(), ioc.WithComponentName("TagTreeTeamRepo"))
|
||||
ioc.Register(newTeamRepo(), ioc.WithComponentName("TeamRepo"))
|
||||
ioc.Register(newTeamMemberRepo(), ioc.WithComponentName("TeamMemberRepo"))
|
||||
ioc.Register(newResourceAuthCertRepoImpl(), ioc.WithComponentName("ResourceAuthCertRepo"))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/internal/tag/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
)
|
||||
|
||||
type resourceAuthCertRepoImpl struct {
|
||||
base.RepoImpl[*entity.ResourceAuthCert]
|
||||
}
|
||||
|
||||
func newResourceAuthCertRepoImpl() repository.ResourceAuthCert {
|
||||
return &resourceAuthCertRepoImpl{base.RepoImpl[*entity.ResourceAuthCert]{M: new(entity.ResourceAuthCert)}}
|
||||
}
|
||||
24
server/internal/tag/router/resource_auth_cert.go
Normal file
24
server/internal/tag/router/resource_auth_cert.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/tag/api"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/req"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitResourceAuthCertRouter(router *gin.RouterGroup) {
|
||||
m := new(api.ResourceAuthCert)
|
||||
biz.ErrIsNil(ioc.Inject(m))
|
||||
|
||||
resourceAuthCert := router.Group("/auth-certs")
|
||||
{
|
||||
reqs := [...]*req.Conf{
|
||||
req.NewGet("", m.ListByQuery),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(resourceAuthCert, reqs[:])
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,5 @@ import "github.com/gin-gonic/gin"
|
||||
func Init(router *gin.RouterGroup) {
|
||||
InitTagTreeRouter(router)
|
||||
InitTeamRouter(router)
|
||||
InitResourceAuthCertRouter(router)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -31,12 +32,28 @@ type App[T model.ModelI] interface {
|
||||
// 使用指定gorm db执行,主要用于事务执行
|
||||
UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T) error
|
||||
|
||||
// UpdateByWheres 更新满足wheres条件的数据
|
||||
// @param wheres key => "age > ?" value => 10等
|
||||
UpdateByWheres(ctx context.Context, e T, wheres collx.M, columns ...string) error
|
||||
|
||||
// UpdateByWheresWithDb 使用指定gorm.Db更新满足wheres条件的数据
|
||||
// @param wheres key => "age > ?" value => 10等
|
||||
UpdateByWheresWithDb(ctx context.Context, db *gorm.DB, e T, wheres collx.M, columns ...string) error
|
||||
|
||||
// 根据实体主键删除实体
|
||||
DeleteById(ctx context.Context, id uint64) error
|
||||
|
||||
// 使用指定gorm db执行,主要用于事务执行
|
||||
DeleteByIdWithDb(ctx context.Context, db *gorm.DB, id uint64) error
|
||||
|
||||
// DeleteByWheres 根据wheres条件进行删除
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
DeleteByWheres(ctx context.Context, wheres collx.M) error
|
||||
|
||||
// DeleteByWheresWithDb 使用指定gorm.Db根据wheres条件进行删除
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
DeleteByWheresWithDb(ctx context.Context, db *gorm.DB, wheres collx.M) error
|
||||
|
||||
// 根据实体条件,更新参数udpateFields指定字段
|
||||
Updates(ctx context.Context, cond any, udpateFields map[string]any) error
|
||||
|
||||
@@ -64,6 +81,9 @@ type App[T model.ModelI] interface {
|
||||
// 根据条件查询数据映射至listModels
|
||||
ListByCond(cond any, listModels any, cols ...string) error
|
||||
|
||||
// PageQuery 分页查询
|
||||
PageQuery(cond any, pageParam *model.PageParam, toModels any) (*model.PageResult[any], error)
|
||||
|
||||
// 获取满足model中不为空的字段值条件的所有数据.
|
||||
//
|
||||
// @param list为数组类型 如 var users *[]User,可指定为非model结构体
|
||||
@@ -117,6 +137,14 @@ func (ai *AppImpl[T, R]) UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T)
|
||||
return ai.GetRepo().UpdateByIdWithDb(ctx, db, e)
|
||||
}
|
||||
|
||||
func (ai *AppImpl[T, R]) UpdateByWheres(ctx context.Context, e T, wheres collx.M, columns ...string) error {
|
||||
return ai.GetRepo().UpdateByWheres(ctx, e, wheres, columns...)
|
||||
}
|
||||
|
||||
func (ai *AppImpl[T, R]) UpdateByWheresWithDb(ctx context.Context, db *gorm.DB, e T, wheres collx.M, columns ...string) error {
|
||||
return ai.GetRepo().UpdateByWheresWithDb(ctx, db, e, wheres, columns...)
|
||||
}
|
||||
|
||||
// 根据实体条件,更新参数udpateFields指定字段 (单纯更新,不做其他业务逻辑处理)
|
||||
func (ai *AppImpl[T, R]) Updates(ctx context.Context, cond any, udpateFields map[string]any) error {
|
||||
return ai.GetRepo().Updates(cond, udpateFields)
|
||||
@@ -152,6 +180,14 @@ func (ai *AppImpl[T, R]) DeleteByCondWithDb(ctx context.Context, db *gorm.DB, co
|
||||
return ai.GetRepo().DeleteByCondWithDb(ctx, db, cond)
|
||||
}
|
||||
|
||||
func (ai *AppImpl[T, R]) DeleteByWheres(ctx context.Context, wheres collx.M) error {
|
||||
return ai.GetRepo().DeleteByWheres(ctx, wheres)
|
||||
}
|
||||
|
||||
func (ai *AppImpl[T, R]) DeleteByWheresWithDb(ctx context.Context, db *gorm.DB, wheres collx.M) error {
|
||||
return ai.GetRepo().DeleteByWheresWithDb(ctx, db, wheres)
|
||||
}
|
||||
|
||||
// 根据实体id查询
|
||||
func (ai *AppImpl[T, R]) GetById(e T, id uint64, cols ...string) (T, error) {
|
||||
if err := ai.GetRepo().GetById(e, id, cols...); err != nil {
|
||||
@@ -174,6 +210,11 @@ func (ai *AppImpl[T, R]) ListByCond(cond any, listModels any, cols ...string) er
|
||||
return ai.GetRepo().ListByCond(cond, listModels, cols...)
|
||||
}
|
||||
|
||||
// PageQuery 分页查询
|
||||
func (ai *AppImpl[T, R]) PageQuery(cond any, pageParam *model.PageParam, toModels any) (*model.PageResult[any], error) {
|
||||
return ai.GetRepo().PageQuery(cond, pageParam, toModels)
|
||||
}
|
||||
|
||||
// 获取满足model中不为空的字段值条件的所有数据.
|
||||
//
|
||||
// @param list为数组类型 如 var users *[]User,可指定为非model结构体
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -33,6 +34,14 @@ type Repo[T model.ModelI] interface {
|
||||
// 使用指定gorm db执行,主要用于事务执行
|
||||
UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T, columns ...string) error
|
||||
|
||||
// UpdateByWheres 更新满足wheres条件的数据
|
||||
// @param wheres key => "age > ?" value => 10等
|
||||
UpdateByWheres(ctx context.Context, e T, wheres collx.M, columns ...string) error
|
||||
|
||||
// UpdateByWheresWithDb 使用指定gorm.Db更新满足wheres条件的数据
|
||||
// @param wheres key => "age > ?" value => 10等
|
||||
UpdateByWheresWithDb(ctx context.Context, db *gorm.DB, e T, wheres collx.M, columns ...string) error
|
||||
|
||||
// 保存实体,实体IsCreate返回true则新增,否则更新
|
||||
Save(ctx context.Context, e T) error
|
||||
|
||||
@@ -55,6 +64,14 @@ type Repo[T model.ModelI] interface {
|
||||
// 使用指定gorm db执行,主要用于事务执行
|
||||
DeleteByCondWithDb(ctx context.Context, db *gorm.DB, cond any) error
|
||||
|
||||
// DeleteByWheres 根据wheres条件进行删除
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
DeleteByWheres(ctx context.Context, wheres collx.M) error
|
||||
|
||||
// DeleteByWheresWithDb 使用指定gorm.Db根据wheres条件进行删除
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
DeleteByWheresWithDb(ctx context.Context, db *gorm.DB, wheres collx.M) error
|
||||
|
||||
// 根据实体id查询
|
||||
GetById(e T, id uint64, cols ...string) error
|
||||
|
||||
@@ -67,6 +84,13 @@ type Repo[T model.ModelI] interface {
|
||||
// 根据实体条件查询数据映射至listModels
|
||||
ListByCond(cond any, listModels any, cols ...string) error
|
||||
|
||||
// 根据wheres条件进行过滤
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
ListByWheres(wheres collx.M, listModels any, cols ...string) error
|
||||
|
||||
// PageQuery 分页查询
|
||||
PageQuery(cond any, pageParam *model.PageParam, toModels any) (*model.PageResult[any], error)
|
||||
|
||||
// 获取满足model中不为空的字段值条件的所有数据.
|
||||
//
|
||||
// @param list为数组类型 如 var users *[]User,可指定为非model结构体
|
||||
@@ -123,6 +147,24 @@ func (br *RepoImpl[T]) UpdateByIdWithDb(ctx context.Context, db *gorm.DB, e T, c
|
||||
return gormx.UpdateByIdWithDb(db, br.fillBaseInfo(ctx, e), columns...)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) UpdateByWheres(ctx context.Context, e T, wheres collx.M, columns ...string) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
return br.UpdateByWheresWithDb(ctx, db, e, wheres, columns...)
|
||||
}
|
||||
|
||||
e = br.fillBaseInfo(ctx, e)
|
||||
// model的主键值需为空,否则会带上主键条件
|
||||
e.SetId(0)
|
||||
return gormx.UpdateByWheres(e, wheres)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) UpdateByWheresWithDb(ctx context.Context, db *gorm.DB, e T, wheres collx.M, columns ...string) error {
|
||||
e = br.fillBaseInfo(ctx, e)
|
||||
// model的主键值需为空,否则会带上主键条件
|
||||
e.SetId(0)
|
||||
return gormx.UpdateByWheresWithDb(db, br.fillBaseInfo(ctx, e), wheres, columns...)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) Updates(cond any, udpateFields map[string]any) error {
|
||||
return gormx.Updates(br.GetModel(), cond, udpateFields)
|
||||
}
|
||||
@@ -163,6 +205,23 @@ func (br *RepoImpl[T]) DeleteByCondWithDb(ctx context.Context, db *gorm.DB, cond
|
||||
return gormx.DeleteByCondWithDb(db, br.GetModel(), cond)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) DeleteByWheres(ctx context.Context, wheres collx.M) error {
|
||||
if db := contextx.GetDb(ctx); db != nil {
|
||||
return br.DeleteByWheresWithDb(ctx, db, wheres)
|
||||
}
|
||||
// model的主键值需为空,否则会带上主键条件
|
||||
e := br.GetModel()
|
||||
e.SetId(0)
|
||||
return gormx.DeleteByWheres(e, wheres)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) DeleteByWheresWithDb(ctx context.Context, db *gorm.DB, wheres collx.M) error {
|
||||
// model的主键值需为空,否则会带上主键条件
|
||||
e := br.GetModel()
|
||||
e.SetId(0)
|
||||
return gormx.DeleteByWheresWithDb(db, e, wheres)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) GetById(e T, id uint64, cols ...string) error {
|
||||
if err := gormx.GetById(e, id, cols...); err != nil {
|
||||
return err
|
||||
@@ -182,6 +241,15 @@ func (br *RepoImpl[T]) ListByCond(cond any, listModels any, cols ...string) erro
|
||||
return gormx.ListByCond(br.GetModel(), cond, listModels, cols...)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) ListByWheres(wheres collx.M, listModels any, cols ...string) error {
|
||||
return gormx.ListByWheres(br.GetModel(), wheres, listModels, cols...)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) PageQuery(cond any, pageParam *model.PageParam, toModels any) (*model.PageResult[any], error) {
|
||||
qd := gormx.NewQuery(br.GetModel()).WithCondModel(cond)
|
||||
return gormx.PageQuery(qd, pageParam, toModels)
|
||||
}
|
||||
|
||||
func (br *RepoImpl[T]) ListByCondOrder(cond any, list any, order ...string) error {
|
||||
return gormx.ListByCondOrder(br.GetModel(), cond, list, order...)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -93,6 +95,19 @@ func ListByCond(model any, cond any, list any, cols ...string) error {
|
||||
return global.Db.Model(model).Select(cols).Where(cond).Scopes(UndeleteScope).Order("id desc").Find(list).Error
|
||||
}
|
||||
|
||||
// 获取满足cond中不为空的字段值条件的所有model表数据.
|
||||
//
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
func ListByWheres(model any, wheres collx.M, list any, cols ...string) error {
|
||||
gdb := global.Db.Model(model).Select(cols)
|
||||
for k, v := range wheres {
|
||||
if !anyx.IsBlank(v) {
|
||||
gdb.Where(k, v)
|
||||
}
|
||||
}
|
||||
return gdb.Scopes(UndeleteScope).Order("id desc").Find(list).Error
|
||||
}
|
||||
|
||||
// 获取满足model中不为空的字段值条件的所有数据.
|
||||
//
|
||||
// @param list为数组类型 如 var users *[]User,可指定为非model结构体
|
||||
@@ -154,6 +169,24 @@ func UpdateByIdWithDb(db *gorm.DB, model any, columns ...string) error {
|
||||
return db.Model(model).Select(columns).Updates(model).Error
|
||||
}
|
||||
|
||||
// UpdateByWheres 更新满足wheres条件的数据(model的主键值需为空,否则会带上主键条件)
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
func UpdateByWheres(model_ any, wheres collx.M) error {
|
||||
return UpdateByWheresWithDb(global.Db, model_, wheres)
|
||||
}
|
||||
|
||||
// UpdateByWheresWithDb 使用指定gorm.DB更新满足wheres条件的数据(model的主键值需为空,否则会带上主键条件)
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
func UpdateByWheresWithDb(db *gorm.DB, model any, wheres collx.M, columns ...string) error {
|
||||
gormDb := db.Model(model).Select(columns)
|
||||
for k, v := range wheres {
|
||||
if !anyx.IsBlank(v) {
|
||||
gormDb.Where(k, v)
|
||||
}
|
||||
}
|
||||
return gormDb.Updates(model).Error
|
||||
}
|
||||
|
||||
// 根据实体条件,更新参数udpateFields指定字段
|
||||
func Updates(model any, condition any, updateFields map[string]any) error {
|
||||
return global.Db.Model(model).Where(condition).Updates(updateFields).Error
|
||||
@@ -190,6 +223,24 @@ func DeleteByWithDb(db *gorm.DB, model_ any) error {
|
||||
return DeleteByCondWithDb(db, model_, model_)
|
||||
}
|
||||
|
||||
// DeleteByWheres 使用指定wheres删除(model的主键值需为空,否则会带上主键条件)
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
func DeleteByWheres(model_ any, wheres collx.M) error {
|
||||
return DeleteByWheresWithDb(global.Db, model_, wheres)
|
||||
}
|
||||
|
||||
// DeleteByWheresWithDb 使用指定gorm.Db根据wheres条件进行删除(model的主键值需为空,否则会带上主键条件)
|
||||
// @param wheres key -> "age > ?" value -> 10等
|
||||
func DeleteByWheresWithDb(db *gorm.DB, model_ any, wheres collx.M) error {
|
||||
gormDb := db.Model(model_)
|
||||
for k, v := range wheres {
|
||||
if !anyx.IsBlank(v) {
|
||||
gormDb.Where(k, v)
|
||||
}
|
||||
}
|
||||
return gormDb.Updates(getDeleteColumnValue()).Error
|
||||
}
|
||||
|
||||
// 根据cond条件删除指定model表数据
|
||||
//
|
||||
// @param model 数据库映射实体模型
|
||||
|
||||
@@ -22,11 +22,13 @@ const (
|
||||
|
||||
// 实体接口
|
||||
type ModelI interface {
|
||||
// SetId 设置id
|
||||
SetId(id uint64)
|
||||
|
||||
// 是否为新建该实体模型, 默认 id == 0 为新建
|
||||
// IsCreate 是否为新建该实体模型, 默认 id == 0 为新建
|
||||
IsCreate() bool
|
||||
|
||||
// 使用当前登录账号信息赋值实体结构体的基础信息
|
||||
// FillBaseInfo 使用当前登录账号信息赋值实体结构体的基础信息
|
||||
//
|
||||
// 如创建时间,修改时间,创建者,修改者信息等
|
||||
FillBaseInfo(idGenType IdGenType, account *LoginAccount)
|
||||
@@ -36,6 +38,10 @@ type IdModel struct {
|
||||
Id uint64 `json:"id"`
|
||||
}
|
||||
|
||||
func (m *IdModel) SetId(id uint64) {
|
||||
m.Id = id
|
||||
}
|
||||
|
||||
func (m *IdModel) IsCreate() bool {
|
||||
return m.Id == 0
|
||||
}
|
||||
@@ -45,7 +51,7 @@ func (m *IdModel) FillBaseInfo(idGenType IdGenType, account *LoginAccount) {
|
||||
if !m.IsCreate() {
|
||||
return
|
||||
}
|
||||
m.Id = GetIdByGenType(idGenType)
|
||||
m.SetId(GetIdByGenType(idGenType))
|
||||
}
|
||||
|
||||
// 含有删除字段模型
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package collx
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 数组比较
|
||||
// 依次返回,新增值,删除值,以及不变值
|
||||
@@ -131,6 +134,13 @@ func ArrayRemoveFunc[T any](arr []T, isDeleteFunc func(T) bool) []T {
|
||||
return newArr
|
||||
}
|
||||
|
||||
// ArrayRemoveBlank 移除元素中的空元素
|
||||
func ArrayRemoveBlank[T any](arr []T) []T {
|
||||
return ArrayRemoveFunc(arr, func(val T) bool {
|
||||
return anyx.IsBlank(val)
|
||||
})
|
||||
}
|
||||
|
||||
// 数组元素去重
|
||||
func ArrayDeduplicate[T comparable](arr []T) []T {
|
||||
encountered := map[T]bool{}
|
||||
@@ -155,3 +165,14 @@ func ArrayAnyMatches(arr []string, subStr string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ArrayFilter 过滤函数,根据提供的条件函数将切片中的元素进行过滤
|
||||
func ArrayFilter[T any](array []T, fn func(T) bool) []T {
|
||||
var filtered []T
|
||||
for _, val := range array {
|
||||
if fn(val) {
|
||||
filtered = append(filtered, val)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
@@ -335,10 +335,6 @@ CREATE TABLE `t_machine` (
|
||||
`ip` varchar(50) NOT NULL,
|
||||
`port` int(12) NOT NULL,
|
||||
`protocol` tinyint(2) NULL COMMENT '协议 1、SSH 2、RDP',
|
||||
`username` varchar(12) NOT NULL,
|
||||
`auth_method` tinyint(2) DEFAULT NULL COMMENT '1.密码登录2.publickey登录',
|
||||
`password` varchar(100) DEFAULT NULL,
|
||||
`auth_cert_id` bigint(20) DEFAULT NULL COMMENT '授权凭证id',
|
||||
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
|
||||
`enable_recorder` tinyint(2) DEFAULT NULL COMMENT '是否启用终端回放记录',
|
||||
`status` tinyint(2) NOT NULL COMMENT '状态: 1:启用; -1:禁用',
|
||||
@@ -893,6 +889,7 @@ DROP TABLE IF EXISTS `t_tag_tree`;
|
||||
CREATE TABLE `t_tag_tree` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`pid` bigint(20) NOT NULL DEFAULT '0',
|
||||
`type` tinyint NOT NULL DEFAULT '-1' COMMENT '类型: -1.普通标签; 其他值则为对应的资源类型',
|
||||
`code` varchar(36) NOT NULL COMMENT '标识符',
|
||||
`code_path` varchar(255) NOT NULL COMMENT '标识符路径',
|
||||
`name` varchar(36) DEFAULT NULL COMMENT '名称',
|
||||
@@ -997,6 +994,31 @@ BEGIN;
|
||||
INSERT INTO `t_team_member` VALUES (7, 3, 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
DROP TABLE IF EXISTS `t_resource_auth_cert`;
|
||||
-- 资源授权凭证
|
||||
CREATE TABLE `t_resource_auth_cert` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号名称',
|
||||
`resource_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '资源编码',
|
||||
`resource_type` tinyint NOT NULL COMMENT '资源类型',
|
||||
`type` tinyint DEFAULT NULL COMMENT '凭证类型',
|
||||
`username` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名',
|
||||
`ciphertext` varchar(5000) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密文内容',
|
||||
`ciphertext_type` tinyint NOT NULL COMMENT '密文类型(-1.公共授权凭证 1.密码 2.秘钥)',
|
||||
`extra` varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号需要的其他额外信息(如秘钥口令等)',
|
||||
`remark` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime NOT NULL,
|
||||
`creator_id` bigint NOT NULL,
|
||||
`creator` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`update_time` datetime NOT NULL,
|
||||
`modifier_id` bigint NOT NULL,
|
||||
`modifier` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`is_deleted` tinyint DEFAULT '0',
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_resource_code` (`resource_code`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='资源授权凭证表';
|
||||
|
||||
DROP TABLE IF EXISTS `t_flow_procdef`;
|
||||
-- 工单流程相关表
|
||||
CREATE TABLE `t_flow_procdef` (
|
||||
|
||||
@@ -55,6 +55,9 @@ update `t_machine` set `protocol` = 1 where `protocol` is NULL;
|
||||
delete from `t_sys_config` where `key` = 'MachineConfig';
|
||||
INSERT INTO t_sys_config ( name, `key`, params, value, remark, permission, create_time, creator_id, creator, update_time, modifier_id, modifier, is_deleted, delete_time) VALUES('机器相关配置', 'MachineConfig', '[{"name":"终端回放存储路径","model":"terminalRecPath","placeholder":"终端回放存储路径"},{"name":"uploadMaxFileSize","model":"uploadMaxFileSize","placeholder":"允许上传的最大文件大小(1MB、2GB等)"},{"model":"termOpSaveDays","name":"终端记录保存时间","placeholder":"终端记录保存时间(单位天)"},{"model":"guacdHost","name":"guacd服务ip","placeholder":"guacd服务ip,默认 127.0.0.1","required":false},{"name":"guacd服务端口","model":"guacdPort","placeholder":"guacd服务端口,默认 4822","required":false},{"model":"guacdFilePath","name":"guacd服务文件存储位置","placeholder":"guacd服务文件存储位置,用于挂载RDP文件夹"},{"name":"guacd服务记录存储位置","model":"guacdRecPath","placeholder":"guacd服务记录存储位置,用于记录rdp操作记录"}]', '{"terminalRecPath":"./rec","uploadMaxFileSize":"1000MB","termOpSaveDays":"30","guacdHost":"","guacdPort":"","guacdFilePath":"./guacd/rdp-file","guacdRecPath":"./guacd/rdp-rec"}', '机器相关配置,如终端回放路径等', 'all', '2023-07-13 16:26:44', 1, 'admin', '2024-04-06 12:25:03', 1, 'admin', 0, NULL);
|
||||
|
||||
|
||||
ALTER TABLE t_tag_tree ADD `type` tinyint NOT NULL DEFAULT '-1' COMMENT '类型: -1.普通标签; 其他值则为对应的资源类型';
|
||||
|
||||
BEGIN;
|
||||
INSERT
|
||||
INTO
|
||||
@@ -88,5 +91,34 @@ from
|
||||
WHERE
|
||||
is_deleted = 0;
|
||||
|
||||
DROP TABLE t_tag_tree;
|
||||
COMMIT;
|
||||
DROP TABLE t_tag_resource;
|
||||
COMMIT;
|
||||
|
||||
-- 资源授权凭证
|
||||
CREATE TABLE `t_resource_auth_cert` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号名称',
|
||||
`resource_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '资源编码',
|
||||
`resource_type` tinyint NOT NULL COMMENT '资源类型',
|
||||
`type` tinyint DEFAULT NULL COMMENT '凭证类型',
|
||||
`username` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名',
|
||||
`ciphertext` varchar(5000) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密文内容',
|
||||
`ciphertext_type` tinyint NOT NULL COMMENT '密文类型(-1.公共授权凭证 1.密码 2.秘钥)',
|
||||
`extra` varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号需要的其他额外信息(如秘钥口令等)',
|
||||
`remark` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注',
|
||||
`create_time` datetime NOT NULL,
|
||||
`creator_id` bigint NOT NULL,
|
||||
`creator` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`update_time` datetime NOT NULL,
|
||||
`modifier_id` bigint NOT NULL,
|
||||
`modifier` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`is_deleted` tinyint DEFAULT '0',
|
||||
`delete_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_resource_code` (`resource_code`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='资源授权凭证表';
|
||||
|
||||
-- 删除机器表 账号相关字段
|
||||
ALTER TABLE t_machine DROP COLUMN username;
|
||||
ALTER TABLE t_machine DROP COLUMN password;
|
||||
ALTER TABLE t_machine DROP COLUMN auth_cert_id;
|
||||
|
||||
Reference in New Issue
Block a user