提交 v1.3.0 beta
This commit is contained in:
70
server/api/abi/abi.go
Normal file
70
server/api/abi/abi.go
Normal file
@ -0,0 +1,70 @@
|
||||
package abi
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/cache"
|
||||
"next-terminal/server/model"
|
||||
)
|
||||
|
||||
type Abi struct {
|
||||
}
|
||||
|
||||
func (r *Abi) Fail(c echo.Context, code int, message string) error {
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": code,
|
||||
"message": message,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Abi) FailWithData(c echo.Context, code int, message string, data interface{}) error {
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Abi) Success(c echo.Context, data interface{}) error {
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": 1,
|
||||
"message": "success",
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Abi) GetToken(c echo.Context) string {
|
||||
token := c.Request().Header.Get(nt.Token)
|
||||
if len(token) > 0 {
|
||||
return token
|
||||
}
|
||||
return c.QueryParam(nt.Token)
|
||||
}
|
||||
|
||||
func (r *Abi) GetCurrentAccount(c echo.Context) (*model.User, bool) {
|
||||
token := r.GetToken(c)
|
||||
get, b := cache.TokenManager.Get(token)
|
||||
if b {
|
||||
return get.(dto.Authorization).User, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r *Abi) HasPermission(c echo.Context, owner string) bool {
|
||||
// 检测是否登录
|
||||
account, found := r.GetCurrentAccount(c)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
// 检测是否为管理人员
|
||||
if nt.TypeAdmin == account.Type {
|
||||
return true
|
||||
}
|
||||
// 检测是否为所有者
|
||||
if owner == account.ID {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
@ -2,6 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -23,7 +25,7 @@ func (api AccessGatewayApi) AccessGatewayCreateEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.GatewayRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
@ -38,11 +40,14 @@ func (api AccessGatewayApi) AccessGatewayAllEndpoint(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var simpleGateways = make([]model.AccessGatewayForPage, 0)
|
||||
for i := 0; i < len(gateways); i++ {
|
||||
simpleGateways = append(simpleGateways, model.AccessGatewayForPage{ID: gateways[i].ID, Name: gateways[i].Name})
|
||||
items := make([]maps.Map, len(gateways))
|
||||
for i, e := range gateways {
|
||||
items[i] = maps.Map{
|
||||
"id": e.ID,
|
||||
"name": e.Name,
|
||||
}
|
||||
}
|
||||
return Success(c, simpleGateways)
|
||||
return Success(c, items)
|
||||
}
|
||||
|
||||
func (api AccessGatewayApi) AccessGatewayPagingEndpoint(c echo.Context) error {
|
||||
@ -66,7 +71,7 @@ func (api AccessGatewayApi) AccessGatewayPagingEndpoint(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
|
@ -3,17 +3,17 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/nt"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/cache"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"next-terminal/server/totp"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@ -50,7 +50,7 @@ func (api AccountApi) LoginEndpoint(c echo.Context) error {
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if user.Status == constant.StatusDisabled {
|
||||
if user.Status == nt.StatusDisabled {
|
||||
return Fail(c, -1, "该账户已停用")
|
||||
}
|
||||
|
||||
@ -64,11 +64,24 @@ func (api AccountApi) LoginEndpoint(c echo.Context) error {
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
// 账号密码正确,需要进行两步验证
|
||||
if user.TOTPSecret != "" && user.TOTPSecret != "-" {
|
||||
return Fail(c, 0, "")
|
||||
if loginAccount.TOTP == "" {
|
||||
return Fail(c, 100, "")
|
||||
} else {
|
||||
if !common.Validate(loginAccount.TOTP, user.TOTPSecret) {
|
||||
count++
|
||||
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "双因素认证授权码不正确"); err != nil {
|
||||
return err
|
||||
}
|
||||
return FailWithData(c, -1, "您输入双因素认证授权码不正确", count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
token, err := api.LoginSuccess(loginAccount, user)
|
||||
token, err := api.LoginSuccess(loginAccount, user, c.RealIP())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -80,12 +93,17 @@ func (api AccountApi) LoginEndpoint(c echo.Context) error {
|
||||
return Success(c, token)
|
||||
}
|
||||
|
||||
func (api AccountApi) LoginSuccess(loginAccount dto.LoginAccount, user model.User) (string, error) {
|
||||
func (api AccountApi) LoginSuccess(loginAccount dto.LoginAccount, user model.User, ip string) (string, error) {
|
||||
// 判断当前时间是否允许该用户登录
|
||||
if err := service.LoginPolicyService.Check(user.ID, ip); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token := utils.LongUUID()
|
||||
|
||||
authorization := dto.Authorization{
|
||||
Token: token,
|
||||
Type: constant.LoginToken,
|
||||
Type: nt.LoginToken,
|
||||
Remember: loginAccount.Remember,
|
||||
User: &user,
|
||||
}
|
||||
@ -97,75 +115,12 @@ func (api AccountApi) LoginSuccess(loginAccount dto.LoginAccount, user model.Use
|
||||
cache.TokenManager.Set(token, authorization, cache.NotRememberExpiration)
|
||||
}
|
||||
|
||||
b := true
|
||||
// 修改登录状态
|
||||
err := repository.UserRepository.Update(context.TODO(), &model.User{Online: true, ID: user.ID})
|
||||
err := repository.UserRepository.Update(context.TODO(), &model.User{Online: &b, ID: user.ID})
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (api AccountApi) LoginWithTotpEndpoint(c echo.Context) error {
|
||||
var loginAccount dto.LoginAccount
|
||||
if err := c.Bind(&loginAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 存储登录失败次数信息
|
||||
loginFailCountKey := c.RealIP() + loginAccount.Username
|
||||
v, ok := cache.LoginFailedKeyManager.Get(loginFailCountKey)
|
||||
if !ok {
|
||||
v = 1
|
||||
}
|
||||
count := v.(int)
|
||||
if count >= 5 {
|
||||
return Fail(c, -1, "登录失败次数过多,请等待5分钟后再试")
|
||||
}
|
||||
|
||||
user, err := repository.UserRepository.FindByUsername(context.TODO(), loginAccount.Username)
|
||||
if err != nil {
|
||||
count++
|
||||
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
||||
return err
|
||||
}
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if user.Status == constant.StatusDisabled {
|
||||
return Fail(c, -1, "该账户已停用")
|
||||
}
|
||||
|
||||
if err := utils.Encoder.Match([]byte(user.Password), []byte(loginAccount.Password)); err != nil {
|
||||
count++
|
||||
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "账号或密码不正确"); err != nil {
|
||||
return err
|
||||
}
|
||||
return FailWithData(c, -1, "您输入的账号或密码不正确", count)
|
||||
}
|
||||
|
||||
if !totp.Validate(loginAccount.TOTP, user.TOTPSecret) {
|
||||
count++
|
||||
cache.LoginFailedKeyManager.Set(loginFailCountKey, count, cache.LoginLockExpiration)
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, false, loginAccount.Remember, "", "双因素认证授权码不正确"); err != nil {
|
||||
return err
|
||||
}
|
||||
return FailWithData(c, -1, "您输入双因素认证授权码不正确", count)
|
||||
}
|
||||
|
||||
token, err := api.LoginSuccess(loginAccount, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 保存登录日志
|
||||
if err := service.UserService.SaveLoginLog(c.RealIP(), c.Request().UserAgent(), loginAccount.Username, true, loginAccount.Remember, token, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, token)
|
||||
}
|
||||
|
||||
func (api AccountApi) LogoutEndpoint(c echo.Context) error {
|
||||
token := GetToken(c)
|
||||
service.UserService.Logout(token)
|
||||
@ -183,7 +138,7 @@ func (api AccountApi) ConfirmTOTPEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !totp.Validate(confirmTOTP.TOTP, confirmTOTP.Secret) {
|
||||
if !common.Validate(confirmTOTP.TOTP, confirmTOTP.Secret) {
|
||||
return Fail(c, -1, "TOTP 验证失败,请重试")
|
||||
}
|
||||
|
||||
@ -202,7 +157,7 @@ func (api AccountApi) ConfirmTOTPEndpoint(c echo.Context) error {
|
||||
func (api AccountApi) ReloadTOTPEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
key, err := totp.NewTOTP(totp.GenerateOpts{
|
||||
key, err := common.NewTOTP(common.GenerateOpts{
|
||||
Issuer: c.Request().Host,
|
||||
AccountName: account.Username,
|
||||
})
|
||||
@ -270,59 +225,68 @@ func (api AccountApi) ChangePasswordEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
type AccountInfo struct {
|
||||
Id string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Type string `json:"type"`
|
||||
EnableTotp bool `json:"enableTotp"`
|
||||
Id string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Type string `json:"type"`
|
||||
EnableTotp bool `json:"enableTotp"`
|
||||
Roles []string `json:"roles"`
|
||||
Menus []string `json:"menus"`
|
||||
}
|
||||
|
||||
func (api AccountApi) InfoEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
if strings.EqualFold("anonymous", account.Type) {
|
||||
return Success(c, account)
|
||||
}
|
||||
|
||||
user, err := repository.UserRepository.FindById(context.TODO(), account.ID)
|
||||
user, err := service.UserService.FindById(account.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var menus []string
|
||||
if service.UserService.IsSuperAdmin(account.ID) {
|
||||
menus = service.MenuService.GetMenus()
|
||||
} else {
|
||||
roles, err := service.RoleService.GetRolesByUserId(account.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, role := range roles {
|
||||
items := service.RoleService.GetMenuListByRole(role)
|
||||
menus = append(menus, items...)
|
||||
}
|
||||
}
|
||||
|
||||
info := AccountInfo{
|
||||
Id: user.ID,
|
||||
Username: user.Username,
|
||||
Nickname: user.Nickname,
|
||||
Type: user.Type,
|
||||
EnableTotp: user.TOTPSecret != "" && user.TOTPSecret != "-",
|
||||
Roles: user.Roles,
|
||||
Menus: menus,
|
||||
}
|
||||
return Success(c, info)
|
||||
}
|
||||
|
||||
func (api AccountApi) AccountAssetEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
protocol := c.QueryParam("protocol")
|
||||
tags := c.QueryParam("tags")
|
||||
owner := c.QueryParam("owner")
|
||||
sharer := c.QueryParam("sharer")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
ip := c.QueryParam("ip")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
func (api AccountApi) MenuEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
items, total, err := repository.AssetRepository.Find(context.TODO(), pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field)
|
||||
if service.UserService.IsSuperAdmin(account.ID) {
|
||||
items := service.MenuService.GetMenus()
|
||||
return Success(c, items)
|
||||
}
|
||||
roles, err := service.RoleService.GetRolesByUserId(account.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range items {
|
||||
items[i].IP = ""
|
||||
items[i].Port = 0
|
||||
var menus []string
|
||||
for _, role := range roles {
|
||||
items := service.RoleService.GetMenuListByRole(role)
|
||||
menus = append(menus, items...)
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
return Success(c, menus)
|
||||
}
|
||||
|
||||
func (api AccountApi) AccountStorageEndpoint(c echo.Context) error {
|
||||
@ -364,3 +328,11 @@ func (api AccountApi) AccessTokenGenEndpoint(c echo.Context) error {
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api AccountApi) AccessTokenDelEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
if err := service.AccessTokenService.DelAccessToken(context.Background(), account.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
@ -1,25 +1,23 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"next-terminal/server/constant"
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/cache"
|
||||
"next-terminal/server/model"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type Map map[string]interface{}
|
||||
|
||||
func Fail(c echo.Context, code int, message string) error {
|
||||
return c.JSON(200, Map{
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": code,
|
||||
"message": message,
|
||||
})
|
||||
}
|
||||
|
||||
func FailWithData(c echo.Context, code int, message string, data interface{}) error {
|
||||
return c.JSON(200, Map{
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data,
|
||||
@ -27,7 +25,7 @@ func FailWithData(c echo.Context, code int, message string, data interface{}) er
|
||||
}
|
||||
|
||||
func Success(c echo.Context, data interface{}) error {
|
||||
return c.JSON(200, Map{
|
||||
return c.JSON(200, maps.Map{
|
||||
"code": 1,
|
||||
"message": "success",
|
||||
"data": data,
|
||||
@ -35,11 +33,11 @@ func Success(c echo.Context, data interface{}) error {
|
||||
}
|
||||
|
||||
func GetToken(c echo.Context) string {
|
||||
token := c.Request().Header.Get(constant.Token)
|
||||
token := c.Request().Header.Get(nt.Token)
|
||||
if len(token) > 0 {
|
||||
return token
|
||||
}
|
||||
return c.QueryParam(constant.Token)
|
||||
return c.QueryParam(nt.Token)
|
||||
}
|
||||
|
||||
func GetCurrentAccount(c echo.Context) (*model.User, bool) {
|
||||
@ -50,20 +48,3 @@ func GetCurrentAccount(c echo.Context) (*model.User, bool) {
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func HasPermission(c echo.Context, owner string) bool {
|
||||
// 检测是否登录
|
||||
account, found := GetCurrentAccount(c)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
// 检测是否为管理人员
|
||||
if constant.TypeAdmin == account.Type {
|
||||
return true
|
||||
}
|
||||
// 检测是否为所有者
|
||||
if owner == account.ID {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -5,10 +5,12 @@ import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
@ -75,13 +77,13 @@ func (assetApi AssetApi) AssetImportEndpoint(c echo.Context) error {
|
||||
Protocol: record[1],
|
||||
IP: record[2],
|
||||
Port: port,
|
||||
AccountType: constant.Custom,
|
||||
AccountType: nt.Custom,
|
||||
Username: record[4],
|
||||
Password: record[5],
|
||||
PrivateKey: record[6],
|
||||
Passphrase: record[7],
|
||||
Description: record[8],
|
||||
Created: utils.NowJsonTime(),
|
||||
Created: common.NowJsonTime(),
|
||||
Owner: account.ID,
|
||||
Active: true,
|
||||
}
|
||||
@ -114,22 +116,18 @@ func (assetApi AssetApi) AssetPagingEndpoint(c echo.Context) error {
|
||||
name := c.QueryParam("name")
|
||||
protocol := c.QueryParam("protocol")
|
||||
tags := c.QueryParam("tags")
|
||||
owner := c.QueryParam("owner")
|
||||
sharer := c.QueryParam("sharer")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
ip := c.QueryParam("ip")
|
||||
active := c.QueryParam("active")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
items, total, err := repository.AssetRepository.Find(context.TODO(), pageIndex, pageSize, name, protocol, tags, account, owner, sharer, userGroupId, ip, order, field)
|
||||
items, total, err := repository.AssetRepository.Find(context.Background(), pageIndex, pageSize, name, protocol, tags, ip, active, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -137,16 +135,22 @@ func (assetApi AssetApi) AssetPagingEndpoint(c echo.Context) error {
|
||||
|
||||
func (assetApi AssetApi) AssetAllEndpoint(c echo.Context) error {
|
||||
protocol := c.QueryParam("protocol")
|
||||
items, _ := repository.AssetRepository.FindByProtocol(context.TODO(), protocol)
|
||||
assets, err := repository.AssetRepository.FindByProtocol(context.TODO(), protocol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items := make([]maps.Map, len(assets))
|
||||
for i, e := range assets {
|
||||
items[i] = maps.Map{
|
||||
"id": e.ID,
|
||||
"name": e.Name,
|
||||
}
|
||||
}
|
||||
return Success(c, items)
|
||||
}
|
||||
|
||||
func (assetApi AssetApi) AssetUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := assetApi.PreCheckAssetPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := echo.Map{}
|
||||
if err := c.Bind(&m); err != nil {
|
||||
return err
|
||||
@ -161,9 +165,6 @@ func (assetApi AssetApi) AssetDeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
split := strings.Split(id, ",")
|
||||
for i := range split {
|
||||
if err := assetApi.PreCheckAssetPermission(c, split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := service.AssetService.DeleteById(split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -174,9 +175,6 @@ func (assetApi AssetApi) AssetDeleteEndpoint(c echo.Context) error {
|
||||
|
||||
func (assetApi AssetApi) AssetGetEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
if err := assetApi.PreCheckAssetPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var item model.Asset
|
||||
if item, err = service.AssetService.FindByIdAndDecrypt(context.TODO(), id); err != nil {
|
||||
@ -202,20 +200,17 @@ func (assetApi AssetApi) AssetTcpingEndpoint(c echo.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
active, err := service.AssetService.CheckStatus(item.AccessGatewayId, item.IP, item.Port)
|
||||
|
||||
if item.Active != active {
|
||||
if err := repository.AssetRepository.UpdateActiveById(context.TODO(), active, item.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
active, err := service.AssetService.CheckStatus(&item, item.IP, item.Port)
|
||||
|
||||
var message = ""
|
||||
if err != nil {
|
||||
message = err.Error()
|
||||
}
|
||||
if err := repository.AssetRepository.UpdateActiveById(context.TODO(), active, message, item.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"active": active,
|
||||
"message": message,
|
||||
})
|
||||
@ -232,25 +227,9 @@ func (assetApi AssetApi) AssetTagsEndpoint(c echo.Context) (err error) {
|
||||
func (assetApi AssetApi) AssetChangeOwnerEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := assetApi.PreCheckAssetPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
owner := c.QueryParam("owner")
|
||||
if err := repository.AssetRepository.UpdateById(context.TODO(), &model.Asset{Owner: owner}, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (assetApi AssetApi) PreCheckAssetPermission(c echo.Context, id string) error {
|
||||
item, err := repository.AssetRepository.FindById(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !HasPermission(c, item.Owner) {
|
||||
return errors.New("permission denied")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
136
server/api/authorised.go
Normal file
136
server/api/authorised.go
Normal file
@ -0,0 +1,136 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type AuthorisedApi struct {
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) Selected(c echo.Context) error {
|
||||
userId := c.QueryParam("userId")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
assetId := c.QueryParam("assetId")
|
||||
key := c.QueryParam("key")
|
||||
|
||||
items, err := repository.AuthorisedRepository.FindAll(context.Background(), userId, userGroupId, assetId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var result = make([]string, 0)
|
||||
switch key {
|
||||
case "userId":
|
||||
for _, item := range items {
|
||||
result = append(result, item.UserId)
|
||||
}
|
||||
case "userGroupId":
|
||||
for _, item := range items {
|
||||
result = append(result, item.UserGroupId)
|
||||
}
|
||||
case "assetId":
|
||||
for _, item := range items {
|
||||
result = append(result, item.AssetId)
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, result)
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) Delete(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := repository.AuthorisedRepository.DeleteById(context.Background(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) PagingAsset(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
assetName := c.QueryParam("assetName")
|
||||
userId := c.QueryParam("userId")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
|
||||
items, total, err := repository.AuthorisedRepository.FindAssetPage(context.Background(), pageIndex, pageSize, assetName, userId, userGroupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) PagingUser(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
userName := c.QueryParam("userName")
|
||||
assetId := c.QueryParam("assetId")
|
||||
|
||||
items, total, err := repository.AuthorisedRepository.FindUserPage(context.Background(), pageIndex, pageSize, userName, assetId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) PagingUserGroup(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
userGroupName := c.QueryParam("userGroupName")
|
||||
assetId := c.QueryParam("assetId")
|
||||
|
||||
items, total, err := repository.AuthorisedRepository.FindUserGroupPage(context.Background(), pageIndex, pageSize, userGroupName, assetId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) AuthorisedAssets(c echo.Context) error {
|
||||
var item dto.AuthorisedAsset
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := service.AuthorisedService.AuthorisedAssets(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) AuthorisedUsers(c echo.Context) error {
|
||||
var item dto.AuthorisedUser
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := service.AuthorisedService.AuthorisedUsers(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api AuthorisedApi) AuthorisedUserGroups(c echo.Context) error {
|
||||
var item dto.AuthorisedUserGroup
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := service.AuthorisedService.AuthorisedUserGroups(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
15
server/api/branding.go
Normal file
15
server/api/branding.go
Normal file
@ -0,0 +1,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"next-terminal/server/branding"
|
||||
"next-terminal/server/common/maps"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func Branding(c echo.Context) error {
|
||||
return Success(c, maps.Map{
|
||||
"name": branding.Name,
|
||||
"copyright": branding.Copyright,
|
||||
})
|
||||
}
|
@ -2,7 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -24,7 +25,7 @@ func (api CommandApi) CommandCreateEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
item.Owner = account.ID
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.CommandRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
@ -34,8 +35,7 @@ func (api CommandApi) CommandCreateEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
func (api CommandApi) CommandAllEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
items, err := repository.CommandRepository.FindByUser(context.TODO(), account)
|
||||
items, err := repository.CommandRepository.FindAll(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -47,17 +47,16 @@ func (api CommandApi) CommandPagingEndpoint(c echo.Context) error {
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
content := c.QueryParam("content")
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.CommandRepository.Find(context.TODO(), pageIndex, pageSize, name, content, order, field, account)
|
||||
items, total, err := repository.CommandRepository.Find(context.TODO(), pageIndex, pageSize, name, content, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -65,9 +64,6 @@ func (api CommandApi) CommandPagingEndpoint(c echo.Context) error {
|
||||
|
||||
func (api CommandApi) CommandUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := api.PreCheckCommandPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var item model.Command
|
||||
if err := c.Bind(&item); err != nil {
|
||||
@ -85,9 +81,6 @@ func (api CommandApi) CommandDeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
split := strings.Split(id, ",")
|
||||
for i := range split {
|
||||
if err := api.PreCheckCommandPermission(c, split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := repository.CommandRepository.DeleteById(context.TODO(), split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -97,11 +90,6 @@ func (api CommandApi) CommandDeleteEndpoint(c echo.Context) error {
|
||||
|
||||
func (api CommandApi) CommandGetEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := api.PreCheckCommandPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var item model.Command
|
||||
if item, err = repository.CommandRepository.FindById(context.TODO(), id); err != nil {
|
||||
return err
|
||||
@ -111,26 +99,9 @@ func (api CommandApi) CommandGetEndpoint(c echo.Context) (err error) {
|
||||
|
||||
func (api CommandApi) CommandChangeOwnerEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := api.PreCheckCommandPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
owner := c.QueryParam("owner")
|
||||
if err := repository.CommandRepository.UpdateById(context.TODO(), &model.Command{Owner: owner}, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api CommandApi) PreCheckCommandPermission(c echo.Context, id string) error {
|
||||
item, err := repository.CommandRepository.FindById(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !HasPermission(c, item.Owner) {
|
||||
return errors.New("permission denied")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
@ -20,7 +21,7 @@ import (
|
||||
type CredentialApi struct{}
|
||||
|
||||
func (api CredentialApi) CredentialAllEndpoint(c echo.Context) error {
|
||||
items, err := repository.CredentialRepository.FindByUser(context.TODO())
|
||||
items, err := repository.CredentialRepository.FindByAll(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -35,10 +36,10 @@ func (api CredentialApi) CredentialCreateEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
item.Owner = account.ID
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
switch item.Type {
|
||||
case constant.Custom:
|
||||
case nt.Custom:
|
||||
item.PrivateKey = "-"
|
||||
item.Passphrase = "-"
|
||||
if item.Username == "" {
|
||||
@ -47,7 +48,7 @@ func (api CredentialApi) CredentialCreateEndpoint(c echo.Context) error {
|
||||
if item.Password == "" {
|
||||
item.Password = "-"
|
||||
}
|
||||
case constant.PrivateKey:
|
||||
case nt.PrivateKey:
|
||||
item.Password = "-"
|
||||
if item.Username == "" {
|
||||
item.Username = "-"
|
||||
@ -79,13 +80,12 @@ func (api CredentialApi) CredentialPagingEndpoint(c echo.Context) error {
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
account, _ := GetCurrentAccount(c)
|
||||
items, total, err := repository.CredentialRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field, account)
|
||||
items, total, err := repository.CredentialRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -94,17 +94,13 @@ func (api CredentialApi) CredentialPagingEndpoint(c echo.Context) error {
|
||||
func (api CredentialApi) CredentialUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := api.PreCheckCredentialPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var item model.Credential
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch item.Type {
|
||||
case constant.Custom:
|
||||
case nt.Custom:
|
||||
item.PrivateKey = "-"
|
||||
item.Passphrase = "-"
|
||||
if item.Username == "" {
|
||||
@ -120,7 +116,7 @@ func (api CredentialApi) CredentialUpdateEndpoint(c echo.Context) error {
|
||||
}
|
||||
item.Password = base64.StdEncoding.EncodeToString(encryptedCBC)
|
||||
}
|
||||
case constant.PrivateKey:
|
||||
case nt.PrivateKey:
|
||||
item.Password = "-"
|
||||
if item.Username == "" {
|
||||
item.Username = "-"
|
||||
@ -161,9 +157,6 @@ func (api CredentialApi) CredentialDeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
split := strings.Split(id, ",")
|
||||
for i := range split {
|
||||
if err := api.PreCheckCredentialPermission(c, split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := repository.CredentialRepository.DeleteById(context.TODO(), split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -174,44 +167,21 @@ func (api CredentialApi) CredentialDeleteEndpoint(c echo.Context) error {
|
||||
|
||||
func (api CredentialApi) CredentialGetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := api.PreCheckCredentialPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
item, err := service.CredentialService.FindByIdAndDecrypt(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !HasPermission(c, item.Owner) {
|
||||
return errors.New("permission denied")
|
||||
}
|
||||
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (api CredentialApi) CredentialChangeOwnerEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
if err := api.PreCheckCredentialPermission(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
owner := c.QueryParam("owner")
|
||||
if err := repository.CredentialRepository.UpdateById(context.TODO(), &model.Credential{Owner: owner}, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api CredentialApi) PreCheckCredentialPermission(c echo.Context, id string) error {
|
||||
item, err := repository.CredentialRepository.FindById(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !HasPermission(c, item.Owner) {
|
||||
return errors.New("permission denied")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"next-terminal/server/common/guacamole"
|
||||
"next-terminal/server/common/nt"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/global/session"
|
||||
"next-terminal/server/guacd"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
@ -34,6 +34,8 @@ const (
|
||||
)
|
||||
|
||||
var UpGrader = websocket.Upgrader{
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
@ -46,7 +48,7 @@ type GuacamoleApi struct {
|
||||
func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||
if err != nil {
|
||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||
log.Warn("升级为WebSocket协议失败", log.NamedError("err", err))
|
||||
return err
|
||||
}
|
||||
ctx := context.TODO()
|
||||
@ -58,7 +60,7 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
intWidth, _ := strconv.Atoi(width)
|
||||
intHeight, _ := strconv.Atoi(height)
|
||||
|
||||
configuration := guacd.NewConfiguration()
|
||||
configuration := guacamole.NewConfiguration()
|
||||
|
||||
propertyMap := repository.PropertyRepository.FindAllMap(ctx)
|
||||
|
||||
@ -74,14 +76,14 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
if s.AccessGatewayId != "" && s.AccessGatewayId != "-" {
|
||||
g, err := service.GatewayService.GetGatewayById(s.AccessGatewayId)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, AccessGatewayUnAvailable, "获取接入网关失败:"+err.Error())
|
||||
guacamole.Disconnect(ws, AccessGatewayUnAvailable, "获取接入网关失败:"+err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
defer g.CloseSshTunnel(s.ID)
|
||||
exposedIP, exposedPort, err := g.OpenSshTunnel(s.ID, s.IP, s.Port)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, AccessGatewayCreateError, "创建SSH隧道失败:"+err.Error())
|
||||
guacamole.Disconnect(ws, AccessGatewayCreateError, "创建SSH隧道失败:"+err.Error())
|
||||
return nil
|
||||
}
|
||||
s.IP = exposedIP
|
||||
@ -108,12 +110,12 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
|
||||
addr := config.GlobalCfg.Guacd.Hostname + ":" + strconv.Itoa(config.GlobalCfg.Guacd.Port)
|
||||
asset := fmt.Sprintf("%s:%s", configuration.GetParameter("hostname"), configuration.GetParameter("port"))
|
||||
log.Debugf("[%v] 新建 guacd 会话, guacd=%v, asset=%v", sessionId, addr, asset)
|
||||
log.Debug("新建 guacd 会话", log.String("sessionId", sessionId), log.String("addr", addr), log.String("asset", asset))
|
||||
|
||||
guacdTunnel, err := guacd.NewTunnel(addr, configuration)
|
||||
guacdTunnel, err := guacamole.NewTunnel(addr, configuration)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, NewTunnelError, err.Error())
|
||||
log.Printf("[%v] 建立连接失败: %v", sessionId, err.Error())
|
||||
guacamole.Disconnect(ws, NewTunnelError, err.Error())
|
||||
log.Error("建立连接失败", log.String("sessionId", sessionId), log.NamedError("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
@ -125,11 +127,11 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
GuacdTunnel: guacdTunnel,
|
||||
}
|
||||
|
||||
if configuration.Protocol == constant.SSH {
|
||||
if configuration.Protocol == nt.SSH {
|
||||
nextTerminal, err := CreateNextTerminalBySession(s)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, NewSshClientError, "建立SSH客户端失败: "+err.Error())
|
||||
log.Printf("[%v] 建立 ssh 客户端失败: %v", sessionId, err.Error())
|
||||
guacamole.Disconnect(ws, NewSshClientError, "建立SSH客户端失败: "+err.Error())
|
||||
log.Debug("建立 ssh 客户端失败", log.String("sessionId", sessionId), log.NamedError("err", err))
|
||||
return err
|
||||
}
|
||||
nextSession.NextTerminal = nextTerminal
|
||||
@ -141,15 +143,15 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
ConnectionId: guacdTunnel.UUID,
|
||||
Width: intWidth,
|
||||
Height: intHeight,
|
||||
Status: constant.Connecting,
|
||||
Recording: configuration.GetParameter(guacd.RecordingPath),
|
||||
Status: nt.Connecting,
|
||||
Recording: configuration.GetParameter(guacamole.RecordingPath),
|
||||
}
|
||||
if sess.Recording == "" {
|
||||
// 未录屏时无需审计
|
||||
sess.Reviewed = true
|
||||
}
|
||||
// 创建新会话
|
||||
log.Debugf("[%v] 新建会话成功: %v", sessionId, sess.ConnectionId)
|
||||
log.Debug("新建会话成功", log.String("sessionId", sessionId))
|
||||
if err := repository.SessionRepository.UpdateById(ctx, &sess, sessionId); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -161,7 +163,7 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
for {
|
||||
_, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
log.Debugf("[%v] WebSocket已关闭, %v", sessionId, err.Error())
|
||||
log.Debug("WebSocket已关闭", log.String("sessionId", sessionId), log.NamedError("err", err))
|
||||
// guacdTunnel.Read() 会阻塞,所以要先把guacdTunnel客户端关闭,才能退出Guacd循环
|
||||
_ = guacdTunnel.Close()
|
||||
|
||||
@ -176,23 +178,22 @@ func (api GuacamoleApi) Guacamole(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (api GuacamoleApi) setAssetConfig(attributes map[string]string, s model.Session, configuration *guacd.Configuration) {
|
||||
func (api GuacamoleApi) setAssetConfig(attributes map[string]string, s model.Session, configuration *guacamole.Configuration) {
|
||||
for key, value := range attributes {
|
||||
if guacd.DrivePath == key {
|
||||
if guacamole.DrivePath == key {
|
||||
// 忽略该参数
|
||||
continue
|
||||
}
|
||||
if guacd.EnableDrive == key && value == "true" {
|
||||
storageId := attributes[guacd.DrivePath]
|
||||
if guacamole.EnableDrive == key && value == "true" {
|
||||
storageId := attributes[guacamole.DrivePath]
|
||||
if storageId == "" || storageId == "-" {
|
||||
// 默认空间ID和用户ID相同
|
||||
storageId = s.Creator
|
||||
}
|
||||
realPath := path.Join(service.StorageService.GetBaseDrivePath(), storageId)
|
||||
configuration.SetParameter(guacd.EnableDrive, "true")
|
||||
configuration.SetParameter(guacd.DriveName, "Filesystem")
|
||||
configuration.SetParameter(guacd.DrivePath, realPath)
|
||||
log.Debugf("[%v] 会话 %v:%v 映射目录地址为 %v", s.ID, s.IP, s.Port, realPath)
|
||||
configuration.SetParameter(guacamole.EnableDrive, "true")
|
||||
configuration.SetParameter(guacamole.DriveName, "Filesystem")
|
||||
configuration.SetParameter(guacamole.DrivePath, realPath)
|
||||
} else {
|
||||
configuration.SetParameter(key, value)
|
||||
}
|
||||
@ -202,7 +203,7 @@ func (api GuacamoleApi) setAssetConfig(attributes map[string]string, s model.Ses
|
||||
func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||
if err != nil {
|
||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||
log.Warn("升级为WebSocket协议失败", log.NamedError("err", err))
|
||||
return err
|
||||
}
|
||||
ctx := context.TODO()
|
||||
@ -212,12 +213,12 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.Status != constant.Connected {
|
||||
utils.Disconnect(ws, AssetNotActive, "会话离线")
|
||||
if s.Status != nt.Connected {
|
||||
guacamole.Disconnect(ws, AssetNotActive, "会话离线")
|
||||
return nil
|
||||
}
|
||||
connectionId := s.ConnectionId
|
||||
configuration := guacd.NewConfiguration()
|
||||
configuration := guacamole.NewConfiguration()
|
||||
configuration.ConnectionID = connectionId
|
||||
sessionId = s.ID
|
||||
configuration.SetParameter("width", strconv.Itoa(s.Width))
|
||||
@ -225,13 +226,10 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
configuration.SetParameter("dpi", "96")
|
||||
|
||||
addr := config.GlobalCfg.Guacd.Hostname + ":" + strconv.Itoa(config.GlobalCfg.Guacd.Port)
|
||||
asset := fmt.Sprintf("%s:%s", configuration.GetParameter("hostname"), configuration.GetParameter("port"))
|
||||
log.Debugf("[%v] 新建 guacd 会话, guacd=%v, asset=%v", sessionId, addr, asset)
|
||||
|
||||
guacdTunnel, err := guacd.NewTunnel(addr, configuration)
|
||||
guacdTunnel, err := guacamole.NewTunnel(addr, configuration)
|
||||
if err != nil {
|
||||
utils.Disconnect(ws, NewTunnelError, err.Error())
|
||||
log.Printf("[%v] 建立连接失败: %v", sessionId, err.Error())
|
||||
guacamole.Disconnect(ws, NewTunnelError, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
@ -246,12 +244,11 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
// 要监控会话
|
||||
forObsSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if forObsSession == nil {
|
||||
utils.Disconnect(ws, NotFoundSession, "获取会话失败")
|
||||
guacamole.Disconnect(ws, NotFoundSession, "获取会话失败")
|
||||
return nil
|
||||
}
|
||||
nextSession.ID = utils.UUID()
|
||||
forObsSession.Observer.Add(nextSession)
|
||||
log.Debugf("[%v:%v] 观察者[%v]加入会话[%v]", sessionId, connectionId, nextSession.ID, s.ConnectionId)
|
||||
|
||||
guacamoleHandler := NewGuacamoleHandler(ws, guacdTunnel)
|
||||
guacamoleHandler.Start()
|
||||
@ -260,13 +257,11 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
for {
|
||||
_, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
log.Debugf("[%v:%v] WebSocket已关闭, %v", sessionId, connectionId, err.Error())
|
||||
// guacdTunnel.Read() 会阻塞,所以要先把guacdTunnel客户端关闭,才能退出Guacd循环
|
||||
_ = guacdTunnel.Close()
|
||||
|
||||
observerId := nextSession.ID
|
||||
forObsSession.Observer.Del(observerId)
|
||||
log.Debugf("[%v:%v] 观察者[%v]退出会话", sessionId, connectionId, observerId)
|
||||
return nil
|
||||
}
|
||||
_, err = guacdTunnel.WriteAndFlush(message)
|
||||
@ -277,12 +272,12 @@ func (api GuacamoleApi) GuacamoleMonitor(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session, configuration *guacd.Configuration) {
|
||||
if propertyMap[guacd.EnableRecording] == "true" {
|
||||
configuration.SetParameter(guacd.RecordingPath, path.Join(config.GlobalCfg.Guacd.Recording, s.ID))
|
||||
configuration.SetParameter(guacd.CreateRecordingPath, "true")
|
||||
func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session, configuration *guacamole.Configuration) {
|
||||
if propertyMap[guacamole.EnableRecording] == "true" {
|
||||
configuration.SetParameter(guacamole.RecordingPath, path.Join(config.GlobalCfg.Guacd.Recording, s.ID))
|
||||
configuration.SetParameter(guacamole.CreateRecordingPath, "true")
|
||||
} else {
|
||||
configuration.SetParameter(guacd.RecordingPath, "")
|
||||
configuration.SetParameter(guacamole.RecordingPath, "")
|
||||
}
|
||||
|
||||
configuration.Protocol = s.Protocol
|
||||
@ -295,18 +290,18 @@ func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session
|
||||
configuration.SetParameter("ignore-cert", "true")
|
||||
configuration.SetParameter("create-drive-path", "true")
|
||||
configuration.SetParameter("resize-method", "reconnect")
|
||||
configuration.SetParameter(guacd.EnableWallpaper, propertyMap[guacd.EnableWallpaper])
|
||||
configuration.SetParameter(guacd.EnableTheming, propertyMap[guacd.EnableTheming])
|
||||
configuration.SetParameter(guacd.EnableFontSmoothing, propertyMap[guacd.EnableFontSmoothing])
|
||||
configuration.SetParameter(guacd.EnableFullWindowDrag, propertyMap[guacd.EnableFullWindowDrag])
|
||||
configuration.SetParameter(guacd.EnableDesktopComposition, propertyMap[guacd.EnableDesktopComposition])
|
||||
configuration.SetParameter(guacd.EnableMenuAnimations, propertyMap[guacd.EnableMenuAnimations])
|
||||
configuration.SetParameter(guacd.DisableBitmapCaching, propertyMap[guacd.DisableBitmapCaching])
|
||||
configuration.SetParameter(guacd.DisableOffscreenCaching, propertyMap[guacd.DisableOffscreenCaching])
|
||||
configuration.SetParameter(guacd.ColorDepth, propertyMap[guacd.ColorDepth])
|
||||
configuration.SetParameter(guacd.ForceLossless, propertyMap[guacd.ForceLossless])
|
||||
configuration.SetParameter(guacd.PreConnectionId, propertyMap[guacd.PreConnectionId])
|
||||
configuration.SetParameter(guacd.PreConnectionBlob, propertyMap[guacd.PreConnectionBlob])
|
||||
configuration.SetParameter(guacamole.EnableWallpaper, propertyMap[guacamole.EnableWallpaper])
|
||||
configuration.SetParameter(guacamole.EnableTheming, propertyMap[guacamole.EnableTheming])
|
||||
configuration.SetParameter(guacamole.EnableFontSmoothing, propertyMap[guacamole.EnableFontSmoothing])
|
||||
configuration.SetParameter(guacamole.EnableFullWindowDrag, propertyMap[guacamole.EnableFullWindowDrag])
|
||||
configuration.SetParameter(guacamole.EnableDesktopComposition, propertyMap[guacamole.EnableDesktopComposition])
|
||||
configuration.SetParameter(guacamole.EnableMenuAnimations, propertyMap[guacamole.EnableMenuAnimations])
|
||||
configuration.SetParameter(guacamole.DisableBitmapCaching, propertyMap[guacamole.DisableBitmapCaching])
|
||||
configuration.SetParameter(guacamole.DisableOffscreenCaching, propertyMap[guacamole.DisableOffscreenCaching])
|
||||
configuration.SetParameter(guacamole.ColorDepth, propertyMap[guacamole.ColorDepth])
|
||||
configuration.SetParameter(guacamole.ForceLossless, propertyMap[guacamole.ForceLossless])
|
||||
configuration.SetParameter(guacamole.PreConnectionId, propertyMap[guacamole.PreConnectionId])
|
||||
configuration.SetParameter(guacamole.PreConnectionBlob, propertyMap[guacamole.PreConnectionBlob])
|
||||
case "ssh":
|
||||
if len(s.PrivateKey) > 0 && s.PrivateKey != "-" {
|
||||
configuration.SetParameter("username", s.Username)
|
||||
@ -317,11 +312,11 @@ func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session
|
||||
configuration.SetParameter("password", s.Password)
|
||||
}
|
||||
|
||||
configuration.SetParameter(guacd.FontSize, propertyMap[guacd.FontSize])
|
||||
configuration.SetParameter(guacd.FontName, propertyMap[guacd.FontName])
|
||||
configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme])
|
||||
configuration.SetParameter(guacd.Backspace, propertyMap[guacd.Backspace])
|
||||
configuration.SetParameter(guacd.TerminalType, propertyMap[guacd.TerminalType])
|
||||
configuration.SetParameter(guacamole.FontSize, propertyMap[guacamole.FontSize])
|
||||
configuration.SetParameter(guacamole.FontName, propertyMap[guacamole.FontName])
|
||||
configuration.SetParameter(guacamole.ColorScheme, propertyMap[guacamole.ColorScheme])
|
||||
configuration.SetParameter(guacamole.Backspace, propertyMap[guacamole.Backspace])
|
||||
configuration.SetParameter(guacamole.TerminalType, propertyMap[guacamole.TerminalType])
|
||||
case "vnc":
|
||||
configuration.SetParameter("username", s.Username)
|
||||
configuration.SetParameter("password", s.Password)
|
||||
@ -329,17 +324,17 @@ func (api GuacamoleApi) setConfig(propertyMap map[string]string, s model.Session
|
||||
configuration.SetParameter("username", s.Username)
|
||||
configuration.SetParameter("password", s.Password)
|
||||
|
||||
configuration.SetParameter(guacd.FontSize, propertyMap[guacd.FontSize])
|
||||
configuration.SetParameter(guacd.FontName, propertyMap[guacd.FontName])
|
||||
configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme])
|
||||
configuration.SetParameter(guacd.Backspace, propertyMap[guacd.Backspace])
|
||||
configuration.SetParameter(guacd.TerminalType, propertyMap[guacd.TerminalType])
|
||||
configuration.SetParameter(guacamole.FontSize, propertyMap[guacamole.FontSize])
|
||||
configuration.SetParameter(guacamole.FontName, propertyMap[guacamole.FontName])
|
||||
configuration.SetParameter(guacamole.ColorScheme, propertyMap[guacamole.ColorScheme])
|
||||
configuration.SetParameter(guacamole.Backspace, propertyMap[guacamole.Backspace])
|
||||
configuration.SetParameter(guacamole.TerminalType, propertyMap[guacamole.TerminalType])
|
||||
case "kubernetes":
|
||||
configuration.SetParameter(guacd.FontSize, propertyMap[guacd.FontSize])
|
||||
configuration.SetParameter(guacd.FontName, propertyMap[guacd.FontName])
|
||||
configuration.SetParameter(guacd.ColorScheme, propertyMap[guacd.ColorScheme])
|
||||
configuration.SetParameter(guacd.Backspace, propertyMap[guacd.Backspace])
|
||||
configuration.SetParameter(guacd.TerminalType, propertyMap[guacd.TerminalType])
|
||||
configuration.SetParameter(guacamole.FontSize, propertyMap[guacamole.FontSize])
|
||||
configuration.SetParameter(guacamole.FontName, propertyMap[guacamole.FontName])
|
||||
configuration.SetParameter(guacamole.ColorScheme, propertyMap[guacamole.ColorScheme])
|
||||
configuration.SetParameter(guacamole.Backspace, propertyMap[guacamole.Backspace])
|
||||
configuration.SetParameter(guacamole.TerminalType, propertyMap[guacamole.TerminalType])
|
||||
default:
|
||||
|
||||
}
|
||||
|
@ -2,22 +2,19 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/guacd"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/utils"
|
||||
"next-terminal/server/common/guacamole"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type GuacamoleHandler struct {
|
||||
ws *websocket.Conn
|
||||
tunnel *guacd.Tunnel
|
||||
tunnel *guacamole.Tunnel
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewGuacamoleHandler(ws *websocket.Conn, tunnel *guacd.Tunnel) *GuacamoleHandler {
|
||||
func NewGuacamoleHandler(ws *websocket.Conn, tunnel *guacamole.Tunnel) *GuacamoleHandler {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &GuacamoleHandler{
|
||||
ws: ws,
|
||||
@ -36,7 +33,7 @@ func (r GuacamoleHandler) Start() {
|
||||
default:
|
||||
instruction, err := r.tunnel.Read()
|
||||
if err != nil {
|
||||
utils.Disconnect(r.ws, TunnelClosed, "远程连接已关闭")
|
||||
guacamole.Disconnect(r.ws, TunnelClosed, "远程连接已关闭")
|
||||
return
|
||||
}
|
||||
if len(instruction) == 0 {
|
||||
@ -44,7 +41,6 @@ func (r GuacamoleHandler) Start() {
|
||||
}
|
||||
err = r.ws.WriteMessage(websocket.TextMessage, instruction)
|
||||
if err != nil {
|
||||
log.Debugf("WebSocket写入失败,即将关闭Guacd连接...")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -23,7 +24,7 @@ func (api JobApi) JobCreateEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := service.JobService.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
@ -45,7 +46,7 @@ func (api JobApi) JobPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -110,13 +111,17 @@ func (api JobApi) JobGetEndpoint(c echo.Context) error {
|
||||
|
||||
func (api JobApi) JobGetLogsEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
items, err := repository.JobLogRepository.FindByJobId(context.TODO(), id)
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
items, total, err := repository.JobLogRepository.FindByJobId(context.TODO(), id, pageIndex, pageSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, items)
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api JobApi) JobDeleteLogsEndpoint(c echo.Context) error {
|
||||
|
@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -26,7 +27,7 @@ func (api LoginLogApi) LoginLogPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
|
143
server/api/login_policy.go
Normal file
143
server/api/login_policy.go
Normal file
@ -0,0 +1,143 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type LoginPolicyApi struct{}
|
||||
|
||||
func (api LoginPolicyApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
userId := c.QueryParam("userId")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.LoginPolicyRepository.Find(context.TODO(), pageIndex, pageSize, name, userId, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) GetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
item, err := service.LoginPolicyService.FindById(context.Background(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) CreateEndpoint(c echo.Context) error {
|
||||
var item model.LoginPolicy
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
item.ID = utils.UUID()
|
||||
|
||||
if err := service.LoginPolicyService.Create(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) DeleteEndpoint(c echo.Context) error {
|
||||
ids := c.Param("id")
|
||||
split := strings.Split(ids, ",")
|
||||
if err := service.LoginPolicyService.DeleteByIds(context.Background(), split); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) UpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
var item model.LoginPolicy
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := service.LoginPolicyService.UpdateById(context.Background(), &item, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) GetUserPageEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
username := c.QueryParam("username")
|
||||
nickname := c.QueryParam("nickname")
|
||||
mail := c.QueryParam("mail")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, "", id, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) BindEndpoint(c echo.Context) error {
|
||||
var items []model.LoginPolicyUserRef
|
||||
if err := c.Bind(&items); err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if err := service.LoginPolicyService.Bind(context.Background(), id, items); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) UnbindEndpoint(c echo.Context) error {
|
||||
var items []model.LoginPolicyUserRef
|
||||
if err := c.Bind(&items); err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if err := service.LoginPolicyService.Unbind(context.Background(), id, items); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api LoginPolicyApi) GetUserIdEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
refs, err := repository.LoginPolicyUserRefRepository.FindByLoginPolicyId(context.Background(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var ids = make([]string, 0)
|
||||
for _, ref := range refs {
|
||||
ids = append(ids, ref.UserId)
|
||||
}
|
||||
|
||||
return Success(c, ids)
|
||||
}
|
@ -2,10 +2,11 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/common/nt"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/stat"
|
||||
"next-terminal/server/repository"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
@ -14,21 +15,27 @@ type OverviewApi struct{}
|
||||
|
||||
func (api OverviewApi) OverviewCounterEndPoint(c echo.Context) error {
|
||||
var (
|
||||
countUser int64
|
||||
countOnlineSession int64
|
||||
credential int64
|
||||
asset int64
|
||||
totalUser int64
|
||||
onlineUser int64
|
||||
countOfflineSession int64
|
||||
totalAsset int64
|
||||
activeAsset int64
|
||||
failLoginCount int64
|
||||
)
|
||||
countUser, _ = repository.UserRepository.CountOnlineUser(context.TODO())
|
||||
countOnlineSession, _ = repository.SessionRepository.CountOnlineSession(context.TODO())
|
||||
credential, _ = repository.CredentialRepository.Count(context.TODO())
|
||||
asset, _ = repository.AssetRepository.Count(context.TODO())
|
||||
totalUser, _ = repository.UserRepository.Count(context.TODO())
|
||||
onlineUser, _ = repository.UserRepository.CountOnlineUser(context.TODO())
|
||||
countOfflineSession, _ = repository.SessionRepository.CountOfflineSession(context.TODO())
|
||||
totalAsset, _ = repository.AssetRepository.Count(context.TODO())
|
||||
activeAsset, _ = repository.AssetRepository.CountByActive(context.TODO(), true)
|
||||
failLoginCount, _ = repository.LoginLogRepository.CountByState(context.TODO(), "0")
|
||||
|
||||
counter := dto.Counter{
|
||||
User: countUser,
|
||||
OnlineSession: countOnlineSession,
|
||||
Credential: credential,
|
||||
Asset: asset,
|
||||
TotalUser: totalUser,
|
||||
OnlineUser: onlineUser,
|
||||
OfflineSession: countOfflineSession,
|
||||
TotalAsset: totalAsset,
|
||||
ActiveAsset: activeAsset,
|
||||
FailLoginCount: failLoginCount,
|
||||
}
|
||||
|
||||
return Success(c, counter)
|
||||
@ -43,11 +50,11 @@ func (api OverviewApi) OverviewAssetEndPoint(c echo.Context) error {
|
||||
kubernetes int64
|
||||
)
|
||||
|
||||
ssh, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.SSH)
|
||||
rdp, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.RDP)
|
||||
vnc, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.VNC)
|
||||
telnet, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.Telnet)
|
||||
kubernetes, _ = repository.AssetRepository.CountByProtocol(context.TODO(), constant.K8s)
|
||||
ssh, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.SSH)
|
||||
rdp, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.RDP)
|
||||
vnc, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.VNC)
|
||||
telnet, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.Telnet)
|
||||
kubernetes, _ = repository.AssetRepository.CountByProtocol(context.TODO(), nt.K8s)
|
||||
|
||||
m := echo.Map{
|
||||
"ssh": ssh,
|
||||
@ -55,14 +62,168 @@ func (api OverviewApi) OverviewAssetEndPoint(c echo.Context) error {
|
||||
"vnc": vnc,
|
||||
"telnet": telnet,
|
||||
"kubernetes": kubernetes,
|
||||
"all": ssh + rdp + vnc + telnet + kubernetes,
|
||||
}
|
||||
return Success(c, m)
|
||||
}
|
||||
|
||||
func (api OverviewApi) OverviewAccessEndPoint(c echo.Context) error {
|
||||
access, err := repository.SessionRepository.OverviewAccess(context.TODO())
|
||||
func (api OverviewApi) OverviewDateCounterEndPoint(c echo.Context) error {
|
||||
d := c.QueryParam("d")
|
||||
var days = 7
|
||||
if d == "month" {
|
||||
days = 30
|
||||
}
|
||||
now := time.Now()
|
||||
lastDate := now.AddDate(0, 0, -days)
|
||||
// 最近一月登录次数
|
||||
loginLogCounters, err := repository.LoginLogRepository.CountWithGroupByLoginTime(context.TODO(), lastDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, access)
|
||||
// 最近一月活跃用户
|
||||
userCounters, err := repository.LoginLogRepository.CountWithGroupByLoginTimeAndUsername(context.TODO(), lastDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 最近一月活跃资产
|
||||
sessionCounters, err := repository.SessionRepository.CountWithGroupByLoginTime(context.TODO(), lastDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var counters []dto.DateCounter
|
||||
for i := 0; i < days; i++ {
|
||||
day := lastDate.AddDate(0, 0, i).Format("2006-01-02")
|
||||
|
||||
var exist = false
|
||||
for _, counter := range loginLogCounters {
|
||||
if counter.Date == day {
|
||||
exist = true
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "登录次数",
|
||||
Date: day,
|
||||
Value: counter.Value,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exist {
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "登录次数",
|
||||
Date: day,
|
||||
Value: 0,
|
||||
})
|
||||
}
|
||||
|
||||
exist = false
|
||||
for _, counter := range userCounters {
|
||||
if counter.Date == day {
|
||||
exist = true
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "活跃用户",
|
||||
Date: day,
|
||||
Value: counter.Value,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exist {
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "活跃用户",
|
||||
Date: day,
|
||||
Value: 0,
|
||||
})
|
||||
}
|
||||
|
||||
exist = false
|
||||
for _, counter := range sessionCounters {
|
||||
if counter.Date == day {
|
||||
exist = true
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "活跃资产",
|
||||
Date: day,
|
||||
Value: counter.Value,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exist {
|
||||
counters = append(counters, dto.DateCounter{
|
||||
Type: "活跃资产",
|
||||
Date: day,
|
||||
Value: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, counters)
|
||||
}
|
||||
|
||||
func (api OverviewApi) OverviewPS(c echo.Context) error {
|
||||
//memoryStat, err := mem.VirtualMemory()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//avgStat, err := load.Avg()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//cpuCount, err := cpu.Counts(true)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//percent, err := cpu.Percent(time.Second, false)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//var bytesRead uint64 = 0
|
||||
//var bytesWrite uint64 = 0
|
||||
//
|
||||
//diskIO, err := disk.IOCounters()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//for _, v := range diskIO {
|
||||
// bytesRead += v.ReadBytes
|
||||
// bytesWrite += v.WriteBytes
|
||||
//}
|
||||
//
|
||||
//var bytesSent uint64 = 0
|
||||
//var bytesRecv uint64 = 0
|
||||
//netIO, err := net.IOCounters(true)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//for _, v := range netIO {
|
||||
// bytesSent += v.BytesSent
|
||||
// bytesRecv += v.BytesRecv
|
||||
//}
|
||||
|
||||
//return Success(c, Map{
|
||||
// "mem": Map{
|
||||
// "total": memoryStat.Total,
|
||||
// "usedPercent": memoryStat.UsedPercent,
|
||||
// },
|
||||
// "cpu": Map{
|
||||
// "count": cpuCount,
|
||||
// "loadAvg": avgStat,
|
||||
// "usedPercent": percent[0],
|
||||
// },
|
||||
// "diskIO": Map{
|
||||
// "bytesRead": bytesRead,
|
||||
// "bytesWrite": bytesWrite,
|
||||
// },
|
||||
// "netIO": Map{
|
||||
// "bytesSent": bytesSent,
|
||||
// "bytesRecv": bytesRecv,
|
||||
// },
|
||||
//})
|
||||
|
||||
return Success(c, stat.SystemLoad)
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type ResourceSharerApi struct{}
|
||||
|
||||
func (api ResourceSharerApi) RSGetSharersEndPoint(c echo.Context) error {
|
||||
resourceId := c.QueryParam("resourceId")
|
||||
resourceType := c.QueryParam("resourceType")
|
||||
userId := c.QueryParam("userId")
|
||||
userGroupId := c.QueryParam("userGroupId")
|
||||
userIds, err := repository.ResourceSharerRepository.Find(context.TODO(), resourceId, resourceType, userId, userGroupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, userIds)
|
||||
}
|
||||
|
||||
func (api ResourceSharerApi) ResourceRemoveByUserIdAssignEndPoint(c echo.Context) error {
|
||||
var ru dto.RU
|
||||
if err := c.Bind(&ru); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repository.ResourceSharerRepository.DeleteByUserIdAndResourceTypeAndResourceIdIn(context.TODO(), ru.UserGroupId, ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api ResourceSharerApi) ResourceAddByUserIdAssignEndPoint(c echo.Context) error {
|
||||
var ru dto.RU
|
||||
if err := c.Bind(&ru); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := service.UserService.AddSharerResources(context.TODO(), ru.UserGroupId, ru.UserId, ru.StrategyId, ru.ResourceType, ru.ResourceIds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, "")
|
||||
}
|
100
server/api/role.go
Normal file
100
server/api/role.go
Normal file
@ -0,0 +1,100 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/service"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type RoleApi struct{}
|
||||
|
||||
func (api RoleApi) AllEndpoint(c echo.Context) error {
|
||||
items, err := repository.RoleRepository.FindAll(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, items)
|
||||
}
|
||||
|
||||
func (api RoleApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
_type := c.QueryParam("type")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.RoleRepository.Find(context.TODO(), pageIndex, pageSize, name, _type, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api RoleApi) GetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
item, err := service.RoleService.FindById(context.Background(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (api RoleApi) CreateEndpoint(c echo.Context) error {
|
||||
var item model.Role
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
item.ID = utils.UUID()
|
||||
item.Created = common.NowJsonTime()
|
||||
item.Deletable = true
|
||||
item.Modifiable = true
|
||||
item.Type = "new"
|
||||
|
||||
if err := service.RoleService.Create(context.Background(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api RoleApi) DeleteEndpoint(c echo.Context) error {
|
||||
ids := c.Param("id")
|
||||
split := strings.Split(ids, ",")
|
||||
if err := service.RoleService.DeleteByIds(context.Background(), split, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api RoleApi) UpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
var item model.Role
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := service.RoleService.UpdateById(context.Background(), &item, id, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api RoleApi) TreeMenus(c echo.Context) error {
|
||||
return Success(c, service.MenuService.GetTreeMenus())
|
||||
}
|
@ -2,7 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -54,7 +54,7 @@ func (api SecurityApi) SecurityPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
|
@ -13,9 +13,10 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"next-terminal/server/global/session"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
@ -44,10 +45,10 @@ func (api SessionApi) SessionPagingEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
for i := 0; i < len(items); i++ {
|
||||
if status == constant.Disconnected && len(items[i].Recording) > 0 {
|
||||
if status == nt.Disconnected && len(items[i].Recording) > 0 {
|
||||
|
||||
var recording string
|
||||
if items[i].Mode == constant.Native || items[i].Mode == constant.Terminal {
|
||||
if items[i].Mode == nt.Native || items[i].Mode == nt.Terminal {
|
||||
recording = items[i].Recording
|
||||
} else {
|
||||
recording = items[i].Recording + "/recording"
|
||||
@ -63,7 +64,7 @@ func (api SessionApi) SessionPagingEndpoint(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -71,7 +72,7 @@ func (api SessionApi) SessionPagingEndpoint(c echo.Context) error {
|
||||
|
||||
func (api SessionApi) SessionDeleteEndpoint(c echo.Context) error {
|
||||
sessionIds := strings.Split(c.Param("id"), ",")
|
||||
err := repository.SessionRepository.DeleteByIds(context.TODO(), sessionIds)
|
||||
err := service.SessionService.DeleteByIds(context.TODO(), sessionIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -115,8 +116,8 @@ func (api SessionApi) SessionConnectEndpoint(c echo.Context) error {
|
||||
|
||||
s := model.Session{}
|
||||
s.ID = sessionId
|
||||
s.Status = constant.Connected
|
||||
s.ConnectedTime = utils.NowJsonTime()
|
||||
s.Status = nt.Connected
|
||||
s.ConnectedTime = common.NowJsonTime()
|
||||
|
||||
if err := repository.SessionRepository.UpdateById(context.TODO(), &s, sessionId); err != nil {
|
||||
return err
|
||||
@ -170,10 +171,10 @@ func (api SessionApi) SessionCreateEndpoint(c echo.Context) error {
|
||||
assetId := c.QueryParam("assetId")
|
||||
mode := c.QueryParam("mode")
|
||||
|
||||
if mode == constant.Native {
|
||||
mode = constant.Native
|
||||
if mode == nt.Native {
|
||||
mode = nt.Native
|
||||
} else {
|
||||
mode = constant.Guacd
|
||||
mode = nt.Guacd
|
||||
}
|
||||
|
||||
user, _ := GetCurrentAccount(c)
|
||||
@ -221,6 +222,10 @@ func (api SessionApi) SessionUploadEndpoint(c echo.Context) error {
|
||||
remoteDir := c.QueryParam("dir")
|
||||
remoteFile := path.Join(remoteDir, filename)
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionUpload, remoteFile)
|
||||
|
||||
if "ssh" == s.Protocol {
|
||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if nextSession == nil {
|
||||
@ -313,6 +318,11 @@ func (api SessionApi) SessionDownloadEndpoint(c echo.Context) error {
|
||||
return errors.New("禁止操作")
|
||||
}
|
||||
file := c.QueryParam("file")
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionDownload, file)
|
||||
|
||||
// 获取带后缀的文件名称
|
||||
filenameWithSuffix := path.Base(file)
|
||||
if "ssh" == s.Protocol {
|
||||
@ -360,7 +370,6 @@ func (api SessionApi) SessionLsEndpoint(c echo.Context) error {
|
||||
if nextSession.NextTerminal.SftpClient == nil {
|
||||
sftpClient, err := sftp.NewClient(nextSession.NextTerminal.SshClient)
|
||||
if err != nil {
|
||||
log.Errorf("创建sftp客户端失败:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
nextSession.NextTerminal.SftpClient = sftpClient
|
||||
@ -374,18 +383,13 @@ func (api SessionApi) SessionLsEndpoint(c echo.Context) error {
|
||||
var files = make([]service.File, 0)
|
||||
for i := range fileInfos {
|
||||
|
||||
// 忽略隐藏文件
|
||||
if strings.HasPrefix(fileInfos[i].Name(), ".") {
|
||||
continue
|
||||
}
|
||||
|
||||
file := service.File{
|
||||
Name: fileInfos[i].Name(),
|
||||
Path: path.Join(remoteDir, fileInfos[i].Name()),
|
||||
IsDir: fileInfos[i].IsDir(),
|
||||
Mode: fileInfos[i].Mode().String(),
|
||||
IsLink: fileInfos[i].Mode()&os.ModeSymlink == os.ModeSymlink,
|
||||
ModTime: utils.NewJsonTime(fileInfos[i].ModTime()),
|
||||
ModTime: common.NewJsonTime(fileInfos[i].ModTime()),
|
||||
Size: fileInfos[i].Size(),
|
||||
}
|
||||
|
||||
@ -415,6 +419,11 @@ func (api SessionApi) SessionMkDirEndpoint(c echo.Context) error {
|
||||
return errors.New("禁止操作")
|
||||
}
|
||||
remoteDir := c.QueryParam("dir")
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionMkdir, remoteDir)
|
||||
|
||||
if "ssh" == s.Protocol {
|
||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if nextSession == nil {
|
||||
@ -445,6 +454,11 @@ func (api SessionApi) SessionRmEndpoint(c echo.Context) error {
|
||||
}
|
||||
// 文件夹或者文件
|
||||
file := c.FormValue("file")
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionRm, file)
|
||||
|
||||
if "ssh" == s.Protocol {
|
||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if nextSession == nil {
|
||||
@ -502,6 +516,11 @@ func (api SessionApi) SessionRenameEndpoint(c echo.Context) error {
|
||||
}
|
||||
oldName := c.QueryParam("oldName")
|
||||
newName := c.QueryParam("newName")
|
||||
|
||||
// 记录日志
|
||||
account, _ := GetCurrentAccount(c)
|
||||
_ = service.StorageLogService.Save(context.Background(), s.AssetId, sessionId, account.ID, nt.StorageLogActionRename, oldName)
|
||||
|
||||
if "ssh" == s.Protocol {
|
||||
nextSession := session.GlobalSessionManager.GetById(sessionId)
|
||||
if nextSession == nil {
|
||||
@ -533,15 +552,13 @@ func (api SessionApi) SessionRecordingEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
var recording string
|
||||
if s.Mode == constant.Native || s.Mode == constant.Terminal {
|
||||
if s.Mode == nt.Native || s.Mode == nt.Terminal {
|
||||
recording = s.Recording
|
||||
} else {
|
||||
recording = s.Recording + "/recording"
|
||||
}
|
||||
_ = repository.SessionRepository.UpdateReadByIds(context.TODO(), true, []string{sessionId})
|
||||
|
||||
log.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording))
|
||||
|
||||
http.ServeFile(c.Response(), c.Request(), recording)
|
||||
return nil
|
||||
}
|
||||
@ -570,8 +587,8 @@ func (api SessionApi) SessionStatsEndpoint(c echo.Context) error {
|
||||
if nextSession == nil {
|
||||
return errors.New("获取会话失败")
|
||||
}
|
||||
sshClient := nextSession.NextTerminal.SshClient
|
||||
stats, err := GetAllStats(sshClient)
|
||||
|
||||
stats, err := GetAllStats(nextSession)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
package api
|
||||
|
||||
func DealCommand(enterKeys []rune) {
|
||||
println(string(enterKeys))
|
||||
|
||||
}
|
@ -3,6 +3,8 @@ package api
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"next-terminal/server/common/taskrunner"
|
||||
"next-terminal/server/global/session"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -70,40 +72,56 @@ type Stat struct {
|
||||
CPU CPU `json:"cpu"`
|
||||
}
|
||||
|
||||
func GetAllStats(client *ssh.Client) (*Stat, error) {
|
||||
func GetAllStats(nextSession *session.Session) (*Stat, error) {
|
||||
client := nextSession.NextTerminal.SshClient
|
||||
start := time.Now()
|
||||
stats := &Stat{}
|
||||
if err := getUptime(client, stats); err != nil {
|
||||
return nil, err
|
||||
|
||||
stats := &Stat{
|
||||
Uptime: nextSession.Uptime,
|
||||
Hostname: nextSession.Hostname,
|
||||
}
|
||||
if err := getHostname(client, stats); err != nil {
|
||||
return nil, err
|
||||
if stats.Uptime == 0 {
|
||||
if err := getUptime(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nextSession.Uptime = stats.Uptime
|
||||
}
|
||||
if err := getLoad(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getMem(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getFileSystems(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getInterfaces(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getInterfaceInfo(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := getCPU(client, stats); err != nil {
|
||||
return nil, err
|
||||
|
||||
if stats.Hostname == "" {
|
||||
if err := getHostname(client, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nextSession.Hostname = stats.Hostname
|
||||
}
|
||||
|
||||
runner := taskrunner.Runner{}
|
||||
|
||||
runner.Add(func() error {
|
||||
return getLoad(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getMem(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getFileSystems(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getInterfaces(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getInterfaceInfo(client, stats)
|
||||
})
|
||||
runner.Add(func() error {
|
||||
return getCPU(client, stats)
|
||||
})
|
||||
runner.Wait()
|
||||
cost := time.Since(start)
|
||||
fmt.Printf("%s: %v\n", "GetAllStats", cost)
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func getHostname(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getHostname")
|
||||
defer utils.TimeWatcher("getHostname")
|
||||
hostname, err := utils.RunCommand(client, "/bin/hostname -f")
|
||||
if err != nil {
|
||||
return
|
||||
@ -113,7 +131,7 @@ func getHostname(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getUptime(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getUptime")
|
||||
defer utils.TimeWatcher("getUptime")
|
||||
uptime, err := utils.RunCommand(client, "/bin/cat /proc/uptime")
|
||||
if err != nil {
|
||||
return
|
||||
@ -132,7 +150,7 @@ func getUptime(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getLoad(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getLoad")
|
||||
defer utils.TimeWatcher("getLoad")
|
||||
line, err := utils.RunCommand(client, "/bin/cat /proc/loadavg")
|
||||
if err != nil {
|
||||
return
|
||||
@ -154,7 +172,7 @@ func getLoad(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getMem(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getMem")
|
||||
defer utils.TimeWatcher("getMem")
|
||||
lines, err := utils.RunCommand(client, "/bin/cat /proc/meminfo")
|
||||
if err != nil {
|
||||
return
|
||||
@ -192,7 +210,7 @@ func getMem(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getFileSystems(client *ssh.Client, stat *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getFileSystems")
|
||||
defer utils.TimeWatcher("getFileSystems")
|
||||
lines, err := utils.RunCommand(client, "/bin/df -B1")
|
||||
if err != nil {
|
||||
return
|
||||
@ -228,7 +246,7 @@ func getFileSystems(client *ssh.Client, stat *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getInterfaces(client *ssh.Client, stats *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getInterfaces")
|
||||
defer utils.TimeWatcher("getInterfaces")
|
||||
var lines string
|
||||
lines, err = utils.RunCommand(client, "/bin/ip -o addr")
|
||||
if err != nil {
|
||||
@ -273,16 +291,16 @@ func getInterfaces(client *ssh.Client, stats *Stat) (err error) {
|
||||
}
|
||||
|
||||
func getInterfaceInfo(client *ssh.Client, stats *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getInterfaceInfo")
|
||||
lines, err := utils.RunCommand(client, "/bin/cat /proc/net/dev")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer utils.TimeWatcher("getInterfaceInfo")
|
||||
|
||||
if stats.Network == nil {
|
||||
return
|
||||
} // should have been here already
|
||||
|
||||
lines, err := utils.RunCommand(client, "/bin/cat /proc/net/dev")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(strings.NewReader(lines))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
@ -345,7 +363,7 @@ func parseCPUFields(fields []string, stat *cpuRaw) {
|
||||
var preCPU cpuRaw
|
||||
|
||||
func getCPU(client *ssh.Client, stats *Stat) (err error) {
|
||||
//defer utils.TimeWatcher("getCPU")
|
||||
defer utils.TimeWatcher("getCPU")
|
||||
lines, err := utils.RunCommand(client, "/bin/cat /proc/stat")
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -3,12 +3,14 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
@ -44,7 +46,7 @@ func (api StorageApi) StoragePagingEndpoint(c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -59,7 +61,7 @@ func (api StorageApi) StorageCreateEndpoint(c echo.Context) error {
|
||||
account, _ := GetCurrentAccount(c)
|
||||
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
item.Owner = account.ID
|
||||
// 创建对应的目录文件夹
|
||||
drivePath := service.StorageService.GetBaseDrivePath()
|
||||
@ -147,7 +149,7 @@ func (api StorageApi) PermissionCheck(c echo.Context, id string) error {
|
||||
return err
|
||||
}
|
||||
account, _ := GetCurrentAccount(c)
|
||||
if account.Type != constant.TypeAdmin {
|
||||
if account.Type != nt.TypeAdmin {
|
||||
if storage.Owner != account.ID {
|
||||
return errors.New("您没有权限访问此地址 :(")
|
||||
}
|
||||
|
48
server/api/storage_log.go
Normal file
48
server/api/storage_log.go
Normal file
@ -0,0 +1,48 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/repository"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type StorageLogApi struct {
|
||||
}
|
||||
|
||||
func (api StorageLogApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
assetId := c.QueryParam("assetId")
|
||||
userId := c.QueryParam("userId")
|
||||
action := c.QueryParam("action")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.StorageLogRepository.Find(context.TODO(), pageIndex, pageSize, assetId, userId, action, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api StorageLogApi) DeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
if err := repository.StorageLogRepository.DeleteById(context.Background(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api StorageLogApi) ClearEndpoint(c echo.Context) error {
|
||||
if err := repository.StorageLogRepository.DeleteAll(context.Background()); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
@ -2,7 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -36,7 +37,7 @@ func (api StrategyApi) StrategyPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -48,7 +49,7 @@ func (api StrategyApi) StrategyCreateEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
item.ID = utils.UUID()
|
||||
item.Created = utils.NowJsonTime()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.StrategyRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
@ -80,3 +81,12 @@ func (api StrategyApi) StrategyUpdateEndpoint(c echo.Context) error {
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api StrategyApi) GetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
strategy, err := repository.StrategyRepository.FindById(context.Background(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, strategy)
|
||||
}
|
||||
|
83
server/api/tenant.go
Normal file
83
server/api/tenant.go
Normal file
@ -0,0 +1,83 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type TenantApi struct{}
|
||||
|
||||
func (api TenantApi) AllEndpoint(c echo.Context) error {
|
||||
items, err := repository.TenantRepository.FindAll(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, items)
|
||||
}
|
||||
|
||||
func (api TenantApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
items, total, err := repository.TenantRepository.Find(context.TODO(), pageIndex, pageSize, name, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api TenantApi) CreateEndpoint(c echo.Context) error {
|
||||
var item model.Tenant
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
item.ID = utils.UUID()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.TenantRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (api TenantApi) DeleteEndpoint(c echo.Context) error {
|
||||
ids := c.Param("id")
|
||||
split := strings.Split(ids, ",")
|
||||
for i := range split {
|
||||
id := split[i]
|
||||
if err := repository.TenantRepository.DeleteById(context.TODO(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (api TenantApi) UpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
var item model.Tenant
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repository.TenantRepository.UpdateById(context.TODO(), &item, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, "")
|
||||
}
|
@ -4,20 +4,18 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"next-terminal/server/common/nt"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"next-terminal/server/common/guacamole"
|
||||
"next-terminal/server/common/term"
|
||||
"next-terminal/server/config"
|
||||
"next-terminal/server/constant"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/session"
|
||||
"next-terminal/server/guacd"
|
||||
"next-terminal/server/log"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"next-terminal/server/term"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
@ -38,7 +36,6 @@ type WebTerminalApi struct {
|
||||
func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||
if err != nil {
|
||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
@ -86,7 +83,7 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
|
||||
recording := ""
|
||||
var isRecording = false
|
||||
property, err := repository.PropertyRepository.FindByName(ctx, guacd.EnableRecording)
|
||||
property, err := repository.PropertyRepository.FindByName(ctx, guacamole.EnableRecording)
|
||||
if err == nil && property.Value == "true" {
|
||||
isRecording = true
|
||||
}
|
||||
@ -102,8 +99,8 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
|
||||
var xterm = "xterm-256color"
|
||||
var nextTerminal *term.NextTerminal
|
||||
if "true" == attributes[constant.SocksProxyEnable] {
|
||||
nextTerminal, err = term.NewNextTerminalUseSocks(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true, attributes[constant.SocksProxyHost], attributes[constant.SocksProxyPort], attributes[constant.SocksProxyUsername], attributes[constant.SocksProxyPassword])
|
||||
if "true" == attributes[nt.SocksProxyEnable] {
|
||||
nextTerminal, err = term.NewNextTerminalUseSocks(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true, attributes[nt.SocksProxyHost], attributes[nt.SocksProxyPort], attributes[nt.SocksProxyUsername], attributes[nt.SocksProxyPassword])
|
||||
} else {
|
||||
nextTerminal, err = term.NewNextTerminal(ip, port, username, password, privateKey, passphrase, rows, cols, recording, xterm, true)
|
||||
}
|
||||
@ -120,20 +117,19 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := model.Session{
|
||||
sessionForUpdate := model.Session{
|
||||
ConnectionId: sessionId,
|
||||
Width: cols,
|
||||
Height: rows,
|
||||
Status: constant.Connecting,
|
||||
Status: nt.Connecting,
|
||||
Recording: recording,
|
||||
}
|
||||
if sess.Recording == "" {
|
||||
if sessionForUpdate.Recording == "" {
|
||||
// 未录屏时无需审计
|
||||
sess.Reviewed = true
|
||||
sessionForUpdate.Reviewed = true
|
||||
}
|
||||
// 创建新会话
|
||||
log.Debugf("创建新会话 %v", sess.ConnectionId)
|
||||
if err := repository.SessionRepository.UpdateById(ctx, &sess, sessionId); err != nil {
|
||||
if err := repository.SessionRepository.UpdateById(ctx, &sessionForUpdate, sessionId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -152,7 +148,7 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
}
|
||||
session.GlobalSessionManager.Add(nextSession)
|
||||
|
||||
termHandler := NewTermHandler(sessionId, isRecording, ws, nextTerminal)
|
||||
termHandler := NewTermHandler(s.Creator, s.AssetId, sessionId, isRecording, ws, nextTerminal)
|
||||
termHandler.Start()
|
||||
defer termHandler.Stop()
|
||||
|
||||
@ -160,14 +156,12 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
_, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
// web socket会话关闭后主动关闭ssh会话
|
||||
log.Debugf("WebSocket已关闭")
|
||||
service.SessionService.CloseSessionById(sessionId, Normal, "用户正常退出")
|
||||
break
|
||||
}
|
||||
|
||||
msg, err := dto.ParseMessage(string(message))
|
||||
if err != nil {
|
||||
log.Warnf("消息解码失败: %v, 原始字符串:%v", err, string(message))
|
||||
continue
|
||||
}
|
||||
|
||||
@ -175,31 +169,28 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
case Resize:
|
||||
decodeString, err := base64.StdEncoding.DecodeString(msg.Content)
|
||||
if err != nil {
|
||||
log.Warnf("Base64解码失败: %v,原始字符串:%v", err, msg.Content)
|
||||
continue
|
||||
}
|
||||
var winSize dto.WindowSize
|
||||
err = json.Unmarshal(decodeString, &winSize)
|
||||
if err != nil {
|
||||
log.Warnf("解析SSH会话窗口大小失败: %v,原始字符串:%v", err, msg.Content)
|
||||
continue
|
||||
}
|
||||
if err := nextTerminal.WindowChange(winSize.Rows, winSize.Cols); err != nil {
|
||||
log.Warnf("更改SSH会话窗口大小失败: %v", err)
|
||||
if err := termHandler.WindowChange(winSize.Rows, winSize.Cols); err != nil {
|
||||
}
|
||||
_ = repository.SessionRepository.UpdateWindowSizeById(ctx, winSize.Rows, winSize.Cols, sessionId)
|
||||
case Data:
|
||||
input := []byte(msg.Content)
|
||||
_, err := nextTerminal.Write(input)
|
||||
err := termHandler.Write(input)
|
||||
if err != nil {
|
||||
service.SessionService.CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
||||
}
|
||||
case Ping:
|
||||
_, _, err := nextTerminal.SshClient.Conn.SendRequest("helloworld1024@foxmail.com", true, nil)
|
||||
err := termHandler.SendRequest()
|
||||
if err != nil {
|
||||
service.SessionService.CloseSessionById(sessionId, TunnelClosed, "远程连接已关闭")
|
||||
} else {
|
||||
_ = termHandler.WriteMessage(dto.NewMessage(Ping, ""))
|
||||
_ = termHandler.SendMessageToWebSocket(dto.NewMessage(Ping, ""))
|
||||
}
|
||||
|
||||
}
|
||||
@ -210,7 +201,6 @@ func (api WebTerminalApi) SshEndpoint(c echo.Context) error {
|
||||
func (api WebTerminalApi) SshMonitorEndpoint(c echo.Context) error {
|
||||
ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil)
|
||||
if err != nil {
|
||||
log.Errorf("升级为WebSocket协议失败:%v", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
@ -238,12 +228,10 @@ func (api WebTerminalApi) SshMonitorEndpoint(c echo.Context) error {
|
||||
WebSocket: ws,
|
||||
}
|
||||
nextSession.Observer.Add(obSession)
|
||||
log.Debugf("会话 %v 观察者 %v 进入", sessionId, obId)
|
||||
|
||||
for {
|
||||
_, _, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
log.Debugf("会话 %v 观察者 %v 退出", sessionId, obId)
|
||||
nextSession.Observer.Del(obId)
|
||||
break
|
||||
}
|
||||
@ -253,16 +241,16 @@ func (api WebTerminalApi) SshMonitorEndpoint(c echo.Context) error {
|
||||
|
||||
func (api WebTerminalApi) permissionCheck(c echo.Context, assetId string) error {
|
||||
user, _ := GetCurrentAccount(c)
|
||||
if constant.TypeUser == user.Type {
|
||||
// 检测是否有访问权限
|
||||
assetIds, err := repository.ResourceSharerRepository.FindAssetIdsByUserId(context.TODO(), user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !utils.Contains(assetIds, assetId) {
|
||||
return errors.New("您没有权限访问此资产")
|
||||
}
|
||||
if nt.TypeUser == user.Type {
|
||||
// 检测是否有访问权限 TODO
|
||||
//assetIds, err := repository.ResourceSharerRepository.FindAssetIdsByUserId(context.TODO(), user.ID)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//
|
||||
//if !utils.Contains(assetIds, assetId) {
|
||||
// return errors.New("您没有权限访问此资产")
|
||||
//}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"next-terminal/server/common/term"
|
||||
"next-terminal/server/dto"
|
||||
"next-terminal/server/global/session"
|
||||
"next-terminal/server/term"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type TermHandler struct {
|
||||
@ -23,11 +23,13 @@ type TermHandler struct {
|
||||
dataChan chan rune
|
||||
tick *time.Ticker
|
||||
mutex sync.Mutex
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func NewTermHandler(sessionId string, isRecording bool, ws *websocket.Conn, nextTerminal *term.NextTerminal) *TermHandler {
|
||||
func NewTermHandler(userId, assetId, sessionId string, isRecording bool, ws *websocket.Conn, nextTerminal *term.NextTerminal) *TermHandler {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
||||
|
||||
return &TermHandler{
|
||||
sessionId: sessionId,
|
||||
isRecording: isRecording,
|
||||
@ -46,6 +48,7 @@ func (r *TermHandler) Start() {
|
||||
}
|
||||
|
||||
func (r *TermHandler) Stop() {
|
||||
// 会话结束时记录最后一个命令
|
||||
r.tick.Stop()
|
||||
r.cancel()
|
||||
}
|
||||
@ -68,38 +71,50 @@ func (r *TermHandler) readFormTunnel() {
|
||||
}
|
||||
|
||||
func (r *TermHandler) writeToWebsocket() {
|
||||
var buf []byte
|
||||
for {
|
||||
select {
|
||||
case <-r.ctx.Done():
|
||||
return
|
||||
case <-r.tick.C:
|
||||
if len(buf) > 0 {
|
||||
s := string(buf)
|
||||
if err := r.WriteMessage(dto.NewMessage(Data, s)); err != nil {
|
||||
return
|
||||
}
|
||||
// 录屏
|
||||
if r.isRecording {
|
||||
_ = r.nextTerminal.Recorder.WriteData(s)
|
||||
}
|
||||
// 监控
|
||||
SendObData(r.sessionId, s)
|
||||
buf = []byte{}
|
||||
s := r.buf.String()
|
||||
if err := r.SendMessageToWebSocket(dto.NewMessage(Data, s)); err != nil {
|
||||
return
|
||||
}
|
||||
// 录屏
|
||||
if r.isRecording {
|
||||
_ = r.nextTerminal.Recorder.WriteData(s)
|
||||
}
|
||||
// 监控
|
||||
SendObData(r.sessionId, s)
|
||||
r.buf.Reset()
|
||||
case data := <-r.dataChan:
|
||||
if data != utf8.RuneError {
|
||||
p := make([]byte, utf8.RuneLen(data))
|
||||
utf8.EncodeRune(p, data)
|
||||
buf = append(buf, p...)
|
||||
r.buf.Write(p)
|
||||
} else {
|
||||
buf = append(buf, []byte("@")...)
|
||||
r.buf.Write([]byte("@"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TermHandler) WriteMessage(msg dto.Message) error {
|
||||
func (r *TermHandler) Write(input []byte) error {
|
||||
// 正常的字符输入
|
||||
_, err := r.nextTerminal.Write(input)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *TermHandler) WindowChange(h int, w int) error {
|
||||
return r.nextTerminal.WindowChange(h, w)
|
||||
}
|
||||
|
||||
func (r *TermHandler) SendRequest() error {
|
||||
_, _, err := r.nextTerminal.SshClient.Conn.SendRequest("helloworld1024@foxmail.com", true, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *TermHandler) SendMessageToWebSocket(msg dto.Message) error {
|
||||
if r.webSocket == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
)
|
||||
|
||||
type pepper struct {
|
||||
Name string
|
||||
HeatUnit int
|
||||
Peppers int
|
||||
}
|
||||
|
||||
func main() {
|
||||
peppers := []pepper{
|
||||
{Name: "Bell Pepper", HeatUnit: 0, Peppers: 0},
|
||||
{Name: "Banana Pepper", HeatUnit: 100, Peppers: 1},
|
||||
{Name: "Poblano", HeatUnit: 1000, Peppers: 2},
|
||||
{Name: "Jalapeño", HeatUnit: 3500, Peppers: 3},
|
||||
{Name: "Aleppo", HeatUnit: 10000, Peppers: 4},
|
||||
{Name: "Tabasco", HeatUnit: 30000, Peppers: 5},
|
||||
{Name: "Malagueta", HeatUnit: 50000, Peppers: 6},
|
||||
{Name: "Habanero", HeatUnit: 100000, Peppers: 7},
|
||||
{Name: "Red Savina Habanero", HeatUnit: 350000, Peppers: 8},
|
||||
{Name: "Dragon’s Breath", HeatUnit: 855000, Peppers: 9},
|
||||
}
|
||||
|
||||
templates := &promptui.SelectTemplates{
|
||||
Label: "{{ . }}?",
|
||||
Active: "\U0001F336 {{ .Name | cyan }} ({{ .HeatUnit | red }})",
|
||||
Inactive: " {{ .Name | cyan }} ({{ .HeatUnit | red }})",
|
||||
Selected: "\U0001F336 {{ .Name | red | cyan }}",
|
||||
Details: `
|
||||
--------- Pepper ----------
|
||||
{{ "Name:" | faint }} {{ .Name }}/
|
||||
{{ "Heat Unit:" | faint }} {{ .HeatUnit }}
|
||||
{{ "Peppers:" | faint }} {{ .Peppers }}`,
|
||||
}
|
||||
|
||||
searcher := func(input string, index int) bool {
|
||||
pepper := peppers[index]
|
||||
name := strings.Replace(strings.ToLower(pepper.Name), " ", "", -1)
|
||||
input = strings.Replace(strings.ToLower(input), " ", "", -1)
|
||||
|
||||
return strings.Contains(name, input)
|
||||
}
|
||||
|
||||
prompt := promptui.Select{
|
||||
Label: "Spicy Level",
|
||||
Items: peppers,
|
||||
Templates: templates,
|
||||
Size: 4,
|
||||
Searcher: searcher,
|
||||
}
|
||||
|
||||
i, _, err := prompt.Run()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Prompt failed %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("You choose number %d: %s\n", i+1, peppers[i].Name)
|
||||
}
|
@ -2,21 +2,19 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"next-terminal/server/common/maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type UserApi struct{}
|
||||
|
||||
func (userApi UserApi) UserCreateEndpoint(c echo.Context) (err error) {
|
||||
func (userApi UserApi) CreateEndpoint(c echo.Context) (err error) {
|
||||
var item model.User
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
@ -29,7 +27,7 @@ func (userApi UserApi) UserCreateEndpoint(c echo.Context) (err error) {
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserPagingEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
username := c.QueryParam("username")
|
||||
@ -38,25 +36,26 @@ func (userApi UserApi) UserPagingEndpoint(c echo.Context) error {
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
online := c.QueryParam("online")
|
||||
|
||||
items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, order, field)
|
||||
items, total, err := repository.UserRepository.Find(context.TODO(), pageIndex, pageSize, username, nickname, mail, online, "", order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserUpdateEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) UpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
account, _ := GetCurrentAccount(c)
|
||||
if account.ID == id {
|
||||
return Fail(c, -1, "cannot modify itself")
|
||||
}
|
||||
//account, _ := GetCurrentAccount(c)
|
||||
//if account.ID == id {
|
||||
// return Fail(c, -1, "cannot modify itself")
|
||||
//}
|
||||
|
||||
var item model.User
|
||||
if err := c.Bind(&item); err != nil {
|
||||
@ -70,7 +69,7 @@ func (userApi UserApi) UserUpdateEndpoint(c echo.Context) error {
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserUpdateStatusEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) UpdateStatusEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
status := c.QueryParam("status")
|
||||
account, _ := GetCurrentAccount(c)
|
||||
@ -85,7 +84,7 @@ func (userApi UserApi) UserUpdateStatusEndpoint(c echo.Context) error {
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserDeleteEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) DeleteEndpoint(c echo.Context) error {
|
||||
ids := c.Param("id")
|
||||
account, found := GetCurrentAccount(c)
|
||||
if !found {
|
||||
@ -105,10 +104,10 @@ func (userApi UserApi) UserDeleteEndpoint(c echo.Context) error {
|
||||
return Success(c, nil)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserGetEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) GetEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
item, err := repository.UserRepository.FindById(context.TODO(), id)
|
||||
item, err := service.UserService.FindById(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -116,49 +115,41 @@ func (userApi UserApi) UserGetEndpoint(c echo.Context) error {
|
||||
return Success(c, item)
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserChangePasswordEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) ChangePasswordEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
password := c.FormValue("password")
|
||||
if password == "" {
|
||||
return Fail(c, -1, "请输入密码")
|
||||
}
|
||||
|
||||
user, err := repository.UserRepository.FindById(context.TODO(), id)
|
||||
if err != nil {
|
||||
ids := strings.Split(id, ",")
|
||||
if err := service.UserService.ChangePassword(ids, password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
passwd, err := utils.Encoder.Encode([]byte(password))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u := &model.User{
|
||||
Password: string(passwd),
|
||||
ID: id,
|
||||
}
|
||||
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.Mail != "" {
|
||||
subject := "密码修改通知"
|
||||
text := fmt.Sprintf(`您好,%s。
|
||||
管理员已将你的密码修改为:%s。
|
||||
`, user.Username, password)
|
||||
go service.MailService.SendMail(user.Mail, subject, text)
|
||||
}
|
||||
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (userApi UserApi) UserResetTotpEndpoint(c echo.Context) error {
|
||||
func (userApi UserApi) ResetTotpEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
u := &model.User{
|
||||
TOTPSecret: "-",
|
||||
ID: id,
|
||||
}
|
||||
if err := repository.UserRepository.Update(context.TODO(), u); err != nil {
|
||||
ids := strings.Split(id, ",")
|
||||
if err := service.UserService.ResetTotp(ids); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, "")
|
||||
}
|
||||
|
||||
func (userApi UserApi) AllEndpoint(c echo.Context) error {
|
||||
users, err := repository.UserRepository.FindAll(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items := make([]maps.Map, len(users))
|
||||
for i, user := range users {
|
||||
items[i] = maps.Map{
|
||||
"id": user.ID,
|
||||
"nickname": user.Nickname,
|
||||
}
|
||||
}
|
||||
return Success(c, items)
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/model"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -15,7 +17,7 @@ import (
|
||||
type UserGroupApi struct{}
|
||||
|
||||
func (userGroupApi UserGroupApi) UserGroupCreateEndpoint(c echo.Context) error {
|
||||
var item dto.UserGroup
|
||||
var item model.UserGroup
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -40,7 +42,7 @@ func (userGroupApi UserGroupApi) UserGroupPagingEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Success(c, Map{
|
||||
return Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
@ -49,7 +51,7 @@ func (userGroupApi UserGroupApi) UserGroupPagingEndpoint(c echo.Context) error {
|
||||
func (userGroupApi UserGroupApi) UserGroupUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
var item dto.UserGroup
|
||||
var item model.UserGroup
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -82,7 +84,7 @@ func (userGroupApi UserGroupApi) UserGroupGetEndpoint(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
members, err := repository.UserGroupMemberRepository.FindUserIdsByUserGroupId(context.TODO(), id)
|
||||
members, err := repository.UserGroupMemberRepository.FindByUserGroupId(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -90,8 +92,17 @@ func (userGroupApi UserGroupApi) UserGroupGetEndpoint(c echo.Context) error {
|
||||
userGroup := dto.UserGroup{
|
||||
Id: item.ID,
|
||||
Name: item.Name,
|
||||
Created: item.Created,
|
||||
Members: members,
|
||||
}
|
||||
|
||||
return Success(c, userGroup)
|
||||
}
|
||||
|
||||
func (userGroupApi UserGroupApi) UserGroupAllEndpoint(c echo.Context) error {
|
||||
userGroups, err := repository.UserGroupRepository.FindAll(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Success(c, userGroups)
|
||||
}
|
49
server/api/worker/asset.go
Normal file
49
server/api/worker/asset.go
Normal file
@ -0,0 +1,49 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/labstack/echo/v4"
|
||||
"next-terminal/server/api/abi"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/service"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type WorkAssetApi struct {
|
||||
abi.Abi
|
||||
}
|
||||
|
||||
func (api WorkAssetApi) PagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
protocol := c.QueryParam("protocol")
|
||||
tags := c.QueryParam("tags")
|
||||
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
|
||||
items, total, err := service.WorkerService.FindMyAssetPaging(pageIndex, pageSize, name, protocol, tags, account.ID, order, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range items {
|
||||
items[i].IP = ""
|
||||
items[i].Port = 0
|
||||
}
|
||||
|
||||
return api.Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api WorkAssetApi) TagsEndpoint(c echo.Context) (err error) {
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
var items []string
|
||||
if items, err = service.WorkerService.FindMyAssetTags(context.TODO(), account.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return api.Success(c, items)
|
||||
}
|
132
server/api/worker/command.go
Normal file
132
server/api/worker/command.go
Normal file
@ -0,0 +1,132 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"next-terminal/server/common/nt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/api/abi"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/utils"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type WorkCommandApi struct {
|
||||
abi.Abi
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandCreateEndpoint(c echo.Context) error {
|
||||
var item model.Command
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
item.Owner = account.ID
|
||||
item.ID = utils.UUID()
|
||||
item.Created = common.NowJsonTime()
|
||||
|
||||
if err := repository.CommandRepository.Create(context.TODO(), &item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.Success(c, item)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandAllEndpoint(c echo.Context) error {
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
userId := account.ID
|
||||
items, err := repository.CommandRepository.FindByUserId(context.Background(), userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return api.Success(c, items)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandPagingEndpoint(c echo.Context) error {
|
||||
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||
name := c.QueryParam("name")
|
||||
content := c.QueryParam("content")
|
||||
order := c.QueryParam("order")
|
||||
field := c.QueryParam("field")
|
||||
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
userId := account.ID
|
||||
|
||||
items, total, err := repository.CommandRepository.WorkerFind(context.TODO(), pageIndex, pageSize, name, content, order, field, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.Success(c, maps.Map{
|
||||
"total": total,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandUpdateEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
if !api.checkPermission(c, id) {
|
||||
return nt.ErrPermissionDenied
|
||||
}
|
||||
|
||||
var item model.Command
|
||||
if err := c.Bind(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repository.CommandRepository.UpdateById(context.TODO(), &item, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.Success(c, nil)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandDeleteEndpoint(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
split := strings.Split(id, ",")
|
||||
for i := range split {
|
||||
if !api.checkPermission(c, id) {
|
||||
return nt.ErrPermissionDenied
|
||||
}
|
||||
if err := repository.CommandRepository.DeleteById(context.TODO(), split[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return api.Success(c, nil)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) CommandGetEndpoint(c echo.Context) (err error) {
|
||||
id := c.Param("id")
|
||||
if !api.checkPermission(c, id) {
|
||||
return nt.ErrPermissionDenied
|
||||
}
|
||||
var item model.Command
|
||||
if item, err = repository.CommandRepository.FindById(context.TODO(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
return api.Success(c, item)
|
||||
}
|
||||
|
||||
func (api WorkCommandApi) checkPermission(c echo.Context, commandId string) bool {
|
||||
command, err := repository.CommandRepository.FindById(context.Background(), commandId)
|
||||
if err != nil {
|
||||
if errors.Is(gorm.ErrRecordNotFound, err) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
account, _ := api.GetCurrentAccount(c)
|
||||
userId := account.ID
|
||||
|
||||
return command.Owner == userId
|
||||
}
|
Reference in New Issue
Block a user