feat: 新增机器授权凭证管理与其他优化

This commit is contained in:
meilin.huang
2023-03-06 16:59:57 +08:00
parent 7c086bbec8
commit 0695ad9a85
73 changed files with 1666 additions and 811 deletions

View File

@@ -6,7 +6,7 @@ require (
github.com/gin-gonic/gin v1.9.0
github.com/go-redis/redis/v8 v8.11.5
github.com/go-sql-driver/mysql v1.7.0
github.com/golang-jwt/jwt/v4 v4.4.3
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/gorilla/websocket v1.5.0
github.com/lib/pq v1.10.7
github.com/mojocn/base64Captcha v1.3.5 //
@@ -15,11 +15,11 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
go.mongodb.org/mongo-driver v1.11.1 // mongo
golang.org/x/crypto v0.6.0 // ssh
golang.org/x/crypto v0.7.0 // ssh
gopkg.in/yaml.v3 v3.0.1
// gorm
gorm.io/driver/mysql v1.4.5
gorm.io/gorm v1.24.3
gorm.io/gorm v1.24.6
)
require (
@@ -55,10 +55,10 @@ require (
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)

View File

@@ -1,21 +1,19 @@
package form
type DbForm struct {
Id uint64
Name string `binding:"required" json:"name"`
Type string `binding:"required" json:"type"` // 类型mysql oracle等
Host string `binding:"required" json:"host"`
Port int `binding:"required" json:"port"`
Username string `binding:"required" json:"username"`
Password string `json:"password"`
Params string `json:"params"`
Database string `json:"database"`
Remark string `json:"remark"`
TagId uint64 `json:"tagId"`
TagPath string `json:"tagPath"`
EnableSshTunnel int8 `json:"enableSshTunnel"`
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"`
Id uint64
Name string `binding:"required" json:"name"`
Type string `binding:"required" json:"type"` // 类型mysql oracle等
Host string `binding:"required" json:"host"`
Port int `binding:"required" json:"port"`
Username string `binding:"required" json:"username"`
Password string `json:"password"`
Params string `json:"params"`
Database string `json:"database"`
Remark string `json:"remark"`
TagId uint64 `json:"tagId"`
TagPath string `json:"tagPath"`
SshTunnelMachineId int `json:"sshTunnelMachineId"`
}
type DbSqlSaveForm struct {

View File

@@ -23,6 +23,5 @@ type SelectDataDbVO struct {
Modifier *string `json:"modifier"`
ModifierId *int64 `json:"modifierId"`
EnableSshTunnel *int8 `json:"enableSshTunnel"`
SshTunnelMachineId *uint64 `json:"sshTunnelMachineId"`
SshTunnelMachineId int `json:"sshTunnelMachineId"`
}

View File

@@ -86,7 +86,7 @@ func (d *dbAppImpl) Save(dbEntity *entity.Db) {
}
// 查找是否存在该库
oldDb := &entity.Db{Host: dbEntity.Host, Port: dbEntity.Port, TagId: dbEntity.TagId}
oldDb := &entity.Db{Host: dbEntity.Host, Port: dbEntity.Port, Username: dbEntity.Username}
err := d.GetDbBy(oldDb)
if dbEntity.Id == 0 {
@@ -228,8 +228,7 @@ type DbInfo struct {
Username string
TagPath string
Database string
EnableSshTunnel int8 // 是否启用ssh隧道
SshTunnelMachineId uint64
SshTunnelMachineId int
}
// 获取记录日志的描述
@@ -306,7 +305,7 @@ var dbCache = cache.NewTimedCache(constant.DbConnExpireTime, 5*time.Second).
})
func init() {
machine.AddCheckSshTunnelMachineUseFunc(func(machineId uint64) bool {
machine.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
// 遍历所有db连接实例若存在db实例使用该ssh隧道机器则返回true表示还在使用中...
items := dbCache.Items()
for _, v := range items {

View File

@@ -14,7 +14,7 @@ import (
func getMysqlDB(d *entity.Db, db string) (*sql.DB, error) {
// SSH Conect
if d.EnableSshTunnel == 1 && d.SshTunnelMachineId != 0 {
if d.SshTunnelMachineId > 0 {
sshTunnelMachine := machineapp.GetMachineApp().GetSshTunnelMachine(d.SshTunnelMachineId)
mysql.RegisterDialContext(d.Network, func(ctx context.Context, addr string) (net.Conn, error) {
return sshTunnelMachine.GetDialConn("tcp", addr)

View File

@@ -18,7 +18,7 @@ import (
func getPgsqlDB(d *entity.Db, db string) (*sql.DB, error) {
driverName := d.Type
// SSH Conect
if d.EnableSshTunnel == 1 && d.SshTunnelMachineId != 0 {
if d.SshTunnelMachineId > 0 {
// 如果使用了隧道,则使用`postgres:ssh:隧道机器id`注册名
driverName = fmt.Sprintf("postgres:ssh:%d", d.SshTunnelMachineId)
if !utils.ArrContains(sql.Drivers(), driverName) {
@@ -36,7 +36,7 @@ func getPgsqlDB(d *entity.Db, db string) (*sql.DB, error) {
// pgsql dialer
type PqSqlDialer struct {
sshTunnelMachineId uint64
sshTunnelMachineId int
}
func (d *PqSqlDialer) Open(name string) (driver.Conn, error) {

View File

@@ -9,27 +9,25 @@ import (
type Db struct {
model.Model
Name string `orm:"column(name)" json:"name"`
Type string `orm:"column(type)" json:"type"` // 类型mysql oracle等
Host string `orm:"column(host)" json:"host"`
Port int `orm:"column(port)" json:"port"`
Network string `orm:"column(network)" json:"network"`
Username string `orm:"column(username)" json:"username"`
Password string `orm:"column(password)" json:"-"`
Database string `orm:"column(database)" json:"database"`
Params string `json:"params"`
Remark string `json:"remark"`
TagId uint64
TagPath string
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
Name string `orm:"column(name)" json:"name"`
Type string `orm:"column(type)" json:"type"` // 类型mysql oracle等
Host string `orm:"column(host)" json:"host"`
Port int `orm:"column(port)" json:"port"`
Network string `orm:"column(network)" json:"network"`
Username string `orm:"column(username)" json:"username"`
Password string `orm:"column(password)" json:"-"`
Database string `orm:"column(database)" json:"database"`
Params string `json:"params"`
Remark string `json:"remark"`
TagId uint64
TagPath string
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
}
// 获取数据库连接网络, 若没有使用ssh隧道则直接返回。否则返回拼接的网络需要注册至指定dial
func (d *Db) GetNetwork() string {
network := d.Network
if d.EnableSshTunnel == 0 || d.EnableSshTunnel == -1 {
if d.SshTunnelMachineId <= 0 {
if network == "" {
return "tcp"
} else {

View File

@@ -18,7 +18,6 @@ type DbQuery struct {
Remark string `json:"remark"`
TagId uint64
ProjectIds []uint64
TagIds []uint64
TagPathLike string
}

View File

@@ -0,0 +1,62 @@
package api
import (
"mayfly-go/internal/machine/api/form"
"mayfly-go/internal/machine/api/vo"
"mayfly-go/internal/machine/application"
"mayfly-go/internal/machine/domain/entity"
"mayfly-go/pkg/ginx"
"mayfly-go/pkg/req"
"mayfly-go/pkg/utils"
)
type AuthCert struct {
AuthCertApp application.AuthCert
}
func (ac *AuthCert) BaseAuthCerts(rc *req.Ctx) {
g := rc.GinCtx
condition := &entity.AuthCert{
Name: g.Query("name"),
AuthMethod: int8(ginx.QueryInt(g, "authMethod", 0)),
}
condition.Id = uint64(ginx.QueryInt(g, "id", 0))
rc.ResData = ac.AuthCertApp.GetPageList(condition, ginx.GetPageParam(g), new([]vo.AuthCertBaseVO))
}
func (ac *AuthCert) AuthCerts(rc *req.Ctx) {
g := rc.GinCtx
condition := &entity.AuthCert{
Name: g.Query("name"),
AuthMethod: int8(ginx.QueryInt(g, "authMethod", 0)),
}
condition.Id = uint64(ginx.QueryInt(g, "id", 0))
res := new([]*entity.AuthCert)
pageRes := ac.AuthCertApp.GetPageList(condition, ginx.GetPageParam(g), res)
for _, r := range *res {
r.PwdDecrypt()
}
rc.ResData = pageRes
}
func (c *AuthCert) SaveAuthCert(rc *req.Ctx) {
g := rc.GinCtx
acForm := &form.AuthCertForm{}
ginx.BindJsonAndValid(g, acForm)
ac := new(entity.AuthCert)
utils.Copy(ac, acForm)
// 脱敏记录日志
acForm.Passphrase = "***"
acForm.Password = "***"
rc.ReqParam = acForm
ac.SetBaseInfo(rc.LoginAccount)
c.AuthCertApp.Save(ac)
}
func (c *AuthCert) Delete(rc *req.Ctx) {
c.AuthCertApp.DeleteById(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
}

View File

@@ -1,19 +1,21 @@
package form
type MachineForm struct {
Id uint64 `json:"id"`
Name string `json:"name" binding:"required"`
Ip string `json:"ip" binding:"required"` // IP地址
Username string `json:"username" binding:"required"` // 用户名
AuthMethod int8 `json:"authMethod" binding:"required"`
Password string `json:"password"`
Port int `json:"port" binding:"required"` // 端口号
Id uint64 `json:"id"`
Name string `json:"name" binding:"required"`
Ip string `json:"ip" binding:"required"` // IP地址
Port int `json:"port" binding:"required"` // 端口号
// 资产授权凭证信息列表
AuthCertId int `json:"authCertId"`
TagId uint64 `json:"tagId"`
TagPath string `json:"tagPath" binding:"required"`
Username string `json:"username"`
Password string `json:"password"`
Remark string `json:"remark"`
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
TagId uint64 `json:"tagId"`
TagPath string `json:"tagPath"`
}
type MachineRunForm struct {
@@ -49,3 +51,21 @@ type MachineFileUpdateForm struct {
Id uint64 `binding:"required"`
Path string `binding:"required"`
}
// 授权凭证
type AuthCertForm struct {
Id uint64 `json:"id"`
Name string `json:"name" binding:"required"`
AuthMethod int8 `json:"authMethod" binding:"required"` // 1.密码 2.秘钥
Username string `json:"username"`
Password string `json:"password"` // 密码or私钥
Passphrase string `json:"passphrase"` // 私钥口令
Remark string `json:"remark"`
}
// 资产授权凭证信息
type AssetAuthCertForm struct {
AuthCertId uint64 `json:"authCertId"`
TagId uint64 `json:"tagId"`
TagPath string `json:"tagPath" binding:"required"`
}

View File

@@ -53,7 +53,7 @@ func (m *Machine) Machines(rc *req.Ctx) {
list := res.List.(*[]*vo.MachineVO)
for _, mv := range *list {
mv.HasCli = machine.HasCli(*mv.Id)
mv.HasCli = machine.HasCli(mv.Id)
}
rc.ResData = res
}
@@ -63,6 +63,7 @@ func (m *Machine) MachineStats(rc *req.Ctx) {
rc.ResData = stats
}
// 保存机器信息
func (m *Machine) SaveMachine(rc *req.Ctx) {
g := rc.GinCtx
machineForm := new(form.MachineForm)
@@ -71,27 +72,22 @@ func (m *Machine) SaveMachine(rc *req.Ctx) {
me := new(entity.Machine)
utils.Copy(me, machineForm)
if me.AuthMethod == entity.MachineAuthMethodPassword {
// 密码解密,并使用解密后的赋值
originPwd, err := utils.DefaultRsaDecrypt(machineForm.Password, true)
biz.ErrIsNilAppendErr(err, "解密密码错误: %s")
me.Password = originPwd
}
// 密码脱敏记录日志
machineForm.Password = "****"
machineForm.Password = "******"
rc.ReqParam = machineForm
me.SetBaseInfo(rc.LoginAccount)
m.MachineApp.Save(me)
}
// 获取机器实例密码,由于数据库是加密存储,故提供该接口展示原文密码
func (m *Machine) GetMachinePwd(rc *req.Ctx) {
mid := GetMachineId(rc.GinCtx)
me := m.MachineApp.GetById(mid, "Password")
me.PwdDecrypt()
rc.ResData = me.Password
func (m *Machine) TestConn(rc *req.Ctx) {
g := rc.GinCtx
machineForm := new(form.MachineForm)
ginx.BindJsonAndValid(g, machineForm)
me := new(entity.Machine)
utils.Copy(me, machineForm)
m.MachineApp.TestConn(me)
}
func (m *Machine) ChangeStatus(rc *req.Ctx) {

View File

@@ -1,17 +1,25 @@
package vo
import "time"
import (
"time"
)
// 授权凭证基础信息
type AuthCertBaseVO struct {
Id int `json:"id"`
Name string `json:"name"`
AuthMethod int8 `json:"authMethod"`
}
type MachineVO struct {
Id *uint64 `json:"id"`
Name *string `json:"name"`
Username *string `json:"username"`
Ip *string `json:"ip"`
Port *int `json:"port"`
AuthMethod *int8 `json:"authMethod"`
Id uint64 `json:"id"`
Name string `json:"name"`
Ip string `json:"ip"`
Port int `json:"port"`
Username string `json:"username"`
AuthCertId int `json:"authCertId"`
Status *int8 `json:"status"`
EnableSshTunnel *int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId *uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
CreateTime *time.Time `json:"createTime"`
Creator *string `json:"creator"`
CreatorId *int64 `json:"creatorId"`

View File

@@ -1,11 +1,20 @@
package application
import "mayfly-go/internal/machine/infrastructure/persistence"
import (
"mayfly-go/internal/machine/infrastructure/persistence"
)
var (
machineApp Machine = newMachineApp(persistence.GetMachineRepo())
machineFileApp MachineFile = newMachineFileApp(persistence.GetMachineFileRepo(), persistence.GetMachineRepo())
machineFileApp MachineFile = newMachineFileApp(persistence.GetMachineFileRepo(), persistence.GetMachineRepo())
machineScriptApp MachineScript = newMachineScriptApp(persistence.GetMachineScriptRepo(), persistence.GetMachineRepo())
authCertApp AuthCert = newAuthCertApp(persistence.GetAuthCertRepo())
machineApp Machine = newMachineApp(
persistence.GetMachineRepo(),
GetAuthCertApp(),
)
)
func GetMachineApp() Machine {
@@ -19,3 +28,7 @@ func GetMachineFileApp() MachineFile {
func GetMachineScriptApp() MachineScript {
return machineScriptApp
}
func GetAuthCertApp() AuthCert {
return authCertApp
}

View File

@@ -0,0 +1,64 @@
package application
import (
"mayfly-go/internal/machine/domain/entity"
"mayfly-go/internal/machine/domain/repository"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/model"
)
type AuthCert interface {
GetPageList(condition *entity.AuthCert, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
Save(ac *entity.AuthCert)
GetById(id uint64) *entity.AuthCert
GetByIds(ids ...uint64) []*entity.AuthCert
DeleteById(id uint64)
}
func newAuthCertApp(authCertRepo repository.AuthCert) AuthCert {
return &authCertAppImpl{
authCertRepo: authCertRepo,
}
}
type authCertAppImpl struct {
authCertRepo repository.AuthCert
}
func (a *authCertAppImpl) GetPageList(condition *entity.AuthCert, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
return a.authCertRepo.GetPageList(condition, pageParam, toEntity)
}
func (a *authCertAppImpl) Save(ac *entity.AuthCert) {
oldAc := &entity.AuthCert{Name: ac.Name}
err := a.authCertRepo.GetByCondition(oldAc, "Id", "Name")
ac.PwdEncrypt()
if ac.Id == 0 {
biz.IsTrue(err != nil, "该凭证名已存在")
a.authCertRepo.Insert(ac)
return
}
// 如果存在该库,则校验修改的库是否为该库
if err == nil {
biz.IsTrue(oldAc.Id == ac.Id, "该凭证名已存在")
}
a.authCertRepo.Update(ac)
}
func (a *authCertAppImpl) GetById(id uint64) *entity.AuthCert {
return a.authCertRepo.GetById(id)
}
func (a *authCertAppImpl) GetByIds(ids ...uint64) []*entity.AuthCert {
return a.authCertRepo.GetByIds(ids...)
}
func (a *authCertAppImpl) DeleteById(id uint64) {
a.authCertRepo.DeleteById(id)
}

View File

@@ -14,7 +14,10 @@ type Machine interface {
// 根据条件获取账号信息
GetMachine(condition *entity.Machine, cols ...string) error
Save(entity *entity.Machine)
Save(*entity.Machine)
// 测试机器连接
TestConn(me *entity.Machine)
// 调整机器状态
ChangeStatus(id uint64, status int8)
@@ -33,16 +36,19 @@ type Machine interface {
GetCli(id uint64) *machine.Cli
// 获取ssh隧道机器连接
GetSshTunnelMachine(id uint64) *machine.SshTunnelMachine
GetSshTunnelMachine(id int) *machine.SshTunnelMachine
}
func newMachineApp(machineRepo repository.Machine) Machine {
return &machineAppImpl{machineRepo: machineRepo}
func newMachineApp(machineRepo repository.Machine, authCertApp AuthCert) Machine {
return &machineAppImpl{
machineRepo: machineRepo,
authCertApp: authCertApp,
}
}
type machineAppImpl struct {
machineRepo repository.Machine
authCertApp AuthCert
}
// 分页获取机器信息列表
@@ -54,36 +60,35 @@ func (m *machineAppImpl) Count(condition *entity.MachineQuery) int64 {
return m.machineRepo.Count(condition)
}
// 根据条件获取机器信息
func (m *machineAppImpl) Save(me *entity.Machine) {
// ’修改机器信息且密码不为空‘ or ‘新增’需要测试是否可连接
if (me.Id != 0 && me.Password != "") || me.Id == 0 {
biz.ErrIsNilAppendErr(machine.TestConn(*me, func(u uint64) *entity.Machine {
me := m.GetById(u)
me.PwdDecrypt()
return me
}), "该机器无法连接: %s")
}
oldMachine := &entity.Machine{Ip: me.Ip, Port: me.Port, Username: me.Username}
err := m.GetMachine(oldMachine)
if me.Id != 0 {
// 如果存在该库,则校验修改的库是否为该库
if err == nil {
biz.IsTrue(oldMachine.Id == me.Id, "该机器信息已存在")
}
// 关闭连接
machine.DeleteCli(me.Id)
me.PwdEncrypt()
m.machineRepo.UpdateById(me)
} else {
me.PwdEncrypt()
if me.Id == 0 {
biz.IsTrue(err != nil, "该机器信息已存在")
// 新增机器,默认启用状态
me.Status = entity.MachineStatusEnable
me.PwdEncrypt()
m.machineRepo.Create(me)
return
}
// 如果存在该库,则校验修改的库是否为该库
if err == nil {
biz.IsTrue(oldMachine.Id == me.Id, "该机器信息已存在")
}
// 关闭连接
machine.DeleteCli(me.Id)
m.machineRepo.UpdateById(me)
}
func (m *machineAppImpl) TestConn(me *entity.Machine) {
me.Id = 0
// 测试连接
biz.ErrIsNilAppendErr(machine.TestConn(*m.toMachineInfo(me), func(u uint64) *machine.Info {
return m.toMachineInfoById(u)
}), "该机器无法连接: %s")
}
func (m *machineAppImpl) ChangeStatus(id uint64, status int8) {
@@ -128,24 +133,55 @@ func (m *machineAppImpl) GetById(id uint64, cols ...string) *entity.Machine {
return m.machineRepo.GetById(id, cols...)
}
func (m *machineAppImpl) GetCli(id uint64) *machine.Cli {
cli, err := machine.GetCli(id, func(machineId uint64) *entity.Machine {
machine := m.GetById(machineId)
machine.PwdDecrypt()
biz.IsTrue(machine.Status == entity.MachineStatusEnable, "该机器已被停用")
return machine
func (m *machineAppImpl) GetCli(machineId uint64) *machine.Cli {
cli, err := machine.GetCli(machineId, func(mid uint64) *machine.Info {
return m.toMachineInfoById(mid)
})
biz.ErrIsNilAppendErr(err, "获取客户端错误: %s")
return cli
}
func (m *machineAppImpl) GetSshTunnelMachine(id uint64) *machine.SshTunnelMachine {
sshTunnel, err := machine.GetSshTunnelMachine(id, func(machineId uint64) *entity.Machine {
machine := m.GetById(machineId)
machine.PwdDecrypt()
biz.IsTrue(machine.Status == entity.MachineStatusEnable, "该机器已被停用")
return machine
func (m *machineAppImpl) GetSshTunnelMachine(machineId int) *machine.SshTunnelMachine {
sshTunnel, err := machine.GetSshTunnelMachine(machineId, func(mid uint64) *machine.Info {
return m.toMachineInfoById(mid)
})
biz.ErrIsNilAppendErr(err, "获取ssh隧道连接失败: %s")
return sshTunnel
}
// 生成机器信息根据授权凭证id填充用户密码等
func (m *machineAppImpl) toMachineInfoById(machineId uint64) *machine.Info {
me := m.GetById(machineId)
biz.IsTrue(me.Status == entity.MachineStatusEnable, "该机器已被停用")
return m.toMachineInfo(me)
}
func (m *machineAppImpl) toMachineInfo(me *entity.Machine) *machine.Info {
mi := new(machine.Info)
mi.Id = me.Id
mi.Name = me.Name
mi.Ip = me.Ip
mi.Port = me.Port
mi.Username = me.Username
mi.TagId = me.TagId
mi.TagPath = me.TagPath
mi.EnableRecorder = me.EnableRecorder
mi.SshTunnelMachineId = me.SshTunnelMachineId
if me.UseAuthCert() {
ac := m.authCertApp.GetById(uint64(me.AuthCertId))
biz.NotNil(ac, "授权凭证信息已不存在,请重新关联")
mi.AuthMethod = ac.AuthMethod
ac.PwdDecrypt()
mi.Password = ac.Password
mi.Passphrase = ac.Passphrase
} else {
mi.AuthMethod = entity.AuthCertAuthMethodPassword
if me.Id != 0 {
me.PwdDecrypt()
}
mi.Password = me.Password
}
return mi
}

View File

@@ -6,6 +6,7 @@ import (
"io/fs"
"mayfly-go/internal/machine/domain/entity"
"mayfly-go/internal/machine/domain/repository"
"mayfly-go/internal/machine/infrastructure/machine"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/model"
"os"
@@ -29,7 +30,7 @@ type MachineFile interface {
Delete(id uint64)
// 获取文件关联的机器信息,主要用于记录日志使用
GetMachine(fileId uint64) *entity.Machine
GetMachine(fileId uint64) *machine.Info
/** sftp 相关操作 **/
@@ -191,7 +192,7 @@ func (m *machineFileAppImpl) getSftpCli(machineId uint64) *sftp.Client {
return GetMachineApp().GetCli(machineId).GetSftpCli()
}
func (m *machineFileAppImpl) GetMachine(fileId uint64) *entity.Machine {
func (m *machineFileAppImpl) GetMachine(fileId uint64) *machine.Info {
return GetMachineApp().GetCli(m.GetById(fileId).MachineId).GetMachine()
}

View File

@@ -0,0 +1,41 @@
package entity
import (
"mayfly-go/internal/common/utils"
"mayfly-go/pkg/model"
)
// 授权凭证
type AuthCert struct {
model.Model
Name string `json:"name"`
AuthMethod int8 `json:"authMethod"` // 1.密码 2.秘钥
Password string `json:"password"` // 密码or私钥
Passphrase string `json:"passphrase"` // 私钥口令
Remark string `json:"remark"`
}
func (a *AuthCert) TableName() string {
return "t_auth_cert"
}
const (
AuthCertAuthMethodPassword int8 = 1 // 密码
MachineAuthMethodPublicKey int8 = 2 // 密钥
AuthCertTypePrivate int8 = 1
AuthCertTypePublic int8 = 2
)
// 密码加密
func (ac *AuthCert) PwdEncrypt() {
ac.Password = utils.PwdAesEncrypt(ac.Password)
ac.Passphrase = utils.PwdAesEncrypt(ac.Passphrase)
}
// 密码解密
func (ac *AuthCert) PwdDecrypt() {
ac.Password = utils.PwdAesDecrypt(ac.Password)
ac.Passphrase = utils.PwdAesDecrypt(ac.Passphrase)
}

View File

@@ -1,7 +1,6 @@
package entity
import (
"fmt"
"mayfly-go/internal/common/utils"
"mayfly-go/pkg/model"
)
@@ -11,24 +10,21 @@ type Machine struct {
Name string `json:"name"`
Ip string `json:"ip"` // IP地址
Port int `json:"port"` // 端口号
Username string `json:"username"` // 用户名
AuthMethod int8 `json:"authMethod"` // 授权认证方式
Password string `json:"-"`
Port int `json:"port"` // 端口号
Password string `json:"password"` // 密码
AuthCertId int `json:"authCertId"` // 授权凭证id
TagId uint64
TagPath string
Status int8 `json:"status"` // 状态 1:启用2:停用
Remark string `json:"remark"` // 备注
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
TagId uint64 `json:"tagId"`
TagPath string `json:"tagPath"`
}
const (
MachineStatusEnable int8 = 1 // 启用状态
MachineStatusDisable int8 = -1 // 禁用状态
MachineAuthMethodPassword int8 = 1 // 密码登录
MachineAuthMethodPublicKey int8 = 2 // 公钥免密登录
MachineStatusEnable int8 = 1 // 启用状态
MachineStatusDisable int8 = -1 // 禁用状态
)
func (m *Machine) PwdEncrypt() {
@@ -41,7 +37,6 @@ func (m *Machine) PwdDecrypt() {
m.Password = utils.PwdAesDecrypt(m.Password)
}
// 获取记录日志的描述
func (m *Machine) GetLogDesc() string {
return fmt.Sprintf("Machine[id=%d, tag=%s, name=%s, ip=%s:%d]", m.Id, m.TagPath, m.Name, m.Ip, m.Port)
func (m *Machine) UseAuthCert() bool {
return m.AuthCertId > 0
}

View File

@@ -4,8 +4,6 @@ import "mayfly-go/pkg/model"
type MachineQuery struct {
model.Model
ProjectId uint64 `json:"projectId"`
ProjectName string `json:"projectName"`
Name string `json:"name"`
Ip string `json:"ip"` // IP地址
Username string `json:"username"` // 用户名
@@ -14,11 +12,9 @@ type MachineQuery struct {
Port int `json:"port"` // 端口号
Status int8 `json:"status"` // 状态 1:启用2:停用
Remark string `json:"remark"` // 备注
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
ProjectIds []uint64
TagId uint64
TagPathLike string
TagIds []uint64

View File

@@ -0,0 +1,22 @@
package repository
import (
"mayfly-go/internal/machine/domain/entity"
"mayfly-go/pkg/model"
)
type AuthCert interface {
GetPageList(condition *entity.AuthCert, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
Insert(ac *entity.AuthCert)
Update(ac *entity.AuthCert)
GetById(id uint64) *entity.AuthCert
GetByIds(ids ...uint64) []*entity.AuthCert
GetByCondition(condition *entity.AuthCert, cols ...string) error
DeleteById(id uint64)
}

View File

@@ -16,15 +16,42 @@ import (
"golang.org/x/crypto/ssh"
)
// 机器信息
type Info struct {
Id uint64
Name string `json:"name"`
Ip string `json:"ip"` // IP地址
Port int `json:"port"` // 端口号
AuthMethod int8 `json:"authMethod"` // 授权认证方式
Username string `json:"username"` // 用户名
Password string `json:"-"`
Passphrase string `json:"-"` // 私钥口令
Status int8 `json:"status"` // 状态 1:启用2:停用
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
EnableRecorder int8 `json:"enableRecorder"` // 是否启用终端回放记录
TagId uint64 `json:"tagId"`
TagPath string `json:"tagPath"`
}
func (m *Info) UseSshTunnel() bool {
return m.SshTunnelMachineId > 0
}
// 获取记录日志的描述
func (m *Info) GetLogDesc() string {
return fmt.Sprintf("Machine[id=%d, tag=%s, name=%s, ip=%s:%d]", m.Id, m.TagPath, m.Name, m.Ip, m.Port)
}
// 客户端信息
type Cli struct {
machine *entity.Machine
machine *Info
client *ssh.Client // ssh客户端
sftpClient *sftp.Client // sftp客户端
enableSshTunnel int8
sshTunnelMachineId uint64
sshTunnelMachineId int
}
// 连接
@@ -54,7 +81,7 @@ func (c *Cli) Close() {
c.sftpClient.Close()
c.sftpClient = nil
}
if c.enableSshTunnel == 1 {
if c.sshTunnelMachineId > 0 {
CloseSshTunnelMachine(c.sshTunnelMachineId, c.machine.Id)
}
}
@@ -106,7 +133,7 @@ func (c *Cli) Run(shell string) (string, error) {
return string(buf), nil
}
func (c *Cli) GetMachine() *entity.Machine {
func (c *Cli) GetMachine() *Info {
return c.machine
}
@@ -118,7 +145,7 @@ var cliCache = cache.NewTimedCache(constant.MachineConnExpireTime, 5*time.Second
})
func init() {
AddCheckSshTunnelMachineUseFunc(func(machineId uint64) bool {
AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
// 遍历所有机器连接实例若存在机器连接实例使用该ssh隧道机器则返回true表示还在使用中...
items := cliCache.Items()
for _, v := range items {
@@ -144,7 +171,7 @@ func DeleteCli(id uint64) {
}
// 从缓存中获取客户端信息,不存在则回调获取机器信息函数,并新建
func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, error) {
func GetCli(machineId uint64, getMachine func(uint64) *Info) (*Cli, error) {
cli, err := cliCache.ComputeIfAbsent(machineId, func(_ interface{}) (interface{}, error) {
me := getMachine(machineId)
err := IfUseSshTunnelChangeIpPort(me, getMachine)
@@ -156,7 +183,6 @@ func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, er
CloseSshTunnelMachine(me.SshTunnelMachineId, me.Id)
return nil, err
}
c.enableSshTunnel = me.EnableSshTunnel
c.sshTunnelMachineId = me.SshTunnelMachineId
return c, nil
})
@@ -168,7 +194,7 @@ func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, er
}
// 测试连接使用传值的方式而非引用。因为如果使用了ssh隧道则ip和端口会变为本地映射地址与端口
func TestConn(me entity.Machine, getSshTunnelMachine func(uint64) *entity.Machine) error {
func TestConn(me Info, getSshTunnelMachine func(uint64) *Info) error {
originId := me.Id
if originId == 0 {
// 随机设置一个ip如果使用了隧道则用于临时保存隧道
@@ -177,7 +203,7 @@ func TestConn(me entity.Machine, getSshTunnelMachine func(uint64) *entity.Machin
err := IfUseSshTunnelChangeIpPort(&me, getSshTunnelMachine)
biz.ErrIsNilAppendErr(err, "ssh隧道连接失败: %s")
if me.EnableSshTunnel == 1 {
if me.UseSshTunnel() {
defer CloseSshTunnelMachine(me.SshTunnelMachineId, me.Id)
}
sshClient, err := GetSshClient(&me)
@@ -189,11 +215,11 @@ func TestConn(me entity.Machine, getSshTunnelMachine func(uint64) *entity.Machin
}
// 如果使用了ssh隧道则修改机器ip port为暴露的ip port
func IfUseSshTunnelChangeIpPort(me *entity.Machine, getMachine func(uint64) *entity.Machine) error {
if me.EnableSshTunnel != 1 {
func IfUseSshTunnelChangeIpPort(me *Info, getMachine func(uint64) *Info) error {
if !me.UseSshTunnel() {
return nil
}
sshTunnelMachine, err := GetSshTunnelMachine(me.SshTunnelMachineId, func(u uint64) *entity.Machine {
sshTunnelMachine, err := GetSshTunnelMachine(me.SshTunnelMachineId, func(u uint64) *Info {
return getMachine(u)
})
if err != nil {
@@ -209,26 +235,34 @@ func IfUseSshTunnelChangeIpPort(me *entity.Machine, getMachine func(uint64) *ent
return nil
}
func GetSshClient(m *entity.Machine) (*ssh.Client, error) {
config := ssh.ClientConfig{
func GetSshClient(m *Info) (*ssh.Client, error) {
config := &ssh.ClientConfig{
User: m.Username,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
Timeout: 5 * time.Second,
}
if m.AuthMethod == entity.MachineAuthMethodPassword {
if m.AuthMethod == entity.AuthCertAuthMethodPassword {
config.Auth = []ssh.AuthMethod{ssh.Password(m.Password)}
} else if m.AuthMethod == entity.MachineAuthMethodPublicKey {
if signer, err := ssh.ParsePrivateKey([]byte(m.Password)); err != nil {
return nil, err
var key ssh.Signer
var err error
if len(m.Passphrase) > 0 {
key, err = ssh.ParsePrivateKeyWithPassphrase([]byte(m.Password), []byte(m.Passphrase))
} else {
config.Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)}
key, err = ssh.ParsePrivateKey([]byte(m.Password))
}
if err != nil {
return nil, err
}
config.Auth = []ssh.AuthMethod{ssh.PublicKeys(key)}
}
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
sshClient, err := ssh.Dial("tcp", addr, &config)
sshClient, err := ssh.Dial("tcp", addr, config)
if err != nil {
return nil, err
}
@@ -236,7 +270,7 @@ func GetSshClient(m *entity.Machine) (*ssh.Client, error) {
}
// 根据机器信息创建客户端对象
func newClient(machine *entity.Machine) (*Cli, error) {
func newClient(machine *Info) (*Cli, error) {
if machine == nil {
return nil, errors.New("机器不存在")
}

View File

@@ -3,7 +3,6 @@ package machine
import (
"fmt"
"io"
"mayfly-go/internal/machine/domain/entity"
"mayfly-go/pkg/global"
"mayfly-go/pkg/scheduler"
"mayfly-go/pkg/utils"
@@ -15,7 +14,7 @@ import (
)
var (
sshTunnelMachines map[uint64]*SshTunnelMachine = make(map[uint64]*SshTunnelMachine)
sshTunnelMachines map[int]*SshTunnelMachine = make(map[int]*SshTunnelMachine)
mutex sync.Mutex
@@ -27,7 +26,7 @@ var (
)
// 检查ssh隧道机器是否有被使用
type CheckSshTunnelMachineHasUseFunc func(uint64) bool
type CheckSshTunnelMachineHasUseFunc func(int) bool
func startCheckUse() {
global.Log.Info("开启定时检测ssh隧道机器是否还有被使用")
@@ -66,7 +65,7 @@ func AddCheckSshTunnelMachineUseFunc(checkFunc CheckSshTunnelMachineHasUseFunc)
// ssh隧道机器
type SshTunnelMachine struct {
machineId uint64 // 隧道机器id
machineId int // 隧道机器id
SshClient *ssh.Client
mutex sync.Mutex
tunnels map[uint64]*Tunnel // 机器id -> 隧道
@@ -137,7 +136,7 @@ func (stm *SshTunnelMachine) Close() {
}
// 获取ssh隧道机器方便统一管理充当ssh隧道的机器避免创建多个ssh client
func GetSshTunnelMachine(machineId uint64, getMachine func(uint64) *entity.Machine) (*SshTunnelMachine, error) {
func GetSshTunnelMachine(machineId int, getMachine func(uint64) *Info) (*SshTunnelMachine, error) {
sshTunnelMachine := sshTunnelMachines[machineId]
if sshTunnelMachine != nil {
return sshTunnelMachine, nil
@@ -146,7 +145,7 @@ func GetSshTunnelMachine(machineId uint64, getMachine func(uint64) *entity.Machi
mutex.Lock()
defer mutex.Unlock()
me := getMachine(machineId)
me := getMachine(uint64(machineId))
sshClient, err := GetSshClient(me)
if err != nil {
return nil, err
@@ -165,7 +164,7 @@ func GetSshTunnelMachine(machineId uint64, getMachine func(uint64) *entity.Machi
}
// 关闭ssh隧道机器的指定隧道
func CloseSshTunnelMachine(machineId uint64, tunnelId uint64) {
func CloseSshTunnelMachine(machineId int, tunnelId uint64) {
sshTunnelMachine := sshTunnelMachines[machineId]
if sshTunnelMachine == nil {
return
@@ -182,7 +181,7 @@ func CloseSshTunnelMachine(machineId uint64, tunnelId uint64) {
type Tunnel struct {
id uint64 // 唯一标识
machineId uint64 // 隧道机器id
machineId int // 隧道机器id
localHost string // 本地监听地址
localPort int // 本地端口
remoteHost string // 远程连接地址

View File

@@ -0,0 +1,49 @@
package persistence
import (
"mayfly-go/internal/machine/domain/entity"
"mayfly-go/internal/machine/domain/repository"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/model"
)
type authCertRepoImpl struct{}
func newAuthCertRepo() repository.AuthCert {
return new(authCertRepoImpl)
}
func (m *authCertRepoImpl) GetPageList(condition *entity.AuthCert, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
return model.GetPage(pageParam, condition, condition, toEntity)
}
func (m *authCertRepoImpl) Insert(ac *entity.AuthCert) {
biz.ErrIsNil(model.Insert(ac), "新增授权凭证失败")
}
func (m *authCertRepoImpl) Update(ac *entity.AuthCert) {
biz.ErrIsNil(model.UpdateById(ac), "更新授权凭证失败")
}
func (m *authCertRepoImpl) GetById(id uint64) *entity.AuthCert {
ac := new(entity.AuthCert)
err := model.GetById(ac, id)
if err != nil {
return nil
}
return ac
}
func (m *authCertRepoImpl) GetByIds(ids ...uint64) []*entity.AuthCert {
acs := new([]*entity.AuthCert)
model.GetByIdIn(new(entity.AuthCert), acs, ids)
return *acs
}
func (m *authCertRepoImpl) GetByCondition(condition *entity.AuthCert, cols ...string) error {
return model.GetBy(condition, cols...)
}
func (m *authCertRepoImpl) DeleteById(id uint64) {
model.DeleteById(new(entity.AuthCert), id)
}

View File

@@ -6,8 +6,6 @@ import (
"mayfly-go/internal/machine/domain/repository"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils"
"strings"
)
type machineRepoImpl struct{}
@@ -30,7 +28,8 @@ func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pagePar
values = append(values, "%"+condition.Name+"%")
}
if len(condition.TagIds) > 0 {
sql = fmt.Sprintf("%s AND m.tag_id IN (%s) ", sql, strings.Join(utils.NumberArr2StrArr(condition.TagIds), ","))
sql = fmt.Sprintf("%s AND m.tag_id IN ? ", sql)
values = append(values, condition.TagIds)
}
if condition.TagPathLike != "" {
sql = sql + " AND m.tag_path LIKE ?"

View File

@@ -6,6 +6,7 @@ var (
machineRepo repository.Machine = newMachineRepo()
machineFileRepo repository.MachineFile = newMachineFileRepo()
machineScriptRepo repository.MachineScript = newMachineScriptRepo()
authCertRepo = newAuthCertRepo()
)
func GetMachineRepo() repository.Machine {
@@ -19,3 +20,7 @@ func GetMachineFileRepo() repository.MachineFile {
func GetMachineScriptRepo() repository.MachineScript {
return machineScriptRepo
}
func GetAuthCertRepo() repository.AuthCert {
return authCertRepo
}

View File

@@ -0,0 +1,45 @@
package router
import (
"mayfly-go/internal/machine/api"
"mayfly-go/internal/machine/application"
"mayfly-go/pkg/req"
"github.com/gin-gonic/gin"
)
func InitAuthCertRouter(router *gin.RouterGroup) {
r := &api.AuthCert{AuthCertApp: application.GetAuthCertApp()}
db := router.Group("sys/authcerts")
{
listAcP := req.NewPermission("authcert")
db.GET("", func(c *gin.Context) {
req.NewCtxWithGin(c).
WithRequiredPermission(listAcP).
Handle(r.AuthCerts)
})
// 基础授权凭证信息,不包含密码等
db.GET("base", func(c *gin.Context) {
req.NewCtxWithGin(c).Handle(r.BaseAuthCerts)
})
saveAc := req.NewLogInfo("保存授权凭证").WithSave(true)
saveAcP := req.NewPermission("authcert:save")
db.POST("", func(c *gin.Context) {
req.NewCtxWithGin(c).
WithLog(saveAc).
WithRequiredPermission(saveAcP).
Handle(r.SaveAuthCert)
})
deleteAc := req.NewLogInfo("删除授权凭证").WithSave(true)
deleteAcP := req.NewPermission("authcert:del")
db.DELETE(":id", func(c *gin.Context) {
req.NewCtxWithGin(c).
WithLog(deleteAc).
WithRequiredPermission(deleteAcP).
Handle(r.Delete)
})
}
}

View File

@@ -21,10 +21,6 @@ func InitMachineRouter(router *gin.RouterGroup) {
req.NewCtxWithGin(c).Handle(m.Machines)
})
machines.GET(":machineId/pwd", func(c *gin.Context) {
req.NewCtxWithGin(c).Handle(m.GetMachinePwd)
})
machines.GET(":machineId/stats", func(c *gin.Context) {
req.NewCtxWithGin(c).Handle(m.MachineStats)
})
@@ -52,6 +48,11 @@ func InitMachineRouter(router *gin.RouterGroup) {
Handle(m.SaveMachine)
})
machines.POST("test-conn", func(c *gin.Context) {
req.NewCtxWithGin(c).
Handle(m.TestConn)
})
changeStatus := req.NewLogInfo("调整机器状态").WithSave(true)
machines.PUT(":machineId/:status", func(c *gin.Context) {
req.NewCtxWithGin(c).

View File

@@ -6,4 +6,5 @@ func Init(router *gin.RouterGroup) {
InitMachineRouter(router)
InitMachineFileRouter(router)
InitMachineScriptRouter(router)
InitAuthCertRouter(router)
}

View File

@@ -3,8 +3,7 @@ package form
type Mongo struct {
Id uint64
Uri string `binding:"required" json:"uri"`
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
Name string `binding:"required" json:"name"`
TagId uint64 `binding:"required" json:"tagId"`
TagPath string `json:"tagPath"`

View File

@@ -108,7 +108,7 @@ var mongoCliCache = cache.NewTimedCache(constant.MongoConnExpireTime, 5*time.Sec
})
func init() {
machine.AddCheckSshTunnelMachineUseFunc(func(machineId uint64) bool {
machine.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
// 遍历所有mongo连接实例若存在redis实例使用该ssh隧道机器则返回true表示还在使用中...
items := mongoCliCache.Items()
for _, v := range items {
@@ -144,7 +144,7 @@ type MongoInfo struct {
Id uint64
Name string
TagPath string
SshTunnelMachineId uint64 // ssh隧道机器id
SshTunnelMachineId int // ssh隧道机器id
}
func (m *MongoInfo) GetLogDesc() string {
@@ -177,7 +177,7 @@ func connect(me *entity.Mongo) (*MongoInstance, error) {
mongoOptions := options.Client().ApplyURI(me.Uri).
SetMaxPoolSize(1)
// 启用ssh隧道则连接隧道机器
if me.EnableSshTunnel == 1 {
if me.SshTunnelMachineId > 0 {
mongoOptions.SetDialer(&MongoSshDialer{machineId: me.SshTunnelMachineId})
}
@@ -206,7 +206,7 @@ func toMongiInfo(me *entity.Mongo) *MongoInfo {
}
type MongoSshDialer struct {
machineId uint64
machineId int
}
func (sd *MongoSshDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {

View File

@@ -7,8 +7,7 @@ type Mongo struct {
Name string `orm:"column(name)" json:"name"`
Uri string `orm:"column(uri)" json:"uri"`
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
TagId uint64 `json:"tagId"`
TagPath string `json:"tagPath"`
}

View File

@@ -7,7 +7,6 @@ type MongoQuery struct {
Name string
Uri string
EnableSshTunnel int8 // 是否启用ssh隧道
SshTunnelMachineId uint64 // ssh隧道机器id
TagId uint64 `json:"tagId"`
TagPath string `json:"tagPath"`

View File

@@ -7,8 +7,7 @@ type Redis struct {
Password string `json:"password"`
Mode string `json:"mode"`
Db string `json:"db"`
EnableSshTunnel int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
TagId uint64 `binding:"required" json:"tagId"`
TagPath string `json:"tagPath"`
Remark string `json:"remark"`

View File

@@ -8,8 +8,7 @@ type Redis struct {
Host *string `json:"host"`
Db string `json:"db"`
Mode *string `json:"mode"`
EnableSshTunnel *int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId *uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
Remark *string `json:"remark"`
TagId *uint64 `json:"tagId"`
TagPath *string `json:"tagPath"`

View File

@@ -189,7 +189,7 @@ func getRedisCient(re *entity.Redis, db int) *RedisInstance {
ReadTimeout: -1, // Disable timeouts, because SSH does not support deadlines.
WriteTimeout: -1,
}
if re.EnableSshTunnel == 1 {
if re.SshTunnelMachineId > 0 {
redisOptions.Dialer = getRedisDialer(re.SshTunnelMachineId)
}
ri.Cli = redis.NewClient(redisOptions)
@@ -204,7 +204,7 @@ func getRedisClusterClient(re *entity.Redis) *RedisInstance {
Password: re.Password,
DialTimeout: 8 * time.Second,
}
if re.EnableSshTunnel == 1 {
if re.SshTunnelMachineId > 0 {
redisClusterOptions.Dialer = getRedisDialer(re.SshTunnelMachineId)
}
ri.ClusterCli = redis.NewClusterClient(redisClusterOptions)
@@ -224,14 +224,14 @@ func getRedisSentinelCient(re *entity.Redis, db int) *RedisInstance {
ReadTimeout: -1, // Disable timeouts, because SSH does not support deadlines.
WriteTimeout: -1,
}
if re.EnableSshTunnel == 1 {
if re.SshTunnelMachineId > 0 {
sentinelOptions.Dialer = getRedisDialer(re.SshTunnelMachineId)
}
ri.Cli = redis.NewFailoverClient(sentinelOptions)
return ri
}
func getRedisDialer(machineId uint64) func(ctx context.Context, network, addr string) (net.Conn, error) {
func getRedisDialer(machineId int) func(ctx context.Context, network, addr string) (net.Conn, error) {
sshTunnel := machineapp.GetMachineApp().GetSshTunnelMachine(machineId)
return func(_ context.Context, network, addr string) (net.Conn, error) {
if sshConn, err := sshTunnel.GetDialConn(network, addr); err == nil {
@@ -259,7 +259,7 @@ func CloseRedis(id uint64, db int) {
}
func init() {
machine.AddCheckSshTunnelMachineUseFunc(func(machineId uint64) bool {
machine.AddCheckSshTunnelMachineUseFunc(func(machineId int) bool {
// 遍历所有redis连接实例若存在redis实例使用该ssh隧道机器则返回true表示还在使用中...
items := redisCache.Items()
for _, v := range items {
@@ -303,7 +303,7 @@ type RedisInfo struct {
Mode string
Name string
SshTunnelMachineId uint64
SshTunnelMachineId int
}
// 获取记录日志的描述

View File

@@ -10,8 +10,7 @@ type RedisQuery struct {
Mode string `json:"mode"`
Password string `orm:"column(password)" json:"-"`
Db string `orm:"column(database)" json:"db"`
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
Remark string
TagId uint64

View File

@@ -13,8 +13,7 @@ type Redis struct {
Mode string `json:"mode"`
Password string `orm:"column(password)" json:"-"`
Db string `orm:"column(database)" json:"db"`
EnableSshTunnel int8 `orm:"column(enable_ssh_tunnel)" json:"enableSshTunnel"` // 是否启用ssh隧道
SshTunnelMachineId uint64 `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id
Remark string
TagId uint64
TagPath string

View File

@@ -1,6 +1,8 @@
package application
import "mayfly-go/internal/sys/infrastructure/persistence"
import (
"mayfly-go/internal/sys/infrastructure/persistence"
)
var (
accountApp = newAccountApp(persistence.GetAccountRepo())

View File

@@ -42,14 +42,14 @@ func (p *TagTree) ListByQuery(rc *req.Ctx) {
}
func (p *TagTree) SaveTagTree(rc *req.Ctx) {
projectTree := &entity.TagTree{}
ginx.BindJsonAndValid(rc.GinCtx, projectTree)
tagTree := &entity.TagTree{}
ginx.BindJsonAndValid(rc.GinCtx, tagTree)
loginAccount := rc.LoginAccount
projectTree.SetBaseInfo(loginAccount)
p.TagTreeApp.Save(projectTree)
tagTree.SetBaseInfo(loginAccount)
p.TagTreeApp.Save(tagTree)
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, codePath: %s", projectTree.Id, projectTree.Name, projectTree.CodePath)
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, codePath: %s", tagTree.Id, tagTree.Name, tagTree.CodePath)
}
func (p *TagTree) DelTagTree(rc *req.Ctx) {

View File

@@ -20,7 +20,7 @@ type TagTree interface {
GetById(id uint64) *entity.TagTree
Save(project *entity.TagTree)
Save(tt *entity.TagTree)
Delete(id uint64)

View File

@@ -11,7 +11,7 @@ type Team interface {
// 分页获取项目团队信息列表
GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
Save(projectTeam *entity.Team)
Save(team *entity.Team)
Delete(id uint64)
@@ -19,99 +19,96 @@ type Team interface {
GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult
SaveMember(projectTeamMember *entity.TeamMember)
SaveMember(tagTeamMember *entity.TeamMember)
DeleteMember(teamId, accountId uint64)
IsExistMember(teamId, accounId uint64) bool
// 账号是否有权限访问该项目关联的资源信息
// CanAccess(accountId, projectId uint64) error
//--------------- 关联项目相关接口 ---------------
ListTagIds(teamId uint64) []uint64
SaveTag(tagTeam *entity.TagTreeTeam)
DeleteTag(teamId, projectId uint64)
DeleteTag(teamId, tagId uint64)
}
func newTeamApp(projectTeamRepo repository.Team,
projectTeamMemberRepo repository.TeamMember,
func newTeamApp(teamRepo repository.Team,
teamMemberRepo repository.TeamMember,
tagTreeTeamRepo repository.TagTreeTeam,
) Team {
return &projectTeamAppImpl{
projectTeamRepo: projectTeamRepo,
projectTeamMemberRepo: projectTeamMemberRepo,
tagTreeTeamRepo: tagTreeTeamRepo,
return &teamAppImpl{
teamRepo: teamRepo,
teamMemberRepo: teamMemberRepo,
tagTreeTeamRepo: tagTreeTeamRepo,
}
}
type projectTeamAppImpl struct {
projectTeamRepo repository.Team
projectTeamMemberRepo repository.TeamMember
tagTreeTeamRepo repository.TagTreeTeam
type teamAppImpl struct {
teamRepo repository.Team
teamMemberRepo repository.TeamMember
tagTreeTeamRepo repository.TagTreeTeam
}
func (p *projectTeamAppImpl) GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
return p.projectTeamRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
func (p *teamAppImpl) GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
return p.teamRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
}
func (p *projectTeamAppImpl) Save(projectTeam *entity.Team) {
if projectTeam.Id == 0 {
p.projectTeamRepo.Insert(projectTeam)
func (p *teamAppImpl) Save(team *entity.Team) {
if team.Id == 0 {
p.teamRepo.Insert(team)
} else {
p.projectTeamRepo.UpdateById(projectTeam)
p.teamRepo.UpdateById(team)
}
}
func (p *projectTeamAppImpl) Delete(id uint64) {
p.projectTeamRepo.Delete(id)
p.projectTeamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: id})
func (p *teamAppImpl) Delete(id uint64) {
p.teamRepo.Delete(id)
p.teamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: id})
}
// --------------- 团队成员相关接口 ---------------
func (p *projectTeamAppImpl) GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult {
return p.projectTeamMemberRepo.GetPageList(condition, pageParam, toEntity)
func (p *teamAppImpl) GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult {
return p.teamMemberRepo.GetPageList(condition, pageParam, toEntity)
}
// 保存团队成员信息
func (p *projectTeamAppImpl) SaveMember(projectTeamMember *entity.TeamMember) {
projectTeamMember.Id = 0
biz.IsTrue(!p.projectTeamMemberRepo.IsExist(projectTeamMember.TeamId, projectTeamMember.AccountId), "该成员已存在")
p.projectTeamMemberRepo.Save(projectTeamMember)
func (p *teamAppImpl) SaveMember(teamMember *entity.TeamMember) {
teamMember.Id = 0
biz.IsTrue(!p.teamMemberRepo.IsExist(teamMember.TeamId, teamMember.AccountId), "该成员已存在")
p.teamMemberRepo.Save(teamMember)
}
// 删除团队成员信息
func (p *projectTeamAppImpl) DeleteMember(teamId, accountId uint64) {
p.projectTeamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: teamId, AccountId: accountId})
func (p *teamAppImpl) DeleteMember(teamId, accountId uint64) {
p.teamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: teamId, AccountId: accountId})
}
func (p *projectTeamAppImpl) IsExistMember(teamId, accounId uint64) bool {
return p.projectTeamMemberRepo.IsExist(teamId, accounId)
func (p *teamAppImpl) IsExistMember(teamId, accounId uint64) bool {
return p.teamMemberRepo.IsExist(teamId, accounId)
}
//--------------- 关联项目相关接口 ---------------
func (p *projectTeamAppImpl) ListTagIds(teamId uint64) []uint64 {
projects := &[]entity.TagTreeTeam{}
p.tagTreeTeamRepo.ListProject(&entity.TagTreeTeam{TeamId: teamId}, projects)
func (p *teamAppImpl) ListTagIds(teamId uint64) []uint64 {
tags := &[]entity.TagTreeTeam{}
p.tagTreeTeamRepo.ListTag(&entity.TagTreeTeam{TeamId: teamId}, tags)
ids := make([]uint64, 0)
for _, v := range *projects {
for _, v := range *tags {
ids = append(ids, v.TagId)
}
return ids
}
// 保存关联项目信息
func (p *projectTeamAppImpl) SaveTag(projectTreeTeam *entity.TagTreeTeam) {
projectTreeTeam.Id = 0
p.tagTreeTeamRepo.Save(projectTreeTeam)
func (p *teamAppImpl) SaveTag(tagTreeTeam *entity.TagTreeTeam) {
tagTreeTeam.Id = 0
p.tagTreeTeamRepo.Save(tagTreeTeam)
}
// 删除关联项目信息
func (p *projectTeamAppImpl) DeleteTag(teamId, tagId uint64) {
func (p *teamAppImpl) DeleteTag(teamId, tagId uint64) {
p.tagTreeTeamRepo.DeleteBy(&entity.TagTreeTeam{TeamId: teamId, TagId: tagId})
}

View File

@@ -4,8 +4,8 @@ import "mayfly-go/internal/tag/domain/entity"
type TagTreeTeam interface {
// 获取团队项目信息列表
ListProject(condition *entity.TagTreeTeam, toEntity interface{}, orderBy ...string)
// 获取团队标签信息列表
ListTag(condition *entity.TagTreeTeam, toEntity interface{}, orderBy ...string)
Save(mp *entity.TagTreeTeam)

View File

@@ -64,12 +64,12 @@ func (a *tagTreeRepoImpl) GetBy(condition *entity.TagTree, cols ...string) error
return model.GetBy(condition, cols...)
}
func (p *tagTreeRepoImpl) Insert(project *entity.TagTree) {
biz.ErrIsNil(model.Insert(project), "新增标签失败")
func (p *tagTreeRepoImpl) Insert(tagTree *entity.TagTree) {
biz.ErrIsNil(model.Insert(tagTree), "新增标签失败")
}
func (p *tagTreeRepoImpl) UpdateById(project *entity.TagTree) {
biz.ErrIsNil(model.UpdateById(project), "更新标签失败")
func (p *tagTreeRepoImpl) UpdateById(tagTree *entity.TagTree) {
biz.ErrIsNil(model.UpdateById(tagTree), "更新标签失败")
}
func (p *tagTreeRepoImpl) Delete(id uint64) {

View File

@@ -13,7 +13,7 @@ func newTagTreeTeamRepo() repository.TagTreeTeam {
return new(tagTreeTeamRepoImpl)
}
func (p *tagTreeTeamRepoImpl) ListProject(condition *entity.TagTreeTeam, toEntity interface{}, orderBy ...string) {
func (p *tagTreeTeamRepoImpl) ListTag(condition *entity.TagTreeTeam, toEntity interface{}, orderBy ...string) {
model.ListByOrder(condition, toEntity, orderBy...)
}

View File

@@ -17,18 +17,18 @@ func (p *teamRepoImpl) GetPageList(condition *entity.Team, pageParam *model.Page
return model.GetPage(pageParam, condition, condition, toEntity, orderBy...)
}
func (p *teamRepoImpl) Insert(projectTeam *entity.Team) {
biz.ErrIsNil(model.Insert(projectTeam), "新增团队失败")
func (p *teamRepoImpl) Insert(team *entity.Team) {
biz.ErrIsNil(model.Insert(team), "新增团队失败")
}
func (p *teamRepoImpl) UpdateById(projectTeam *entity.Team) {
biz.ErrIsNil(model.UpdateById(projectTeam), "更新团队失败")
func (p *teamRepoImpl) UpdateById(team *entity.Team) {
biz.ErrIsNil(model.UpdateById(team), "更新团队失败")
}
func (p *teamRepoImpl) Delete(id uint64) {
model.DeleteById(new(entity.Team), id)
}
func (p *teamRepoImpl) DeleteBy(projectTeam *entity.Team) {
model.DeleteByCondition(projectTeam)
func (p *teamRepoImpl) DeleteBy(team *entity.Team) {
model.DeleteByCondition(team)
}

View File

@@ -13,37 +13,37 @@ func InitTagTreeRouter(router *gin.RouterGroup) {
TagTreeApp: application.GetTagTreeApp(),
}
project := router.Group("/tag-trees")
tagTree := router.Group("/tag-trees")
{
// 获取标签树列表
project.GET("", func(c *gin.Context) {
tagTree.GET("", func(c *gin.Context) {
req.NewCtxWithGin(c).Handle(m.GetTagTree)
})
// 根据条件获取标签
project.GET("query", func(c *gin.Context) {
tagTree.GET("query", func(c *gin.Context) {
req.NewCtxWithGin(c).Handle(m.ListByQuery)
})
// 获取登录账号拥有的标签信息
project.GET("account-has", func(c *gin.Context) {
tagTree.GET("account-has", func(c *gin.Context) {
req.NewCtxWithGin(c).Handle(m.GetAccountTags)
})
saveProjectTreeLog := req.NewLogInfo("标签树-保存信息").WithSave(true)
saveTagTreeLog := req.NewLogInfo("标签树-保存信息").WithSave(true)
savePP := req.NewPermission("tag:save")
// 保存项目树下的环境信息
project.POST("", func(c *gin.Context) {
req.NewCtxWithGin(c).WithLog(saveProjectTreeLog).
tagTree.POST("", func(c *gin.Context) {
req.NewCtxWithGin(c).WithLog(saveTagTreeLog).
WithRequiredPermission(savePP).
Handle(m.SaveTagTree)
})
delProjectLog := req.NewLogInfo("标签树-删除信息").WithSave(true)
delTagLog := req.NewLogInfo("标签树-删除信息").WithSave(true)
delPP := req.NewPermission("tag:del")
// 删除标签
project.DELETE(":id", func(c *gin.Context) {
req.NewCtxWithGin(c).WithLog(delProjectLog).
tagTree.DELETE(":id", func(c *gin.Context) {
req.NewCtxWithGin(c).WithLog(delTagLog).
WithRequiredPermission(delPP).
Handle(m.DelTagTree)
})

View File

@@ -30,7 +30,6 @@ CREATE TABLE `t_db` (
`database` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '数据库,空格分割多个数据库',
`params` varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '其他连接参数',
`network` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`enable_ssh_tunnel` tinyint(2) DEFAULT NULL COMMENT '是否启用ssh隧道',
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
`remark` varchar(125) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注,描述等',
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
@@ -105,6 +104,23 @@ CREATE TABLE `t_db_sql_exec` (
BEGIN;
COMMIT;
DROP TABLE IF EXISTS `t_auth_cert`;
CREATE TABLE `t_auth_cert` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`auth_method` tinyint NOT NULL COMMENT '1.密码 2.秘钥',
`password` varchar(4200) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码or私钥',
`passphrase` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '私钥口令',
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`create_time` datetime NOT NULL,
`creator` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`creator_id` bigint NOT NULL,
`update_time` datetime NOT NULL,
`modifier` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`modifier_id` bigint NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='授权凭证';
-- ----------------------------
-- Table structure for t_machine
-- ----------------------------
@@ -116,8 +132,8 @@ CREATE TABLE `t_machine` (
`port` int(12) NOT NULL,
`username` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`auth_method` tinyint(2) DEFAULT NULL COMMENT '1.密码登录2.publickey登录',
`password` varchar(3200) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`enable_ssh_tunnel` tinyint(2) DEFAULT NULL COMMENT '是否启用ssh隧道',
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin 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:禁用',
@@ -226,7 +242,6 @@ CREATE TABLE `t_mongo` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '名称',
`uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '连接uri',
`enable_ssh_tunnel` tinyint(2) DEFAULT NULL COMMENT '是否启用ssh隧道',
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
`tag_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '标签路径',
@@ -256,7 +271,6 @@ CREATE TABLE `t_redis` (
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`db` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '库号: 多个库用,分割',
`mode` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,
`enable_ssh_tunnel` tinyint(2) DEFAULT NULL COMMENT '是否启用ssh隧道',
`ssh_tunnel_machine_id` bigint(20) DEFAULT NULL COMMENT 'ssh隧道的机器id',
`remark` varchar(125) COLLATE utf8mb4_bin DEFAULT NULL,
`tag_id` bigint(20) DEFAULT NULL COMMENT '标签id',
@@ -498,6 +512,10 @@ INSERT INTO `t_sys_resource` VALUES (99, 95, 2, 1, '删除团队', 'team:del', 2
INSERT INTO `t_sys_resource` VALUES (100, 95, 2, 1, '新增团队成员', 'team:member:save', 3, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:27', '2022-10-26 13:59:27');
INSERT INTO `t_sys_resource` VALUES (101, 95, 2, 1, '移除团队成员', 'team:member:del', 4, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:43', '2022-10-26 13:59:43');
INSERT INTO `t_sys_resource` VALUES (102, 95, 2, 1, '保存团队标签', 'team:tag:save', 5, 'null', 1, 'admin', 1, 'admin', '2022-10-26 13:59:57', '2022-10-26 13:59:57');
INSERT INTO `t_sys_resource` VALUES (103, 2, 1, 1, '授权凭证', 'authcerts', 6, '{"component":"AuthCertList","icon":"Unlock","isKeepAlive":true,"routeName":"AuthCertList"}', 1, 'admin', 1, 'admin', '2023-02-23 11:36:26', '2023-02-23 14:40:23');
INSERT INTO `t_sys_resource` VALUES (104, 103, 2, 1, '基本权限', 'authcert', 1, 'null', 1, 'admin', 1, 'admin', '2023-02-23 11:37:24', '2023-02-23 11:37:24');
INSERT INTO `t_sys_resource` VALUES (105, 103, 2, 1, '保存权限', 'authcert:save', 2, 'null', 1, 'admin', 1, 'admin', '2023-02-23 11:37:54', '2023-02-23 11:37:54');
INSERT INTO `t_sys_resource` VALUES (106, 103, 2, 1, '删除权限', 'authcert:del', 3, 'null', 1, 'admin', 1, 'admin', '2023-02-23 11:38:09', '2023-02-23 11:38:09');
COMMIT;
-- ----------------------------
@@ -702,6 +720,10 @@ INSERT INTO `t_sys_role_resource` VALUES (522, 1, 99, 1, 'admin', '2022-10-26 20
INSERT INTO `t_sys_role_resource` VALUES (523, 1, 100, 1, 'admin', '2022-10-26 20:03:14');
INSERT INTO `t_sys_role_resource` VALUES (524, 1, 101, 1, 'admin', '2022-10-26 20:03:14');
INSERT INTO `t_sys_role_resource` VALUES (525, 1, 102, 1, 'admin', '2022-10-26 20:03:14');
INSERT INTO `t_sys_role_resource` VALUES (526, 1, 103, 1, 'admin', '2022-10-26 20:03:14');
INSERT INTO `t_sys_role_resource` VALUES (527, 1, 104, 1, 'admin', '2022-10-26 20:03:14');
INSERT INTO `t_sys_role_resource` VALUES (528, 1, 105, 1, 'admin', '2022-10-26 20:03:14');
INSERT INTO `t_sys_role_resource` VALUES (529, 1, 106, 1, 'admin', '2022-10-26 20:03:14');
COMMIT;
-- ----------------------------

View File

@@ -8,7 +8,7 @@ import (
"reflect"
)
func ErrIsNil(err error, msg string, params ...interface{}) {
func ErrIsNil(err error, msg string, params ...any) {
if err != nil {
global.Log.Error(msg + ": " + err.Error())
panic(NewBizErr(fmt.Sprintf(msg, params...)))
@@ -31,7 +31,7 @@ func IsNil(err error) {
}
}
func IsTrue(exp bool, msg string, params ...interface{}) {
func IsTrue(exp bool, msg string, params ...any) {
if !exp {
panic(NewBizErr(fmt.Sprintf(msg, params...)))
}
@@ -43,21 +43,21 @@ func IsTrueBy(exp bool, err BizError) {
}
}
func NotEmpty(str string, msg string, params ...interface{}) {
func NotEmpty(str string, msg string, params ...any) {
if str == "" {
panic(NewBizErr(fmt.Sprintf(msg, params...)))
}
}
func NotNil(data interface{}, msg string) {
func NotNil(data interface{}, msg string, params ...any) {
if reflect.ValueOf(data).IsNil() {
panic(NewBizErr(msg))
panic(NewBizErr(fmt.Sprintf(msg, params...)))
}
}
func NotBlank(data interface{}, msg string) {
func NotBlank(data interface{}, msg string, params ...any) {
if utils.IsBlank(data) {
panic(NewBizErr(msg))
panic(NewBizErr(fmt.Sprintf(msg, params...)))
}
}

View File

@@ -4,7 +4,7 @@ import "fmt"
const (
AppName = "mayfly-go"
Version = "v1.4.0"
Version = "v1.4.1"
)
func GetAppInfo() string {