mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-04 05:00:25 +08:00
优化系统用户登录校验
This commit is contained in:
3
go.mod
3
go.mod
@@ -9,9 +9,10 @@ require (
|
|||||||
github.com/cespare/xxhash v1.1.0
|
github.com/cespare/xxhash v1.1.0
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||||
github.com/iwind/TeaGo v0.0.0-20201120063500-ee2d7090f4bc
|
github.com/iwind/TeaGo v0.0.0-20201206115018-cdd967bfb13d
|
||||||
github.com/tealeg/xlsx/v3 v3.2.3
|
github.com/tealeg/xlsx/v3 v3.2.3
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c // indirect
|
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c // indirect
|
||||||
google.golang.org/grpc v1.32.0
|
google.golang.org/grpc v1.32.0
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||||
)
|
)
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -57,6 +57,10 @@ github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e h1:/xn7wUvlwaoA5IkdBUc
|
|||||||
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
github.com/iwind/TeaGo v0.0.0-20200923021120-f5d76441fe9e/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||||
github.com/iwind/TeaGo v0.0.0-20201120063500-ee2d7090f4bc h1:AOKJWsgCX5e7xnW2f7evcrgj6vzvvHIoDmA+xxL3BMI=
|
github.com/iwind/TeaGo v0.0.0-20201120063500-ee2d7090f4bc h1:AOKJWsgCX5e7xnW2f7evcrgj6vzvvHIoDmA+xxL3BMI=
|
||||||
github.com/iwind/TeaGo v0.0.0-20201120063500-ee2d7090f4bc/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
github.com/iwind/TeaGo v0.0.0-20201120063500-ee2d7090f4bc/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||||
|
github.com/iwind/TeaGo v0.0.0-20201206100217-6b8965ebe91b h1:ZoR880XHIMdWgGqqLok3OUbpViBIgDmyvVXiw3lmLlA=
|
||||||
|
github.com/iwind/TeaGo v0.0.0-20201206100217-6b8965ebe91b/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||||
|
github.com/iwind/TeaGo v0.0.0-20201206115018-cdd967bfb13d h1:J4ohNUwOqCQkY62LFwaygfGtHJj+87pnzr+RJxEYSBo=
|
||||||
|
github.com/iwind/TeaGo v0.0.0-20201206115018-cdd967bfb13d/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
@@ -199,6 +203,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ func loadAdminModuleMapping() (map[int64]*AdminModuleList, error) {
|
|||||||
mapping := map[int64]*AdminModuleList{}
|
mapping := map[int64]*AdminModuleList{}
|
||||||
for _, m := range modulesResp.AdminModules {
|
for _, m := range modulesResp.AdminModules {
|
||||||
list := &AdminModuleList{
|
list := &AdminModuleList{
|
||||||
IsSuper: m.IsSuper,
|
IsSuper: m.IsSuper,
|
||||||
|
Fullname: m.Fullname,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pbModule := range m.Modules {
|
for _, pbModule := range m.Modules {
|
||||||
@@ -65,6 +66,20 @@ func NotifyAdminModuleMappingChange() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查用户是否存在
|
||||||
|
func CheckAdmin(adminId int64) bool {
|
||||||
|
locker.Lock()
|
||||||
|
defer locker.Unlock()
|
||||||
|
|
||||||
|
// 如果还没有数据,则尝试加载
|
||||||
|
if len(sharedAdminModuleMapping) == 0 {
|
||||||
|
_, _ = loadAdminModuleMapping()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := sharedAdminModuleMapping[adminId]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// 检查模块是否允许访问
|
// 检查模块是否允许访问
|
||||||
func AllowModule(adminId int64, module string) bool {
|
func AllowModule(adminId int64, module string) bool {
|
||||||
locker.Lock()
|
locker.Lock()
|
||||||
@@ -101,6 +116,19 @@ func FindFirstAdminModule(adminId int64) (module AdminModuleCode, ok bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 查找某个管理员名称
|
||||||
|
func FindAdminFullname(adminId int64) string {
|
||||||
|
locker.Lock()
|
||||||
|
defer locker.Unlock()
|
||||||
|
|
||||||
|
list, ok := sharedAdminModuleMapping[adminId]
|
||||||
|
if ok {
|
||||||
|
return list.Fullname
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// 所有权限列表
|
// 所有权限列表
|
||||||
func AllModuleMaps() []maps.Map {
|
func AllModuleMaps() []maps.Map {
|
||||||
return []maps.Map{
|
return []maps.Map{
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ package configloaders
|
|||||||
import "github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
import "github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||||
|
|
||||||
type AdminModuleList struct {
|
type AdminModuleList struct {
|
||||||
IsSuper bool
|
IsSuper bool
|
||||||
Modules []*systemconfigs.AdminModule
|
Modules []*systemconfigs.AdminModule
|
||||||
|
Fullname string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *AdminModuleList) Allow(module string) bool {
|
func (this *AdminModuleList) Allow(module string) bool {
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ const (
|
|||||||
EncryptMethod = "aes-256-cfb"
|
EncryptMethod = "aes-256-cfb"
|
||||||
|
|
||||||
ErrServer = "服务器出了点小问题,请联系技术人员处理。"
|
ErrServer = "服务器出了点小问题,请联系技术人员处理。"
|
||||||
|
CookieSID = "edgesid"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package nodes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/errors"
|
"github.com/TeaOSLab/EdgeAdmin/internal/errors"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/events"
|
"github.com/TeaOSLab/EdgeAdmin/internal/events"
|
||||||
"github.com/iwind/TeaGo"
|
"github.com/iwind/TeaGo"
|
||||||
@@ -60,7 +61,7 @@ func (this *AdminNode) Run() {
|
|||||||
TeaGo.NewServer(false).
|
TeaGo.NewServer(false).
|
||||||
AccessLog(false).
|
AccessLog(false).
|
||||||
EndAll().
|
EndAll().
|
||||||
Session(sessions.NewFileSessionManager(86400, secret)).
|
Session(sessions.NewFileSessionManager(86400, secret), teaconst.CookieSID).
|
||||||
ReadHeaderTimeout(3 * time.Second).
|
ReadHeaderTimeout(3 * time.Second).
|
||||||
ReadTimeout(600 * time.Second).
|
ReadTimeout(600 * time.Second).
|
||||||
Start()
|
Start()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (this *TokenAction) RunGet(params struct {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// 没有登录,则限制请求速度
|
// 没有登录,则限制请求速度
|
||||||
if params.Auth.AdminId() <= 0 && lastTimestamp > 0 && time.Now().Unix()-lastTimestamp <= 1 {
|
if params.Auth.AdminId() <= 0 && lastTimestamp > 0 && time.Now().Unix()-lastTimestamp <= 0 {
|
||||||
this.Fail("请求速度过快,请稍后刷新后重试")
|
this.Fail("请求速度过快,请稍后刷新后重试")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package servers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
@@ -136,6 +137,15 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 用户
|
||||||
|
var userMap maps.Map = nil
|
||||||
|
if server.User != nil {
|
||||||
|
userMap = maps.Map{
|
||||||
|
"id": server.User.Id,
|
||||||
|
"fullname": server.User.Fullname,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serverMaps = append(serverMaps, maps.Map{
|
serverMaps = append(serverMaps, maps.Map{
|
||||||
"id": server.Id,
|
"id": server.Id,
|
||||||
"isOn": server.IsOn,
|
"isOn": server.IsOn,
|
||||||
@@ -149,6 +159,7 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
"groups": groupMaps,
|
"groups": groupMaps,
|
||||||
"serverNames": serverNames,
|
"serverNames": serverNames,
|
||||||
"countServerNames": countServerNames,
|
"countServerNames": countServerNames,
|
||||||
|
"user": userMap,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.Data["servers"] = serverMaps
|
this.Data["servers"] = serverMaps
|
||||||
@@ -178,5 +189,8 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}
|
}
|
||||||
this.Data["groups"] = groupMaps
|
this.Data["groups"] = groupMaps
|
||||||
|
|
||||||
|
// 是否有用户管理权限
|
||||||
|
this.Data["canVisitUser"] = configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeUser)
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ package helpers
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||||
nodes "github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
|
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -49,11 +46,20 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
|
|||||||
|
|
||||||
var session = action.Session()
|
var session = action.Session()
|
||||||
var adminId = session.GetInt64("adminId")
|
var adminId = session.GetInt64("adminId")
|
||||||
|
|
||||||
if adminId <= 0 {
|
if adminId <= 0 {
|
||||||
this.login(action)
|
this.login(action)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查用户是否存在
|
||||||
|
if !configloaders.CheckAdmin(adminId) {
|
||||||
|
session.Delete()
|
||||||
|
|
||||||
|
this.login(action)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// 检查用户权限
|
// 检查用户权限
|
||||||
if len(this.module) > 0 && !configloaders.AllowModule(adminId, this.module) {
|
if len(this.module) > 0 && !configloaders.AllowModule(adminId, this.module) {
|
||||||
action.ResponseWriter.WriteHeader(http.StatusForbidden)
|
action.ResponseWriter.WriteHeader(http.StatusForbidden)
|
||||||
@@ -61,28 +67,6 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查用户是否存在
|
|
||||||
rpc, err := nodes.SharedRPC()
|
|
||||||
if err != nil {
|
|
||||||
action.WriteString("setup rpc error: " + err.Error())
|
|
||||||
utils.PrintError(err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcResp, err := rpc.AdminRPC().CheckAdminExists(rpc.Context(0), &pb.CheckAdminExistsRequest{AdminId: adminId})
|
|
||||||
if err != nil {
|
|
||||||
utils.PrintError(err)
|
|
||||||
action.WriteString(teaconst.ErrServer)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rpcResp.IsOk {
|
|
||||||
session.Delete()
|
|
||||||
|
|
||||||
this.login(action)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
this.AdminId = adminId
|
this.AdminId = adminId
|
||||||
action.Context.Set("adminId", this.AdminId)
|
action.Context.Set("adminId", this.AdminId)
|
||||||
|
|
||||||
@@ -104,14 +88,7 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
|
|||||||
action.Data["teaShowVersion"] = config.ShowVersion
|
action.Data["teaShowVersion"] = config.ShowVersion
|
||||||
action.Data["teaTitle"] = config.AdminSystemName
|
action.Data["teaTitle"] = config.AdminSystemName
|
||||||
action.Data["teaName"] = config.ProductName
|
action.Data["teaName"] = config.ProductName
|
||||||
|
action.Data["teaUsername"] = configloaders.FindAdminFullname(adminId)
|
||||||
resp, err := rpc.AdminRPC().FindAdminFullname(rpc.Context(0), &pb.FindAdminFullnameRequest{AdminId: this.AdminId})
|
|
||||||
if err != nil {
|
|
||||||
utils.PrintError(err)
|
|
||||||
action.Data["teaUsername"] = ""
|
|
||||||
} else {
|
|
||||||
action.Data["teaUsername"] = resp.Fullname
|
|
||||||
}
|
|
||||||
|
|
||||||
action.Data["teaUserAvatar"] = ""
|
action.Data["teaUserAvatar"] = ""
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package helpers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -38,7 +39,7 @@ func (this *UserShouldAuth) StoreAdmin(adminId int64, remember bool) {
|
|||||||
// 修改sid的时间
|
// 修改sid的时间
|
||||||
if remember {
|
if remember {
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: "sid",
|
Name: teaconst.CookieSID,
|
||||||
Value: this.action.Session().Sid,
|
Value: this.action.Session().Sid,
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: 14 * 86400,
|
MaxAge: 14 * 86400,
|
||||||
@@ -51,7 +52,7 @@ func (this *UserShouldAuth) StoreAdmin(adminId int64, remember bool) {
|
|||||||
this.action.AddCookie(cookie)
|
this.action.AddCookie(cookie)
|
||||||
} else {
|
} else {
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: "sid",
|
Name: teaconst.CookieSID,
|
||||||
Value: this.action.Session().Sid,
|
Value: this.action.Session().Sid,
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: 0,
|
MaxAge: 0,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>服务名称</th>
|
<th>服务名称</th>
|
||||||
<th>所属分组</th>
|
<th>所属用户</th>
|
||||||
<th>部署集群</th>
|
<th>部署集群</th>
|
||||||
<th>域名</th>
|
<th>域名</th>
|
||||||
<th>端口</th>
|
<th>端口</th>
|
||||||
@@ -43,12 +43,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div v-if="server.groups.length > 0">
|
<span v-if="server.user != null">{{server.user.fullname}}<link-icon v-if="canVisitUser" :href="'/users/user?userId=' + server.user.id"></link-icon></span>
|
||||||
<div v-for="group in server.groups">
|
<span v-else>-</span>
|
||||||
<tiny-basic-label>{{group.name}}</tiny-basic-label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span class="disabled" v-else>-</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td>{{server.cluster.name}}</td>
|
<td>{{server.cluster.name}}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
Reference in New Issue
Block a user