mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
feat: 新增机器授权凭证管理与其他优化
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -18,7 +18,6 @@ type DbQuery struct {
|
||||
Remark string `json:"remark"`
|
||||
TagId uint64
|
||||
|
||||
ProjectIds []uint64
|
||||
TagIds []uint64
|
||||
TagPathLike string
|
||||
}
|
||||
|
||||
62
server/internal/machine/api/auth_cert.go
Normal file
62
server/internal/machine/api/auth_cert.go
Normal 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")))
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
64
server/internal/machine/application/auth_cert_app.go
Normal file
64
server/internal/machine/application/auth_cert_app.go
Normal 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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
41
server/internal/machine/domain/entity/auth_cert.go
Normal file
41
server/internal/machine/domain/entity/auth_cert.go
Normal 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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
22
server/internal/machine/domain/repository/auth_cert.go
Normal file
22
server/internal/machine/domain/repository/auth_cert.go
Normal 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)
|
||||
}
|
||||
@@ -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("机器不存在")
|
||||
}
|
||||
|
||||
@@ -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 // 远程连接地址
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 ?"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
45
server/internal/machine/router/auth_cert.go
Normal file
45
server/internal/machine/router/auth_cert.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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).
|
||||
|
||||
@@ -6,4 +6,5 @@ func Init(router *gin.RouterGroup) {
|
||||
InitMachineRouter(router)
|
||||
InitMachineFileRouter(router)
|
||||
InitMachineScriptRouter(router)
|
||||
InitAuthCertRouter(router)
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// 获取记录日志的描述
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -20,7 +20,7 @@ type TagTree interface {
|
||||
|
||||
GetById(id uint64) *entity.TagTree
|
||||
|
||||
Save(project *entity.TagTree)
|
||||
Save(tt *entity.TagTree)
|
||||
|
||||
Delete(id uint64)
|
||||
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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;
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
@@ -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...)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import "fmt"
|
||||
|
||||
const (
|
||||
AppName = "mayfly-go"
|
||||
Version = "v1.4.0"
|
||||
Version = "v1.4.1"
|
||||
)
|
||||
|
||||
func GetAppInfo() string {
|
||||
|
||||
Reference in New Issue
Block a user