dev (#239)
* 优化图标和LOGO * 修改登录页面动画的速度为3 * 增加对websocket的异常处理 * 修复了用户组和用户名唯一判断错误的问题 * 提示版本号 * 修复readme错别字 * 修复单词拼写错误的问题 * 修复代码格式 * 修改Windows资产属性名称 * Docker 打包流程增加 upx 压缩 * 升级依赖文件,修改sqlite驱动为 github.com/glebarez/sqlite * 修复第一次查询「授权令牌」的错误 * 移除无关代码 * 修改docker打包脚本 * 增加打包脚本 * 增加微信群 * 修复单词拼写错误的问题 * 修复代码格式 * 修改Windows资产属性名称 * Docker 打包流程增加 upx 压缩 * 修改docker打包脚本 * - 替换 sqlite 驱动为 github.com/glebarez/sqlite - 修复数据库锁定的问题 - 修复部分代码不完善的问题 - 修复策略显示不完整的问题 - 修复编辑文件换行符的问题 - 优化guacd连接
This commit is contained in:
parent
3ec6e5332e
commit
04f8d0079b
@ -168,10 +168,7 @@ func (api AccountApi) LoginWithTotpEndpoint(c echo.Context) error {
|
||||
|
||||
func (api AccountApi) LogoutEndpoint(c echo.Context) error {
|
||||
token := GetToken(c)
|
||||
err := service.UserService.LogoutByToken(token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service.UserService.Logout(token)
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
@ -317,6 +314,10 @@ func (api AccountApi) AccountAssetEndpoint(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range items {
|
||||
items[i].IP = ""
|
||||
items[i].Port = 0
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
"total": total,
|
||||
|
@ -28,7 +28,7 @@ func (assetApi AssetApi) AssetCreateEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
m["owner"] = account.ID
|
||||
|
||||
if _, err := service.AssetService.Create(m); err != nil {
|
||||
if _, err := service.AssetService.Create(context.TODO(), m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ func (api BackupApi) BackupExportEndpoint(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=next-terminal_backup_%s.json", time.Now().Format("20060102150405")))
|
||||
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=backup_%s.json", time.Now().Format("20060102150405")))
|
||||
return c.Stream(http.StatusOK, echo.MIMEOctetStream, bytes.NewReader(jsonBytes))
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ func (api CredentialApi) CredentialCreateEndpoint(c echo.Context) error {
|
||||
|
||||
item.Encrypted = true
|
||||
|
||||
if err := service.CredentialService.Create(&item); err != nil {
|
||||
if err := service.CredentialService.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -159,6 +159,7 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
|
||||
guacamoleHandler := NewGuacamoleHandler(ws, guacdTunnel)
|
||||
guacamoleHandler.Start()
|
||||
defer guacamoleHandler.Stop()
|
||||
|
||||
for {
|
||||
_, message, err := ws.ReadMessage()
|
||||
@ -168,7 +169,6 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
_ = guacdTunnel.Close()
|
||||
|
||||
service.SessionService.CloseSessionById(sessionId, Normal, "用户正常退出")
|
||||
guacamoleHandler.Stop()
|
||||
return nil
|
||||
}
|
||||
_, err = guacdTunnel.WriteAndFlush(message)
|
||||
@ -193,7 +193,7 @@ func (api GuacamoleApi) setAssetConfig(attributes map[string]string, s model.Ses
|
||||
}
|
||||
realPath := path.Join(service.StorageService.GetBaseDrivePath(), storageId)
|
||||
configuration.SetParameter(guacd.EnableDrive, "true")
|
||||
configuration.SetParameter(guacd.DriveName, "Next Terminal Filesystem")
|
||||
configuration.SetParameter(guacd.DriveName, "Filesystem")
|
||||
configuration.SetParameter(guacd.DrivePath, realPath)
|
||||
log.Debugf("[%v] 会话 %v:%v 映射目录地址为 %v", s.ID, s.IP, s.Port, realPath)
|
||||
} else {
|
||||
@ -258,6 +258,7 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
|
||||
guacamoleHandler := NewGuacamoleHandler(ws, guacdTunnel)
|
||||
guacamoleHandler.Start()
|
||||
defer guacamoleHandler.Stop()
|
||||
|
||||
for {
|
||||
_, message, err := ws.ReadMessage()
|
||||
@ -269,7 +270,6 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
observerId := nextSession.ID
|
||||
forObsSession.Observer.Del <- observerId
|
||||
log.Debugf("[%v:%v] 观察者[%v]退出会话", sessionId, connectionId, observerId)
|
||||
guacamoleHandler.Stop()
|
||||
return nil
|
||||
}
|
||||
_, err = guacdTunnel.WriteAndFlush(message)
|
||||
|
@ -2,7 +2,6 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"next-terminal/server/guacd"
|
||||
"next-terminal/server/log"
|
||||
@ -12,73 +11,47 @@ import (
|
||||
)
|
||||
|
||||
type GuacamoleHandler struct {
|
||||
ws *websocket.Conn
|
||||
tunnel *guacd.Tunnel
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
dataChan chan []byte
|
||||
tick *time.Ticker
|
||||
ws *websocket.Conn
|
||||
tunnel *guacd.Tunnel
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewGuacamoleHandler(ws *websocket.Conn, tunnel *guacd.Tunnel) *GuacamoleHandler {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
||||
return &GuacamoleHandler{
|
||||
ws: ws,
|
||||
tunnel: tunnel,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
dataChan: make(chan []byte),
|
||||
tick: tick,
|
||||
ws: ws,
|
||||
tunnel: tunnel,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
}
|
||||
|
||||
func (r GuacamoleHandler) Start() {
|
||||
go r.readFormTunnel()
|
||||
go r.writeToWebsocket()
|
||||
}
|
||||
|
||||
func (r GuacamoleHandler) Stop() {
|
||||
r.tick.Stop()
|
||||
r.cancel()
|
||||
}
|
||||
|
||||
func (r GuacamoleHandler) readFormTunnel() {
|
||||
for {
|
||||
select {
|
||||
case <-r.ctx.Done():
|
||||
return
|
||||
default:
|
||||
instruction, err := r.tunnel.Read()
|
||||
if err != nil {
|
||||
utils.Disconnect(r.ws, TunnelClosed, "远程连接已关闭")
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-r.ctx.Done():
|
||||
return
|
||||
}
|
||||
if len(instruction) == 0 {
|
||||
continue
|
||||
}
|
||||
r.dataChan <- instruction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r GuacamoleHandler) writeToWebsocket() {
|
||||
var buf []byte
|
||||
for {
|
||||
select {
|
||||
case <-r.ctx.Done():
|
||||
return
|
||||
case <-r.tick.C:
|
||||
if len(buf) > 0 {
|
||||
err := r.ws.WriteMessage(websocket.TextMessage, buf)
|
||||
default:
|
||||
instruction, err := r.tunnel.Read()
|
||||
if err != nil {
|
||||
utils.Disconnect(r.ws, TunnelClosed, "远程连接已关闭")
|
||||
return
|
||||
}
|
||||
if len(instruction) == 0 {
|
||||
continue
|
||||
}
|
||||
err = r.ws.WriteMessage(websocket.TextMessage, instruction)
|
||||
if err != nil {
|
||||
log.Debugf("WebSocket写入失败,即将关闭Guacd连接...")
|
||||
return
|
||||
}
|
||||
buf = []byte{}
|
||||
}
|
||||
case data := <-r.dataChan:
|
||||
buf = append(buf, data...)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (r GuacamoleHandler) Stop() {
|
||||
r.cancel()
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func (api JobApi) JobCreateEndpoint(c echo.Context) error {
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
|
||||
if err := service.JobService.Create(&item); err != nil {
|
||||
if err := service.JobService.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
|
@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/service"
|
||||
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/repository"
|
||||
@ -42,7 +43,7 @@ func (api ResourceSharerApi) ResourceAddByUserIdAssignEndPoint(c echo.Context) e
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repository.ResourceSharerRepository.AddSharerResources(ru.UserGroupId, ru.UserId, ru.StrategyId, ru.ResourceType, ru.ResourceIds); err != nil {
|
||||
if err := service.UserService.AddSharerResources(context.TODO(), ru.UserGroupId, ru.UserId, ru.StrategyId, ru.ResourceType, ru.ResourceIds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -312,16 +312,16 @@ func (api SessionApi) SessionDownloadEndpoint(c echo.Context) error {
|
||||
if s.Download != "1" {
|
||||
return errors.New("禁止操作")
|
||||
}
|
||||
remoteFile := c.QueryParam("file")
|
||||
file := c.QueryParam("file")
|
||||
// 获取带后缀的文件名称
|
||||
filenameWithSuffix := path.Base(remoteFile)
|
||||
filenameWithSuffix := path.Base(file)
|
||||
if "ssh" == s.Protocol {
|
||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if nextSession == nil {
|
||||
return errors.New("获取会话失败")
|
||||
}
|
||||
|
||||
dstFile, err := nextSession.NextTerminal.SftpClient.Open(remoteFile)
|
||||
dstFile, err := nextSession.NextTerminal.SftpClient.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -337,7 +337,7 @@ func (api SessionApi) SessionDownloadEndpoint(c echo.Context) error {
|
||||
return c.Stream(http.StatusOK, echo.MIMEOctetStream, bytes.NewReader(buff.Bytes()))
|
||||
} else if "rdp" == s.Protocol {
|
||||
storageId := s.StorageId
|
||||
return service.StorageService.StorageDownload(c, remoteFile, storageId)
|
||||
return service.StorageService.StorageDownload(c, file, storageId)
|
||||
}
|
||||
|
||||
return err
|
||||
@ -541,7 +541,9 @@ func (api SessionApi) SessionRecordingEndpoint(c echo.Context) error {
|
||||
_ = repository.SessionRepository.UpdateReadByIds(context.TODO(), true, []string{sessionId})
|
||||
|
||||
log.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording))
|
||||
return c.File(recording)
|
||||
|
||||
http.ServeFile(c.Response(), c.Request(), recording)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api SessionApi) SessionGetEndpoint(c echo.Context) error {
|
||||
|
@ -134,7 +134,7 @@ func (api StorageApi) StorageDeleteEndpoint(c echo.Context) error {
|
||||
split := strings.Split(ids, ",")
|
||||
for i := range split {
|
||||
id := split[i]
|
||||
if err := service.StorageService.DeleteStorageById(id, false); err != nil {
|
||||
if err := service.StorageService.DeleteStorageById(context.TODO(), id, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -173,8 +173,8 @@ func (api StorageApi) StorageDownloadEndpoint(c echo.Context) error {
|
||||
if err := api.PermissionCheck(c, storageId); err != nil {
|
||||
return err
|
||||
}
|
||||
remoteFile := c.QueryParam("file")
|
||||
return service.StorageService.StorageDownload(c, remoteFile, storageId)
|
||||
file := c.QueryParam("file")
|
||||
return service.StorageService.StorageDownload(c, file, storageId)
|
||||
}
|
||||
|
||||
func (api StorageApi) StorageUploadEndpoint(c echo.Context) error {
|
||||
|
@ -157,6 +157,7 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
|
||||
termHandler := NewTermHandler(sessionId, isRecording, ws, nextTerminal)
|
||||
termHandler.Start()
|
||||
defer termHandler.Stop()
|
||||
|
||||
for {
|
||||
_, message, err := ws.ReadMessage()
|
||||
@ -164,7 +165,6 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
// web socket会话关闭后主动关闭ssh会话
|
||||
log.Debugf("WebSocket已关闭")
|
||||
service.SessionService.CloseSessionById(sessionId, Normal, "用户正常退出")
|
||||
termHandler.Stop()
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ func (userGroupApi UserGroupApi) UserGroupCreateEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := service.UserGroupService.Create(item.Name, item.Members); err != nil {
|
||||
if _, err := service.UserGroupService.Create(context.TODO(), item.Name, item.Members); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -141,7 +141,11 @@ func (userApi UserApi) UserChangePasswordEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
if user.Mail != "" {
|
||||
go service.MailService.SendMail(user.Mail, "[Next Terminal] 密码修改通知", "你好,"+user.Nickname+"。管理员已将你的密码修改为:"+password)
|
||||
subject := "密码修改通知"
|
||||
text := fmt.Sprintf(`您好,%s。
|
||||
管理员已将你的密码修改为:%s。
|
||||
`, user.Username, password)
|
||||
go service.MailService.SendMail(user.Mail, subject, text)
|
||||
}
|
||||
|
||||
return Success(c, "")
|
||||
|
@ -26,13 +26,6 @@ func newApp() *App {
|
||||
func init() {
|
||||
setupCache()
|
||||
app = newApp()
|
||||
if err := app.InitDBData(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := app.ReloadData(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.Server = setupRoutes()
|
||||
}
|
||||
|
||||
func (app App) InitDBData() (err error) {
|
||||
@ -96,7 +89,15 @@ func (app App) ReloadData() error {
|
||||
|
||||
func Run() error {
|
||||
|
||||
fmt.Printf(constant.Banner, constant.Version)
|
||||
fmt.Printf(constant.AppBanner, constant.AppVersion)
|
||||
|
||||
if err := app.InitDBData(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := app.ReloadData(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.Server = setupRoutes()
|
||||
|
||||
if config.GlobalCfg.Debug {
|
||||
jsonBytes, err := json.MarshalIndent(config.GlobalCfg, "", " ")
|
||||
|
@ -5,8 +5,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "v1.2.5"
|
||||
Banner = `
|
||||
AppVersion = "v1.2.5"
|
||||
AppName = "Next Terminal"
|
||||
AppBanner = `
|
||||
_______ __ ___________ .__ .__
|
||||
\ \ ____ ___ ____/ |_ \__ ___/__________ _____ |__| ____ _____ | |
|
||||
/ | \_/ __ \\ \/ /\ __\ | |_/ __ \_ __ \/ \| |/ \\__ \ | |
|
||||
@ -72,8 +73,9 @@ const (
|
||||
SocksProxyUsername = "socks-proxy-username"
|
||||
SocksProxyPassword = "socks-proxy-password"
|
||||
|
||||
LoginToken = "login-token"
|
||||
AccessToken = "access-token"
|
||||
LoginToken = "login-token"
|
||||
AccessToken = "access-token"
|
||||
ShareSession = "share-session"
|
||||
|
||||
Anonymous = "anonymous"
|
||||
)
|
||||
|
6
server/env/db.go
vendored
6
server/env/db.go
vendored
@ -36,8 +36,10 @@ func setupDB() *gorm.DB {
|
||||
Logger: logMode,
|
||||
})
|
||||
} else {
|
||||
db, err = gorm.Open(sqlite.Open(config.GlobalCfg.Sqlite.File), &gorm.Config{
|
||||
Logger: logMode,
|
||||
dsn := fmt.Sprintf("file:%s?cache=shared&mode=rwc", config.GlobalCfg.Sqlite.File)
|
||||
db, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{
|
||||
Logger: logMode,
|
||||
SkipDefaultTransaction: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -13,14 +13,9 @@ type baseRepository struct {
|
||||
}
|
||||
|
||||
func (b *baseRepository) GetDB(c context.Context) *gorm.DB {
|
||||
db := c.Value(constant.DB)
|
||||
if db == nil {
|
||||
return env.GetDB()
|
||||
}
|
||||
switch val := db.(type) {
|
||||
case gorm.DB:
|
||||
return &val
|
||||
default:
|
||||
db, ok := c.Value(constant.DB).(*gorm.DB)
|
||||
if !ok {
|
||||
return env.GetDB()
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -101,57 +100,8 @@ func (r *resourceSharerRepository) DeleteByUserGroupId(c context.Context, userGr
|
||||
return r.GetDB(c).Where("user_group_id = ?", userGroupId).Delete(&model.ResourceSharer{}).Error
|
||||
}
|
||||
|
||||
func (r *resourceSharerRepository) AddSharerResources(userGroupId, userId, strategyId, resourceType string, resourceIds []string) error {
|
||||
return r.GetDB(context.TODO()).Transaction(func(tx *gorm.DB) (err error) {
|
||||
|
||||
for i := range resourceIds {
|
||||
resourceId := resourceIds[i]
|
||||
|
||||
var owner string
|
||||
// 检查资产是否存在
|
||||
switch resourceType {
|
||||
case "asset":
|
||||
resource := model.Asset{}
|
||||
if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil {
|
||||
return errors.Wrap(err, "find asset fail")
|
||||
}
|
||||
owner = resource.Owner
|
||||
case "command":
|
||||
resource := model.Command{}
|
||||
if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil {
|
||||
return errors.Wrap(err, "find command fail")
|
||||
}
|
||||
owner = resource.Owner
|
||||
case "credential":
|
||||
resource := model.Credential{}
|
||||
if err = tx.Where("id = ?", resourceId).First(&resource).Error; err != nil {
|
||||
return errors.Wrap(err, "find credential fail")
|
||||
|
||||
}
|
||||
owner = resource.Owner
|
||||
}
|
||||
|
||||
if owner == userId {
|
||||
return echo.NewHTTPError(400, "参数错误")
|
||||
}
|
||||
|
||||
// 保证同一个资产只能分配给一个用户或者组
|
||||
id := utils.Sign([]string{resourceId, resourceType, userId, userGroupId})
|
||||
resource := &model.ResourceSharer{
|
||||
ID: id,
|
||||
ResourceId: resourceId,
|
||||
ResourceType: resourceType,
|
||||
StrategyId: strategyId,
|
||||
UserId: userId,
|
||||
UserGroupId: userGroupId,
|
||||
}
|
||||
err = tx.Create(resource).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
func (r *resourceSharerRepository) AddSharerResource(c context.Context, m *model.ResourceSharer) error {
|
||||
return r.GetDB(c).Create(m).Error
|
||||
}
|
||||
|
||||
func (r *resourceSharerRepository) FindAssetIdsByUserId(c context.Context, userId string) (assetIds []string, err error) {
|
||||
@ -228,3 +178,7 @@ func (r *resourceSharerRepository) FindAll(c context.Context) (o []model.Resourc
|
||||
err = r.GetDB(c).Find(&o).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (r *resourceSharerRepository) DeleteById(ctx context.Context, id string) error {
|
||||
return r.GetDB(ctx).Where("id = ?", id).Delete(&model.ResourceSharer{}).Error
|
||||
}
|
||||
|
@ -19,14 +19,9 @@ type accessTokenService struct {
|
||||
baseService
|
||||
}
|
||||
|
||||
func (service accessTokenService) FindByUserId(userId string) (model.AccessToken, error) {
|
||||
return repository.AccessTokenRepository.FindByUserId(context.TODO(), userId)
|
||||
}
|
||||
|
||||
func (service accessTokenService) GenAccessToken(userId string) error {
|
||||
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
ctx := service.Context(tx)
|
||||
|
||||
user, err := repository.UserRepository.FindById(ctx, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -119,7 +119,7 @@ func (s assetService) FindByIdAndDecrypt(c context.Context, id string) (model.As
|
||||
func (s assetService) CheckStatus(accessGatewayId string, ip string, port int) (active bool, err error) {
|
||||
if accessGatewayId != "" && accessGatewayId != "-" {
|
||||
g, e1 := GatewayService.GetGatewayAndReconnectById(accessGatewayId)
|
||||
if err != nil {
|
||||
if e1 != nil {
|
||||
return false, e1
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ func (s assetService) CheckStatus(accessGatewayId string, ip string, port int) (
|
||||
return active, err
|
||||
}
|
||||
|
||||
func (s assetService) Create(m echo.Map) (model.Asset, error) {
|
||||
func (s assetService) Create(ctx context.Context, m echo.Map) (model.Asset, error) {
|
||||
|
||||
data, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
@ -156,29 +156,36 @@ func (s assetService) Create(m echo.Map) (model.Asset, error) {
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Active = true
|
||||
|
||||
return item, env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := s.Context(tx)
|
||||
if s.InTransaction(ctx) {
|
||||
return item, s.create(ctx, item, m)
|
||||
} else {
|
||||
return item, env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := s.Context(tx)
|
||||
return s.create(c, item, m)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Encrypt(&item, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := repository.AssetRepository.Create(c, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
func (s assetService) create(c context.Context, item model.Asset, m echo.Map) error {
|
||||
if err := s.Encrypt(&item, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := repository.AssetRepository.Create(c, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repository.AssetRepository.UpdateAttributes(c, item.ID, item.Protocol, m); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := repository.AssetRepository.UpdateAttributes(c, item.ID, item.Protocol, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
active, _ := s.CheckStatus(item.AccessGatewayId, item.IP, item.Port)
|
||||
|
||||
if item.Active != active {
|
||||
_ = repository.AssetRepository.UpdateActiveById(context.TODO(), active, item.ID)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
})
|
||||
//go func() {
|
||||
// active, _ := s.CheckStatus(item.AccessGatewayId, item.IP, item.Port)
|
||||
//
|
||||
// if item.Active != active {
|
||||
// _ = repository.AssetRepository.UpdateActiveById(context.TODO(), active, item.ID)
|
||||
// }
|
||||
//}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s assetService) DeleteById(id string) error {
|
||||
|
@ -128,12 +128,12 @@ func (service backupService) Export() (error, *dto.Backup) {
|
||||
|
||||
func (service backupService) Import(backup *dto.Backup) error {
|
||||
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := service.Context(tx)
|
||||
ctx := service.Context(tx)
|
||||
var userIdMapping = make(map[string]string)
|
||||
if len(backup.Users) > 0 {
|
||||
for _, item := range backup.Users {
|
||||
oldId := item.ID
|
||||
exist, err := repository.UserRepository.ExistByUsername(c, item.Username)
|
||||
exist, err := repository.UserRepository.ExistByUsername(ctx, item.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -144,7 +144,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
newId := utils.UUID()
|
||||
item.ID = newId
|
||||
item.Password = utils.GenPassword()
|
||||
if err := repository.UserRepository.Create(c, &item); err != nil {
|
||||
if err := repository.UserRepository.Create(ctx, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
userIdMapping[oldId] = newId
|
||||
@ -163,7 +163,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
}
|
||||
}
|
||||
|
||||
userGroup, err := UserGroupService.Create(item.Name, members)
|
||||
userGroup, err := UserGroupService.Create(ctx, item.Name, members)
|
||||
if err != nil {
|
||||
if errors.Is(constant.ErrNameAlreadyUsed, err) {
|
||||
// 删除名称重复的用户组
|
||||
@ -187,7 +187,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
item.ID = utils.UUID()
|
||||
item.Owner = owner
|
||||
item.Created = utils.NowJsonTime()
|
||||
if err := repository.StorageRepository.Create(c, &item); err != nil {
|
||||
if err := repository.StorageRepository.Create(ctx, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -200,7 +200,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
newId := utils.UUID()
|
||||
item.ID = newId
|
||||
item.Created = utils.NowJsonTime()
|
||||
if err := repository.StrategyRepository.Create(c, &item); err != nil {
|
||||
if err := repository.StrategyRepository.Create(ctx, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
strategyIdMapping[oldId] = newId
|
||||
@ -210,7 +210,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
if len(backup.AccessSecurities) > 0 {
|
||||
for _, item := range backup.AccessSecurities {
|
||||
item.ID = utils.UUID()
|
||||
if err := repository.SecurityRepository.Create(c, &item); err != nil {
|
||||
if err := repository.SecurityRepository.Create(ctx, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
// 更新内存中的安全规则
|
||||
@ -231,7 +231,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
newId := utils.UUID()
|
||||
item.ID = newId
|
||||
item.Created = utils.NowJsonTime()
|
||||
if err := repository.GatewayRepository.Create(c, &item); err != nil {
|
||||
if err := repository.GatewayRepository.Create(ctx, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
accessGatewayIdMapping[oldId] = newId
|
||||
@ -242,7 +242,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
for _, item := range backup.Commands {
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
if err := repository.CommandRepository.Create(c, &item); err != nil {
|
||||
if err := repository.CommandRepository.Create(ctx, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -254,7 +254,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
oldId := item.ID
|
||||
newId := utils.UUID()
|
||||
item.ID = newId
|
||||
if err := CredentialService.Create(&item); err != nil {
|
||||
if err := CredentialService.Create(ctx, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
credentialIdMapping[oldId] = newId
|
||||
@ -282,7 +282,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
}
|
||||
|
||||
oldId := m["id"].(string)
|
||||
asset, err := AssetService.Create(m)
|
||||
asset, err := AssetService.Create(ctx, m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -299,7 +299,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
strategyId := strategyIdMapping[item.StrategyId]
|
||||
resourceId := assetIdMapping[item.ResourceId]
|
||||
|
||||
if err := repository.ResourceSharerRepository.AddSharerResources(userGroupId, userId, strategyId, item.ResourceType, []string{resourceId}); err != nil {
|
||||
if err := UserService.AddSharerResources(ctx, userGroupId, userId, strategyId, item.ResourceType, []string{resourceId}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -311,6 +311,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
continue
|
||||
}
|
||||
|
||||
item.ID = utils.UUID()
|
||||
resourceIds := strings.Split(item.ResourceIds, ",")
|
||||
if len(resourceIds) > 0 {
|
||||
var newResourceIds = make([]string, 0)
|
||||
@ -319,7 +320,7 @@ func (service backupService) Import(backup *dto.Backup) error {
|
||||
}
|
||||
item.ResourceIds = strings.Join(newResourceIds, ",")
|
||||
}
|
||||
if err := JobService.Create(&item); err != nil {
|
||||
if err := JobService.Create(ctx, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -14,3 +14,8 @@ type baseService struct {
|
||||
func (service baseService) Context(db *gorm.DB) context.Context {
|
||||
return context.WithValue(context.TODO(), constant.DB, db)
|
||||
}
|
||||
|
||||
func (service baseService) InTransaction(ctx context.Context) bool {
|
||||
_, ok := ctx.Value(constant.DB).(*gorm.DB)
|
||||
return ok
|
||||
}
|
||||
|
@ -99,8 +99,8 @@ func (s credentialService) Decrypt(item *model.Credential, password []byte) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s credentialService) FindByIdAndDecrypt(c context.Context, id string) (o model.Credential, err error) {
|
||||
credential, err := repository.CredentialRepository.FindById(c, id)
|
||||
func (s credentialService) FindByIdAndDecrypt(ctx context.Context, id string) (o model.Credential, err error) {
|
||||
credential, err := repository.CredentialRepository.FindById(ctx, id)
|
||||
if err != nil {
|
||||
return o, err
|
||||
}
|
||||
@ -110,10 +110,10 @@ func (s credentialService) FindByIdAndDecrypt(c context.Context, id string) (o m
|
||||
return credential, nil
|
||||
}
|
||||
|
||||
func (s credentialService) Create(item *model.Credential) error {
|
||||
func (s credentialService) Create(ctx context.Context, item *model.Credential) error {
|
||||
// 加密密码之后进行存储
|
||||
if err := s.Encrypt(item, config.GlobalCfg.EncryptionPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
return repository.CredentialRepository.Create(context.TODO(), item)
|
||||
return repository.CredentialRepository.Create(ctx, item)
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ func (r jobService) InitJob() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r jobService) Create(o *model.Job) (err error) {
|
||||
func (r jobService) Create(ctx context.Context, o *model.Job) (err error) {
|
||||
|
||||
if o.Status == constant.JobStatusRunning {
|
||||
j, err := getJob(o)
|
||||
@ -117,7 +117,7 @@ func (r jobService) Create(o *model.Job) (err error) {
|
||||
o.CronJobId = int(jobId)
|
||||
}
|
||||
|
||||
return repository.JobRepository.Create(context.TODO(), o)
|
||||
return repository.JobRepository.Create(ctx, o)
|
||||
}
|
||||
|
||||
func (r jobService) DeleteJobById(id string) error {
|
||||
|
@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
@ -27,7 +28,7 @@ func (r mailService) SendMail(to, subject, text string) {
|
||||
}
|
||||
|
||||
e := email.NewEmail()
|
||||
e.From = "Next Terminal <" + username + ">"
|
||||
e.From = fmt.Sprintf("%s <%s>", constant.AppName, username)
|
||||
e.To = []string{to}
|
||||
e.Subject = subject
|
||||
e.Text = []byte(text)
|
||||
|
@ -17,161 +17,63 @@ type propertyService struct {
|
||||
baseService
|
||||
}
|
||||
|
||||
var deprecatedPropertyNames = []string{
|
||||
guacd.EnableDrive,
|
||||
guacd.DrivePath,
|
||||
guacd.DriveName,
|
||||
guacd.DisableGlyphCaching,
|
||||
guacd.CreateRecordingPath,
|
||||
}
|
||||
|
||||
var defaultProperties = map[string]string{
|
||||
guacd.EnableRecording: "true",
|
||||
guacd.FontName: "menlo",
|
||||
guacd.FontSize: "12",
|
||||
guacd.ColorScheme: "gray-black",
|
||||
guacd.EnableWallpaper: "true",
|
||||
guacd.EnableTheming: "true",
|
||||
guacd.EnableFontSmoothing: "true",
|
||||
guacd.EnableFullWindowDrag: "true",
|
||||
guacd.EnableDesktopComposition: "true",
|
||||
guacd.EnableMenuAnimations: "true",
|
||||
guacd.DisableBitmapCaching: "false",
|
||||
guacd.DisableOffscreenCaching: "false",
|
||||
"cron-log-saved-limit": "360",
|
||||
"login-log-saved-limit": "360",
|
||||
"session-saved-limit": "360",
|
||||
"user-default-storage-size": "5120",
|
||||
}
|
||||
|
||||
func (service propertyService) InitProperties() error {
|
||||
propertyMap := repository.PropertyRepository.FindAllMap(context.TODO())
|
||||
|
||||
if len(propertyMap[guacd.EnableRecording]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.EnableRecording,
|
||||
Value: "true",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
for name, value := range defaultProperties {
|
||||
if err := service.CreateIfAbsent(propertyMap, name, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.CreateRecordingPath]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.CreateRecordingPath,
|
||||
Value: "true",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.FontName]) == 0 {
|
||||
func (service propertyService) CreateIfAbsent(propertyMap map[string]string, name, value string) error {
|
||||
if len(propertyMap[name]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.FontName,
|
||||
Value: "menlo",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.FontSize]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.FontSize,
|
||||
Value: "12",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.ColorScheme]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.ColorScheme,
|
||||
Value: "gray-black",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.EnableWallpaper]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.EnableWallpaper,
|
||||
Value: "false",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.EnableTheming]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.EnableTheming,
|
||||
Value: "false",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.EnableFontSmoothing]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.EnableFontSmoothing,
|
||||
Value: "false",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.EnableFullWindowDrag]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.EnableFullWindowDrag,
|
||||
Value: "false",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.EnableDesktopComposition]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.EnableDesktopComposition,
|
||||
Value: "false",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.EnableMenuAnimations]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.EnableMenuAnimations,
|
||||
Value: "false",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.DisableBitmapCaching]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.DisableBitmapCaching,
|
||||
Value: "false",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.DisableOffscreenCaching]) == 0 {
|
||||
property := model.Property{
|
||||
Name: guacd.DisableOffscreenCaching,
|
||||
Value: "false",
|
||||
}
|
||||
if err := repository.PropertyRepository.Create(context.TODO(), &property); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(propertyMap[guacd.DisableGlyphCaching]) > 0 {
|
||||
if err := repository.PropertyRepository.DeleteByName(context.TODO(), guacd.DisableGlyphCaching); err != nil {
|
||||
return err
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
return repository.PropertyRepository.Create(context.TODO(), &property)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service propertyService) DeleteDeprecatedProperty() error {
|
||||
propertyMap := repository.PropertyRepository.FindAllMap(context.TODO())
|
||||
if propertyMap[guacd.EnableDrive] != "" {
|
||||
if err := repository.PropertyRepository.DeleteByName(context.TODO(), guacd.DriveName); err != nil {
|
||||
return err
|
||||
for _, name := range deprecatedPropertyNames {
|
||||
if propertyMap[name] == "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if propertyMap[guacd.DrivePath] != "" {
|
||||
if err := repository.PropertyRepository.DeleteByName(context.TODO(), guacd.DrivePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if propertyMap[guacd.DriveName] != "" {
|
||||
if err := repository.PropertyRepository.DeleteByName(context.TODO(), guacd.DriveName); err != nil {
|
||||
if err := repository.PropertyRepository.DeleteByName(context.TODO(), name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,14 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/config"
|
||||
@ -33,7 +36,7 @@ func (service storageService) InitStorages() error {
|
||||
userId := users[i].ID
|
||||
_, err := repository.StorageRepository.FindByOwnerIdAndDefault(context.TODO(), userId, true)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = service.CreateStorageByUser(&users[i])
|
||||
err = service.CreateStorageByUser(context.TODO(), &users[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -58,7 +61,7 @@ func (service storageService) InitStorages() error {
|
||||
}
|
||||
|
||||
if !userExist {
|
||||
if err := service.DeleteStorageById(storage.ID, true); err != nil {
|
||||
if err := service.DeleteStorageById(context.TODO(), storage.ID, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -75,14 +78,29 @@ func (service storageService) InitStorages() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service storageService) CreateStorageByUser(user *model.User) error {
|
||||
func (service storageService) CreateStorageByUser(c context.Context, user *model.User) error {
|
||||
drivePath := service.GetBaseDrivePath()
|
||||
var limitSize int64 = -1
|
||||
property, err := repository.PropertyRepository.FindByName(c, "user-default-storage-size")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
limitSize, err = strconv.ParseInt(property.Value, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
limitSize = limitSize * 1024 * 1024
|
||||
if limitSize < 0 {
|
||||
limitSize = -1
|
||||
}
|
||||
|
||||
storage := model.Storage{
|
||||
ID: user.ID,
|
||||
Name: user.Nickname + "的默认空间",
|
||||
IsShare: false,
|
||||
IsDefault: true,
|
||||
LimitSize: -1,
|
||||
LimitSize: limitSize,
|
||||
Owner: user.ID,
|
||||
Created: utils.NowJsonTime(),
|
||||
}
|
||||
@ -91,8 +109,9 @@ func (service storageService) CreateStorageByUser(user *model.User) error {
|
||||
return err
|
||||
}
|
||||
log.Infof("创建storage:「%v」文件夹: %v", storage.Name, storageDir)
|
||||
err := repository.StorageRepository.Create(context.TODO(), &storage)
|
||||
err = repository.StorageRepository.Create(c, &storage)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(storageDir)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -135,9 +154,9 @@ func (service storageService) GetBaseDrivePath() string {
|
||||
return config.GlobalCfg.Guacd.Drive
|
||||
}
|
||||
|
||||
func (service storageService) DeleteStorageById(id string, force bool) error {
|
||||
func (service storageService) DeleteStorageById(c context.Context, id string, force bool) error {
|
||||
drivePath := service.GetBaseDrivePath()
|
||||
storage, err := repository.StorageRepository.FindById(context.TODO(), id)
|
||||
storage, err := repository.StorageRepository.FindById(c, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil
|
||||
@ -152,7 +171,7 @@ func (service storageService) DeleteStorageById(id string, force bool) error {
|
||||
if err := os.RemoveAll(path.Join(drivePath, id)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := repository.StorageRepository.DeleteById(context.TODO(), id); err != nil {
|
||||
if err := repository.StorageRepository.DeleteById(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -229,14 +248,20 @@ func (service storageService) StorageEdit(file string, fileContent string, stora
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service storageService) StorageDownload(c echo.Context, remoteFile, storageId string) error {
|
||||
func (service storageService) StorageDownload(c echo.Context, file, storageId string) error {
|
||||
drivePath := service.GetBaseDrivePath()
|
||||
if strings.Contains(remoteFile, "../") {
|
||||
if strings.Contains(file, "../") {
|
||||
return errors.New("非法请求 :(")
|
||||
}
|
||||
// 获取带后缀的文件名称
|
||||
filenameWithSuffix := path.Base(remoteFile)
|
||||
return c.Attachment(path.Join(path.Join(drivePath, storageId), remoteFile), filenameWithSuffix)
|
||||
filenameWithSuffix := path.Base(file)
|
||||
p := path.Join(path.Join(drivePath, storageId), file)
|
||||
//log.Infof("download %v", p)
|
||||
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filenameWithSuffix))
|
||||
c.Response().Header().Set("Content-Type", "application/octet-stream")
|
||||
|
||||
http.ServeFile(c.Response(), c.Request(), p)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service storageService) StorageLs(remoteDir, storageId string) (error, []File) {
|
||||
|
@ -90,31 +90,31 @@ func (service userService) FixUserOnlineState() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service userService) Logout(token string) {
|
||||
cache.TokenManager.Delete(token)
|
||||
}
|
||||
|
||||
func (service userService) LogoutByToken(token string) (err error) {
|
||||
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := service.Context(tx)
|
||||
loginLog, err := repository.LoginLogRepository.FindById(c, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.TokenManager.Delete(token)
|
||||
|
||||
loginLogForUpdate := &model.LoginLog{LogoutTime: utils.NowJsonTime(), ID: token}
|
||||
err = repository.LoginLogRepository.Update(c, loginLogForUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(c, loginLog.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loginLogs) == 0 {
|
||||
err = repository.UserRepository.UpdateOnlineByUsername(c, loginLog.Username, false)
|
||||
}
|
||||
loginLog, err := repository.LoginLogRepository.FindById(context.TODO(), token)
|
||||
if err != nil {
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
loginLogForUpdate := &model.LoginLog{LogoutTime: utils.NowJsonTime(), ID: token}
|
||||
err = repository.LoginLogRepository.Update(context.TODO(), loginLogForUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(context.TODO(), loginLog.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loginLogs) == 0 {
|
||||
err = repository.UserRepository.UpdateOnlineByUsername(context.TODO(), loginLog.Username, false)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (service userService) LogoutById(c context.Context, id string) error {
|
||||
@ -130,13 +130,26 @@ func (service userService) LogoutById(c context.Context, id string) error {
|
||||
|
||||
for j := range loginLogs {
|
||||
token := loginLogs[j].ID
|
||||
if err := service.LogoutByToken(token); err != nil {
|
||||
return err
|
||||
}
|
||||
service.Logout(token)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service userService) GetUserLoginToken(c context.Context, username string) ([]string, error) {
|
||||
|
||||
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(c, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var tokens []string
|
||||
for j := range loginLogs {
|
||||
token := loginLogs[j].ID
|
||||
tokens = append(tokens, token)
|
||||
}
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func (service userService) OnEvicted(token string, value interface{}) {
|
||||
|
||||
if strings.HasPrefix(token, "forever") {
|
||||
@ -144,30 +157,24 @@ func (service userService) OnEvicted(token string, value interface{}) {
|
||||
} else {
|
||||
log.Debugf("用户Token「%v」过期", token)
|
||||
err := service.LogoutByToken(token)
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(gorm.ErrRecordNotFound, err) {
|
||||
log.Errorf("退出登录失败 %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (service userService) UpdateStatusById(id string, status string) error {
|
||||
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := service.Context(tx)
|
||||
if c.Value(constant.DB) == nil {
|
||||
c = context.WithValue(c, constant.DB, env.GetDB())
|
||||
if constant.StatusDisabled == status {
|
||||
// 将该用户下线
|
||||
if err := service.LogoutById(context.TODO(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
if constant.StatusDisabled == status {
|
||||
// 将该用户下线
|
||||
if err := service.LogoutById(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
u := model.User{
|
||||
ID: id,
|
||||
Status: status,
|
||||
}
|
||||
return repository.UserRepository.Update(c, &u)
|
||||
})
|
||||
}
|
||||
u := model.User{
|
||||
ID: id,
|
||||
Status: status,
|
||||
}
|
||||
return repository.UserRepository.Update(context.TODO(), &u)
|
||||
|
||||
}
|
||||
|
||||
@ -231,13 +238,19 @@ func (service userService) CreateUser(user model.User) (err error) {
|
||||
if err := repository.UserRepository.Create(c, &user); err != nil {
|
||||
return err
|
||||
}
|
||||
err = StorageService.CreateStorageByUser(&user)
|
||||
err = StorageService.CreateStorageByUser(c, &user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.Mail != "" {
|
||||
go MailService.SendMail(user.Mail, "[Next Terminal] 注册通知", "你好,"+user.Nickname+"。管理员为你注册了账号:"+user.Username+" 密码:"+password)
|
||||
subject := fmt.Sprintf("%s 注册通知", constant.AppName)
|
||||
text := fmt.Sprintf(`您好,%s。
|
||||
管理员为你开通了账户。
|
||||
账号:%s
|
||||
密码:%s
|
||||
`, user.Username, user.Username, password)
|
||||
go MailService.SendMail(user.Mail, subject, text)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@ -245,16 +258,19 @@ func (service userService) CreateUser(user model.User) (err error) {
|
||||
}
|
||||
|
||||
func (service userService) DeleteUserById(userId string) error {
|
||||
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
user, err := repository.UserRepository.FindById(context.TODO(), userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
username := user.Username
|
||||
// 下线该用户
|
||||
loginTokens, err := service.GetUserLoginToken(context.TODO(), username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := service.Context(tx)
|
||||
// 下线该用户
|
||||
if err := service.LogoutById(c, userId); err != nil {
|
||||
return err
|
||||
}
|
||||
// 删除用户
|
||||
if err := repository.UserRepository.DeleteById(c, userId); err != nil {
|
||||
return err
|
||||
}
|
||||
// 删除用户与用户组的关系
|
||||
if err := repository.UserGroupMemberRepository.DeleteByUserId(c, userId); err != nil {
|
||||
return err
|
||||
@ -264,19 +280,37 @@ func (service userService) DeleteUserById(userId string) error {
|
||||
return err
|
||||
}
|
||||
// 删除用户的默认磁盘空间
|
||||
if err := StorageService.DeleteStorageById(userId, true); err != nil {
|
||||
if err := StorageService.DeleteStorageById(c, userId, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
if err := repository.UserRepository.DeleteById(c, userId); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, token := range loginTokens {
|
||||
service.Logout(token)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service userService) DeleteLoginLogs(tokens []string) error {
|
||||
if len(tokens) > 0 {
|
||||
for _, token := range tokens {
|
||||
// 手动触发用户退出登录
|
||||
if err := service.LogoutByToken(token); err != nil {
|
||||
return err
|
||||
}
|
||||
// 移除缓存中的token
|
||||
service.Logout(token)
|
||||
// 删除登录日志
|
||||
if err := repository.LoginLogRepository.DeleteById(context.TODO(), token); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -337,3 +371,37 @@ func (service userService) UpdateUser(id string, user model.User) error {
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (service userService) AddSharerResources(ctx context.Context, userGroupId, userId, strategyId, resourceType string, resourceIds []string) error {
|
||||
if service.InTransaction(ctx) {
|
||||
return service.addSharerResources(ctx, resourceIds, userGroupId, userId, strategyId, resourceType)
|
||||
} else {
|
||||
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
ctx2 := service.Context(tx)
|
||||
return service.addSharerResources(ctx2, resourceIds, userGroupId, userId, strategyId, resourceType)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (service userService) addSharerResources(ctx context.Context, resourceIds []string, userGroupId string, userId string, strategyId string, resourceType string) error {
|
||||
for i := range resourceIds {
|
||||
resourceId := resourceIds[i]
|
||||
// 保证同一个资产只能分配给一个用户或者组
|
||||
id := utils.Sign([]string{resourceId, resourceType, userId, userGroupId})
|
||||
if err := repository.ResourceSharerRepository.DeleteById(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
rs := &model.ResourceSharer{
|
||||
ID: id,
|
||||
ResourceId: resourceId,
|
||||
ResourceType: resourceType,
|
||||
StrategyId: strategyId,
|
||||
UserId: userId,
|
||||
UserGroupId: userGroupId,
|
||||
}
|
||||
if err := repository.ResourceSharerRepository.AddSharerResource(ctx, rs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -13,11 +13,12 @@ import (
|
||||
)
|
||||
|
||||
type userGroupService struct {
|
||||
baseService
|
||||
}
|
||||
|
||||
func (service userGroupService) DeleteById(userGroupId string) error {
|
||||
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := context.WithValue(context.TODO(), constant.DB, tx)
|
||||
c := service.Context(tx)
|
||||
// 删除用户组
|
||||
if err := repository.UserGroupRepository.DeleteById(c, userGroupId); err != nil {
|
||||
return err
|
||||
@ -34,8 +35,8 @@ func (service userGroupService) DeleteById(userGroupId string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (service userGroupService) Create(name string, members []string) (model.UserGroup, error) {
|
||||
exist, err := repository.UserGroupRepository.ExistByName(context.TODO(), name)
|
||||
func (service userGroupService) Create(ctx context.Context, name string, members []string) (model.UserGroup, error) {
|
||||
exist, err := repository.UserGroupRepository.ExistByName(ctx, name)
|
||||
if err != nil {
|
||||
return model.UserGroup{}, err
|
||||
}
|
||||
@ -51,26 +52,33 @@ func (service userGroupService) Create(name string, members []string) (model.Use
|
||||
Name: name,
|
||||
}
|
||||
|
||||
return userGroup, env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := context.WithValue(context.TODO(), constant.DB, tx)
|
||||
if err := repository.UserGroupRepository.Create(c, &userGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(members) > 0 {
|
||||
for _, member := range members {
|
||||
userGroupMember := model.UserGroupMember{
|
||||
ID: utils.Sign([]string{userGroupId, member}),
|
||||
UserId: member,
|
||||
UserGroupId: userGroupId,
|
||||
}
|
||||
if err := repository.UserGroupMemberRepository.Create(c, &userGroupMember); err != nil {
|
||||
return err
|
||||
}
|
||||
if service.InTransaction(ctx) {
|
||||
return userGroup, service.create(ctx, userGroup, members, userGroupId)
|
||||
} else {
|
||||
return userGroup, env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := service.Context(tx)
|
||||
return service.create(c, userGroup, members, userGroupId)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (service userGroupService) create(c context.Context, userGroup model.UserGroup, members []string, userGroupId string) error {
|
||||
if err := repository.UserGroupRepository.Create(c, &userGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(members) > 0 {
|
||||
for _, member := range members {
|
||||
userGroupMember := model.UserGroupMember{
|
||||
ID: utils.Sign([]string{userGroupId, member}),
|
||||
UserId: member,
|
||||
UserGroupId: userGroupId,
|
||||
}
|
||||
if err := repository.UserGroupMemberRepository.Create(c, &userGroupMember); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service userGroupService) Update(userGroupId string, name string, members []string) (err error) {
|
||||
@ -91,7 +99,7 @@ func (service userGroupService) Update(userGroupId string, name string, members
|
||||
}
|
||||
|
||||
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
c := context.WithValue(context.TODO(), constant.DB, tx)
|
||||
c := service.Context(tx)
|
||||
userGroup := model.UserGroup{
|
||||
ID: userGroupId,
|
||||
Name: name,
|
||||
|
@ -131,7 +131,7 @@ func (sshd sshd) sessionHandler(sess *ssh.Session) {
|
||||
|
||||
func (sshd sshd) Serve() {
|
||||
ssh.Handle(func(s ssh.Session) {
|
||||
_, _ = io.WriteString(s, fmt.Sprintf(constant.Banner, constant.Version))
|
||||
_, _ = io.WriteString(s, fmt.Sprintf(constant.AppBanner, constant.AppVersion))
|
||||
sshd.sessionHandler(&s)
|
||||
})
|
||||
|
||||
|
@ -59,19 +59,22 @@ func (gui Gui) AssetUI(sess *ssh.Session, user model.User) {
|
||||
return
|
||||
}
|
||||
|
||||
for i := range assets {
|
||||
assets[i].IP = ""
|
||||
assets[i].Port = 0
|
||||
}
|
||||
|
||||
quitItem := model.Asset{ID: "quit", Name: "返回上级菜单", Description: "这里是返回上级菜单的选项"}
|
||||
assets = append([]model.Asset{quitItem}, assets...)
|
||||
|
||||
templates := &promptui.SelectTemplates{
|
||||
Label: "{{ . }}?",
|
||||
Active: "\U0001F336 {{ .Name | cyan }} ({{ .IP | red }}:{{ .Port | red }})",
|
||||
Inactive: " {{ .Name | cyan }} ({{ .IP | red }}:{{ .Port | red }})",
|
||||
Active: "\U0001F336 {{ .Name | cyan }}",
|
||||
Inactive: " {{ .Name | cyan }}",
|
||||
Selected: "\U0001F336 {{ .Name | red | cyan }}",
|
||||
Details: `
|
||||
--------- 详细信息 ----------
|
||||
{{ "名称:" | faint }} {{ .Name }}
|
||||
{{ "主机:" | faint }} {{ .IP }}
|
||||
{{ "端口:" | faint }} {{ .Port }}
|
||||
{{ "标签:" | faint }} {{ .Tags }}
|
||||
{{ "备注:" | faint }} {{ .Description }}
|
||||
`,
|
||||
|
@ -18,7 +18,6 @@
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "^4.0.0",
|
||||
"react-tsparticles": "^1.37.5",
|
||||
"xterm": "^4.9.0",
|
||||
"xterm-addon-fit": "^0.4.0",
|
||||
"xterm-addon-web-links": "^0.4.0"
|
||||
|
@ -5,8 +5,7 @@ import request from "../common/request";
|
||||
import {message} from "antd/es";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import {LockOutlined, OneToOneOutlined, UserOutlined} from '@ant-design/icons';
|
||||
import Particles from "react-tsparticles";
|
||||
import Background from '../images/bg.png'
|
||||
import Background from '../images/bg.jpg'
|
||||
import {setToken} from "../utils/utils";
|
||||
|
||||
const {Title} = Typography;
|
||||
@ -109,90 +108,7 @@ class LoginForm extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className='login-bg'
|
||||
style={{width: this.state.width, height: this.state.height}}>
|
||||
<Particles
|
||||
id="tsparticles"
|
||||
options={{
|
||||
background: {
|
||||
color: {
|
||||
// value: "#0d47a1",
|
||||
},
|
||||
image: `url(${Background})`,
|
||||
repeat: 'no-repeat',
|
||||
size: '100% 100%'
|
||||
},
|
||||
fpsLimit: 60,
|
||||
interactivity: {
|
||||
events: {
|
||||
onClick: {
|
||||
enable: true,
|
||||
mode: "push",
|
||||
},
|
||||
onHover: {
|
||||
enable: true,
|
||||
mode: "repulse",
|
||||
},
|
||||
resize: true,
|
||||
},
|
||||
modes: {
|
||||
bubble: {
|
||||
distance: 400,
|
||||
duration: 2,
|
||||
opacity: 0.8,
|
||||
size: 40,
|
||||
},
|
||||
push: {
|
||||
quantity: 4,
|
||||
},
|
||||
repulse: {
|
||||
distance: 200,
|
||||
duration: 0.4,
|
||||
},
|
||||
},
|
||||
},
|
||||
particles: {
|
||||
color: {
|
||||
value: "#ffffff",
|
||||
},
|
||||
links: {
|
||||
color: "#ffffff",
|
||||
distance: 150,
|
||||
enable: true,
|
||||
opacity: 0.5,
|
||||
width: 1,
|
||||
},
|
||||
collisions: {
|
||||
enable: true,
|
||||
},
|
||||
move: {
|
||||
direction: "none",
|
||||
enable: true,
|
||||
outMode: "bounce",
|
||||
random: false,
|
||||
speed: 3,
|
||||
straight: false,
|
||||
},
|
||||
number: {
|
||||
density: {
|
||||
enable: true,
|
||||
value_area: 800,
|
||||
},
|
||||
value: 80,
|
||||
},
|
||||
opacity: {
|
||||
value: 0.5,
|
||||
},
|
||||
shape: {
|
||||
type: "circle",
|
||||
},
|
||||
size: {
|
||||
random: true,
|
||||
value: 5,
|
||||
},
|
||||
},
|
||||
detectRetina: true,
|
||||
}}
|
||||
/>
|
||||
style={{width: this.state.width, height: this.state.height, background: `url(${Background})`}}>
|
||||
<Card className='login-card' title={null}>
|
||||
<div style={{textAlign: "center", margin: '15px auto 30px auto', color: '#1890ff'}}>
|
||||
<Title level={1}>Next Terminal</Title>
|
||||
|
@ -127,7 +127,7 @@ class Access extends Component {
|
||||
|
||||
sendClipboard(data) {
|
||||
if (this.state.session['paste'] === '0') {
|
||||
message.warn('禁止粘贴');
|
||||
// message.warn('禁止粘贴');
|
||||
return
|
||||
}
|
||||
let writer;
|
||||
@ -338,7 +338,7 @@ class Access extends Component {
|
||||
|
||||
clientClipboardReceived = (stream, mimetype) => {
|
||||
if (this.state.session['copy'] === '0') {
|
||||
message.warn('禁止复制');
|
||||
// message.warn('禁止复制');
|
||||
return
|
||||
}
|
||||
let reader;
|
||||
|
@ -82,7 +82,9 @@ class Term extends Component {
|
||||
document.body.oncopy = (event) => {
|
||||
event.preventDefault();
|
||||
if (this.state.session['copy'] === '0') {
|
||||
message.warn('禁止复制')
|
||||
// message.warn('禁止复制')
|
||||
return false;
|
||||
}else {
|
||||
if (event.clipboardData) {
|
||||
return event.clipboardData.setData('text', '');
|
||||
} else {
|
||||
@ -90,13 +92,12 @@ class Term extends Component {
|
||||
return window.clipboardData.setData("text", '');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
document.body.onpaste = (event) => {
|
||||
event.preventDefault();
|
||||
if (this.state.session['paste'] === '0') {
|
||||
message.warn('禁止粘贴')
|
||||
// message.warn('禁止粘贴')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -412,7 +412,7 @@ Windows需要对远程应用程序的名称使用特殊的符号。
|
||||
<Panel header={<Text strong>映射网络驱动器</Text>} key="storage">
|
||||
<Form.Item
|
||||
name="enable-drive"
|
||||
label="启用映射网络驱动器"
|
||||
label="启用"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"
|
||||
@ -424,7 +424,7 @@ Windows需要对远程应用程序的名称使用特殊的符号。
|
||||
enableDrive ?
|
||||
<Form.Item
|
||||
name="drive-path"
|
||||
label="映射网络驱动器"
|
||||
label="映射空间"
|
||||
tooltip='用于文件传输的映射网络驱动器,为空时使用操作人的默认空间'
|
||||
>
|
||||
<Select onChange={null} allowClear placeholder='为空时使用操作人的默认空间'>
|
||||
@ -456,7 +456,7 @@ Windows需要对远程应用程序的名称使用特殊的符号。
|
||||
setSshMode(value)
|
||||
}}>
|
||||
<Option value="">guacd</Option>
|
||||
<Option value="native">原生</Option>
|
||||
<Option value="naive">原生</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Panel>
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
CodeOutlined,
|
||||
DesktopOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
SyncOutlined,
|
||||
TagsOutlined,
|
||||
@ -195,14 +194,6 @@ class MyAsset extends Component {
|
||||
style={{width: 200}}
|
||||
/>
|
||||
|
||||
<Search
|
||||
ref={this.inputRefOfIp}
|
||||
placeholder="资产IP"
|
||||
allowClear
|
||||
onSearch={this.handleSearchByIp}
|
||||
style={{width: 200}}
|
||||
/>
|
||||
|
||||
<Select mode="multiple"
|
||||
allowClear
|
||||
value={this.state.selectedTags}
|
||||
@ -302,9 +293,6 @@ class MyAsset extends Component {
|
||||
<Descriptions.Item label={<div><CodeOutlined/> 资产协议</div>}>
|
||||
<strong>{item['protocol']}</strong>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={<div><DesktopOutlined/> 主机地址</div>}>
|
||||
<strong>{item['ip'] + ':' + item['port']}</strong>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={<div><TagsOutlined/> 标签</div>}>
|
||||
<strong>{this.renderTags(item['tags'])}</strong>
|
||||
</Descriptions.Item>
|
||||
|
@ -9,7 +9,7 @@ const formItemLayout = {
|
||||
const StorageModal = ({title, visible, handleOk, handleCancel, confirmLoading, model}) => {
|
||||
|
||||
const [form] = Form.useForm();
|
||||
if(!model){
|
||||
if (!model) {
|
||||
model = {
|
||||
isShare: false
|
||||
}
|
||||
@ -46,11 +46,13 @@ const StorageModal = ({title, visible, handleOk, handleCancel, confirmLoading, m
|
||||
<Input autoComplete="off" placeholder="网盘的名称"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="是否共享" name='isShare' rules={[{required: true, message: '请选择是否共享'}]} valuePropName="checked">
|
||||
<Switch checkedChildren="是" unCheckedChildren="否" />
|
||||
<Form.Item label="是否共享" name='isShare' rules={[{required: true, message: '请选择是否共享'}]}
|
||||
valuePropName="checked">
|
||||
<Switch checkedChildren="是" unCheckedChildren="否"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="大小限制" name='limitSize' rules={[{required: true, message: '请输入大小限制'}]}>
|
||||
<Form.Item label="大小限制" name='limitSize' rules={[{required: true, message: '请输入大小限制'}]}
|
||||
tooltip='无限制请填写-1'>
|
||||
<Input type={'number'} min={-1} suffix="MB"/>
|
||||
</Form.Item>
|
||||
|
||||
|
@ -25,7 +25,6 @@ class Setting extends Component {
|
||||
state = {
|
||||
refs: [],
|
||||
properties: {},
|
||||
ldapUserSyncLoading: false
|
||||
}
|
||||
|
||||
rdpSettingFormRef = React.createRef();
|
||||
@ -33,8 +32,8 @@ class Setting extends Component {
|
||||
vncSettingFormRef = React.createRef();
|
||||
guacdSettingFormRef = React.createRef();
|
||||
mailSettingFormRef = React.createRef();
|
||||
ldapSettingFormRef = React.createRef();
|
||||
logSettingFormRef = React.createRef();
|
||||
otherSettingFormRef = React.createRef();
|
||||
|
||||
componentDidMount() {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
@ -43,7 +42,15 @@ class Setting extends Component {
|
||||
};
|
||||
|
||||
this.setState({
|
||||
refs: [this.rdpSettingFormRef, this.sshSettingFormRef, this.vncSettingFormRef, this.guacdSettingFormRef, this.mailSettingFormRef, this.logSettingFormRef]
|
||||
refs: [
|
||||
this.rdpSettingFormRef,
|
||||
this.sshSettingFormRef,
|
||||
this.vncSettingFormRef,
|
||||
this.guacdSettingFormRef,
|
||||
this.mailSettingFormRef,
|
||||
this.logSettingFormRef,
|
||||
this.otherSettingFormRef
|
||||
]
|
||||
}, this.getProperties)
|
||||
}
|
||||
|
||||
@ -121,26 +128,6 @@ class Setting extends Component {
|
||||
reader.readAsText(files[0]);
|
||||
}
|
||||
|
||||
ldapUserSync = async () => {
|
||||
const id = 'ldap-user-sync'
|
||||
try {
|
||||
this.setState({
|
||||
ldapUserSyncLoading: true
|
||||
});
|
||||
message.info({content: '同步中...', key: id, duration: 5});
|
||||
let result = await request.post(`/properties/ldap-user-sync`);
|
||||
if (result.code !== 1) {
|
||||
message.error({content: result.message, key: id, duration: 10});
|
||||
return;
|
||||
}
|
||||
message.success({content: '同步成功。', key: id, duration: 3});
|
||||
} finally {
|
||||
this.setState({
|
||||
ldapUserSyncLoading: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
@ -551,7 +538,7 @@ class Setting extends Component {
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="日志配置" key="log">
|
||||
<Title level={3}>其他配置</Title>
|
||||
<Title level={3}>日志配置</Title>
|
||||
<Form ref={this.logSettingFormRef} name="log" onFinish={this.changeProperties}
|
||||
layout="vertical">
|
||||
|
||||
@ -593,6 +580,28 @@ class Setting extends Component {
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="其他配置" key="other">
|
||||
<Title level={3}>其他配置</Title>
|
||||
<Form ref={this.otherSettingFormRef} name="other" onFinish={this.changeProperties}
|
||||
layout="vertical">
|
||||
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
name="user-default-storage-size"
|
||||
label="用户空间默认大小"
|
||||
tooltip='无限制请填写-1'
|
||||
>
|
||||
<Input type={'number'} min={-1} suffix="MB"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item {...formTailLayout}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
更新
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="备份与恢复" key="backup">
|
||||
<Title level={3}>备份与恢复</Title>
|
||||
|
||||
|
@ -280,7 +280,7 @@ class User extends Component {
|
||||
let result = await request.delete('/users/' + id);
|
||||
if (result.code === 1) {
|
||||
message.success('操作成功', 3);
|
||||
await this.loadTableData(this.state.queryParams);
|
||||
this.loadTableData(this.state.queryParams);
|
||||
} else {
|
||||
message.error(result.message, 10);
|
||||
}
|
||||
|
@ -425,8 +425,11 @@ class UserShareSelectedAsset extends Component {
|
||||
<div>
|
||||
<p>上传:{renderStatus(item['strategy']['upload'])}</p>
|
||||
<p>下载:{renderStatus(item['strategy']['download'])}</p>
|
||||
<p>编辑:{renderStatus(item['strategy']['edit'])}</p>
|
||||
<p>删除:{renderStatus(item['strategy']['delete'])}</p>
|
||||
<p>改名:{renderStatus(item['strategy']['rename'])}</p>
|
||||
<p>重命名:{renderStatus(item['strategy']['rename'])}</p>
|
||||
<p>复制:{renderStatus(item['strategy']['copy'])}</p>
|
||||
<p>粘贴:{renderStatus(item['strategy']['paste'])}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
BIN
web/src/images/bg.jpg
Normal file
BIN
web/src/images/bg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 152 KiB |
Loading…
Reference in New Issue
Block a user