diff --git a/internal/web/actions/default/clusters/cluster/nodes.go b/internal/web/actions/default/clusters/cluster/nodes.go index cc861fb3..de35d963 100644 --- a/internal/web/actions/default/clusters/cluster/nodes.go +++ b/internal/web/actions/default/clusters/cluster/nodes.go @@ -36,6 +36,7 @@ func (this *NodesAction) RunGet(params struct { MemoryOrder string TrafficInOrder string TrafficOutOrder string + LoadOrder string }) { this.Data["groupId"] = params.GroupId this.Data["regionId"] = params.RegionId @@ -43,6 +44,7 @@ func (this *NodesAction) RunGet(params struct { this.Data["activeState"] = params.ActiveState this.Data["keyword"] = params.Keyword this.Data["level"] = params.Level + this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0 // 集群是否已经设置了线路 clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId}) @@ -106,6 +108,10 @@ func (this *NodesAction) RunGet(params struct { req.TrafficOutAsc = true } else if params.TrafficOutOrder == "desc" { req.TrafficOutDesc = true + } else if params.LoadOrder == "asc" { + req.LoadAsc = true + } else if params.LoadOrder == "desc" { + req.LoadDesc = true } nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req) if err != nil { @@ -204,6 +210,7 @@ func (this *NodesAction) RunGet(params struct { "memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100), "trafficInBytes": status.TrafficInBytes, "trafficOutBytes": status.TrafficOutBytes, + "load1m": fmt.Sprintf("%.2f", status.Load1m), }, "cluster": maps.Map{ "id": node.NodeCluster.Id, diff --git a/internal/web/actions/default/clusters/create.go b/internal/web/actions/default/clusters/create.go index 0b7805cc..5fb5ae8f 100644 --- a/internal/web/actions/default/clusters/create.go +++ b/internal/web/actions/default/clusters/create.go @@ -27,7 +27,7 @@ func (this *CreateAction) RunGet(params struct{}) { } this.Data["hasDomains"] = hasDomainsResp.Exist - // 集群总数 + // 菜单:集群总数 totalResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{}) if err != nil { this.ErrorPage(err) @@ -35,6 +35,14 @@ func (this *CreateAction) RunGet(params struct{}) { } this.Data["totalNodeClusters"] = totalResp.Count + // 菜单:节点总数 + totalNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodes(this.AdminContext(), &pb.CountAllEnabledNodesRequest{}) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["totalNodes"] = totalNodesResp.Count + this.Show() } diff --git a/internal/web/actions/default/clusters/index.go b/internal/web/actions/default/clusters/index.go index 4fe3c115..06140825 100644 --- a/internal/web/actions/default/clusters/index.go +++ b/internal/web/actions/default/clusters/index.go @@ -1,15 +1,12 @@ package clusters import ( - "encoding/json" - "fmt" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/configutils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/types" - "time" ) type IndexAction struct { @@ -24,18 +21,26 @@ func (this *IndexAction) RunGet(params struct { Keyword string SearchType string }) { - isSearching := len(params.Keyword) > 0 + var isSearching = len(params.Keyword) > 0 this.Data["keyword"] = params.Keyword this.Data["searchType"] = params.SearchType this.Data["isSearching"] = isSearching // 集群总数 - totalResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{}) + totalClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{}) if err != nil { this.ErrorPage(err) return } - this.Data["totalNodeClusters"] = totalResp.Count + this.Data["totalNodeClusters"] = totalClustersResp.Count + + // 节点总数 + totalNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodes(this.AdminContext(), &pb.CountAllEnabledNodesRequest{}) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["totalNodes"] = totalNodesResp.Count // 常用的集群 latestClusterMaps := []maps.Map{} @@ -54,12 +59,6 @@ func (this *IndexAction) RunGet(params struct { } this.Data["latestClusters"] = latestClusterMaps - // 搜索节点 - if params.SearchType == "node" && len(params.Keyword) > 0 { - this.searchNodes(params.Keyword) - return - } - // 搜索集群 countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{ Keyword: params.Keyword, @@ -166,149 +165,3 @@ func (this *IndexAction) RunGet(params struct { this.Show() } - -func (this *IndexAction) searchNodes(keyword string) { - // 搜索节点 - countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{ - Keyword: keyword, - }) - if err != nil { - this.ErrorPage(err) - return - } - count := countResp.Count - page := this.NewPage(count) - this.Data["page"] = page.AsHTML() - this.Data["countNodes"] = count - - nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), &pb.ListEnabledNodesMatchRequest{ - Offset: page.Offset, - Size: page.Size, - Keyword: keyword, - }) - if err != nil { - this.ErrorPage(err) - return - } - - nodeMaps := []maps.Map{} - for _, node := range nodesResp.Nodes { - // 状态 - isSynced := false - status := &nodeconfigs.NodeStatus{} - if len(node.StatusJSON) > 0 { - err = json.Unmarshal(node.StatusJSON, &status) - if err != nil { - this.ErrorPage(err) - return - } - status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃 - isSynced = status.ConfigVersion == node.Version - } - - // IP - ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{ - NodeId: node.Id, - Role: nodeconfigs.NodeRoleNode, - }) - if err != nil { - this.ErrorPage(err) - return - } - ipAddresses := []maps.Map{} - for _, addr := range ipAddressesResp.NodeIPAddresses { - ipAddresses = append(ipAddresses, maps.Map{ - "id": addr.Id, - "name": addr.Name, - "ip": addr.Ip, - "canAccess": addr.CanAccess, - "isOn": addr.IsOn, - "isUp": addr.IsUp, - }) - } - - // 分组 - var groupMap maps.Map = nil - if node.NodeGroup != nil { - groupMap = maps.Map{ - "id": node.NodeGroup.Id, - "name": node.NodeGroup.Name, - } - } - - // 区域 - var regionMap maps.Map = nil - if node.NodeRegion != nil { - regionMap = maps.Map{ - "id": node.NodeRegion.Id, - "name": node.NodeRegion.Name, - } - } - - // DNS - dnsRouteNames := []string{} - for _, route := range node.DnsRoutes { - dnsRouteNames = append(dnsRouteNames, route.Name) - } - - // 从集群 - var secondaryClusterMaps []maps.Map - for _, secondaryCluster := range node.SecondaryNodeClusters { - secondaryClusterMaps = append(secondaryClusterMaps, maps.Map{ - "id": secondaryCluster.Id, - "name": secondaryCluster.Name, - "isOn": secondaryCluster.IsOn, - }) - } - - nodeMaps = append(nodeMaps, maps.Map{ - "id": node.Id, - "name": node.Name, - "isInstalled": node.IsInstalled, - "isOn": node.IsOn, - "isUp": node.IsUp, - "installStatus": maps.Map{ - "isRunning": node.InstallStatus.IsRunning, - "isFinished": node.InstallStatus.IsFinished, - "isOk": node.InstallStatus.IsOk, - "error": node.InstallStatus.Error, - }, - "status": maps.Map{ - "isActive": status.IsActive, - "updatedAt": status.UpdatedAt, - "hostname": status.Hostname, - "cpuUsage": status.CPUUsage, - "cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100), - "memUsage": status.MemoryUsage, - "memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100), - }, - "cluster": maps.Map{ - "id": node.NodeCluster.Id, - "name": node.NodeCluster.Name, - }, - "secondaryClusters": secondaryClusterMaps, - "isSynced": isSynced, - "ipAddresses": ipAddresses, - "group": groupMap, - "region": regionMap, - "dnsRouteNames": dnsRouteNames, - }) - } - this.Data["nodes"] = nodeMaps - - this.Data["clusters"] = []maps.Map{} - - // 搜索集群 - { - countResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{ - Keyword: keyword, - }) - if err != nil { - this.ErrorPage(err) - return - } - this.Data["countClusters"] = countResp.Count - } - - this.Show() -} diff --git a/internal/web/actions/default/clusters/init.go b/internal/web/actions/default/clusters/init.go index 09166295..81b86011 100644 --- a/internal/web/actions/default/clusters/init.go +++ b/internal/web/actions/default/clusters/init.go @@ -17,6 +17,7 @@ func init() { Get("", new(IndexAction)). GetPost("/create", new(CreateAction)). Post("/pin", new(PinAction)). + Get("/nodes", new(NodesAction)). // 只要登录即可访问的Action EndHelpers(). diff --git a/internal/web/actions/default/clusters/nodes.go b/internal/web/actions/default/clusters/nodes.go new file mode 100644 index 00000000..e026c31e --- /dev/null +++ b/internal/web/actions/default/clusters/nodes.go @@ -0,0 +1,293 @@ +// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package clusters + +import ( + "encoding/json" + "fmt" + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/logs" + "github.com/iwind/TeaGo/maps" + "github.com/iwind/TeaGo/types" + "strconv" + "time" +) + +type NodesAction struct { + actionutils.ParentAction +} + +func (this *NodesAction) Init() { + this.Nav("", "", "node") +} + +func (this *NodesAction) RunGet(params struct { + ClusterId int64 + GroupId int64 + RegionId int64 + InstalledState int + ActiveState int + Keyword string + Level int32 + + CpuOrder string + MemoryOrder string + TrafficInOrder string + TrafficOutOrder string + LoadOrder string +}) { + this.Data["groupId"] = params.GroupId + this.Data["regionId"] = params.RegionId + this.Data["installState"] = params.InstalledState + this.Data["activeState"] = params.ActiveState + this.Data["keyword"] = params.Keyword + this.Data["level"] = params.Level + this.Data["clusterId"] = params.ClusterId + this.Data["hasOrder"] = len(params.CpuOrder) > 0 || len(params.MemoryOrder) > 0 || len(params.TrafficInOrder) > 0 || len(params.TrafficOutOrder) > 0 || len(params.LoadOrder) > 0 + + // 集群是否已经设置了线路 + clusterDNSResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeClusterDNS(this.AdminContext(), &pb.FindEnabledNodeClusterDNSRequest{NodeClusterId: params.ClusterId}) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["hasClusterDNS"] = clusterDNSResp.Domain != nil + + // 数量 + countAllResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{ + NodeClusterId: params.ClusterId, + }) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["countAll"] = countAllResp.Count + + countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{ + NodeClusterId: params.ClusterId, + NodeGroupId: params.GroupId, + NodeRegionId: params.RegionId, + Level: params.Level, + InstallState: types.Int32(params.InstalledState), + ActiveState: types.Int32(params.ActiveState), + Keyword: params.Keyword, + }) + if err != nil { + this.ErrorPage(err) + return + } + + var page = this.NewPage(countResp.Count) + this.Data["page"] = page.AsHTML() + + var req = &pb.ListEnabledNodesMatchRequest{ + Offset: page.Offset, + Size: page.Size, + NodeClusterId: params.ClusterId, + NodeGroupId: params.GroupId, + NodeRegionId: params.RegionId, + Level: params.Level, + InstallState: types.Int32(params.InstalledState), + ActiveState: types.Int32(params.ActiveState), + Keyword: params.Keyword, + } + if params.CpuOrder == "asc" { + req.CpuAsc = true + } else if params.CpuOrder == "desc" { + req.CpuDesc = true + } else if params.MemoryOrder == "asc" { + req.MemoryAsc = true + } else if params.MemoryOrder == "desc" { + req.MemoryDesc = true + } else if params.TrafficInOrder == "asc" { + req.TrafficInAsc = true + } else if params.TrafficInOrder == "desc" { + req.TrafficInDesc = true + } else if params.TrafficOutOrder == "asc" { + req.TrafficOutAsc = true + } else if params.TrafficOutOrder == "desc" { + req.TrafficOutDesc = true + } else if params.LoadOrder == "asc" { + req.LoadAsc = true + } else if params.LoadOrder == "desc" { + req.LoadDesc = true + } + nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req) + if err != nil { + this.ErrorPage(err) + return + } + var nodeMaps = []maps.Map{} + for _, node := range nodesResp.Nodes { + // 状态 + isSynced := false + status := &nodeconfigs.NodeStatus{} + if len(node.StatusJSON) > 0 { + err = json.Unmarshal(node.StatusJSON, &status) + if err != nil { + logs.Error(err) + continue + } + status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃 + isSynced = status.ConfigVersion == node.Version + } + + // IP + ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledNodeIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledNodeIPAddressesWithNodeIdRequest{ + NodeId: node.Id, + Role: nodeconfigs.NodeRoleNode, + }) + if err != nil { + this.ErrorPage(err) + return + } + ipAddresses := []maps.Map{} + for _, addr := range ipAddressesResp.NodeIPAddresses { + ipAddresses = append(ipAddresses, maps.Map{ + "id": addr.Id, + "name": addr.Name, + "ip": addr.Ip, + "canAccess": addr.CanAccess, + "isUp": addr.IsUp, + "isOn": addr.IsOn, + }) + } + + // 分组 + var groupMap maps.Map = nil + if node.NodeGroup != nil { + groupMap = maps.Map{ + "id": node.NodeGroup.Id, + "name": node.NodeGroup.Name, + } + } + + // 区域 + var regionMap maps.Map = nil + if node.NodeRegion != nil { + regionMap = maps.Map{ + "id": node.NodeRegion.Id, + "name": node.NodeRegion.Name, + } + } + + // DNS + dnsRouteNames := []string{} + for _, route := range node.DnsRoutes { + dnsRouteNames = append(dnsRouteNames, route.Name) + } + + // 从集群 + var secondaryClusterMaps []maps.Map + for _, secondaryCluster := range node.SecondaryNodeClusters { + secondaryClusterMaps = append(secondaryClusterMaps, maps.Map{ + "id": secondaryCluster.Id, + "name": secondaryCluster.Name, + "isOn": secondaryCluster.IsOn, + }) + } + + nodeMaps = append(nodeMaps, maps.Map{ + "id": node.Id, + "name": node.Name, + "isInstalled": node.IsInstalled, + "isOn": node.IsOn, + "isUp": node.IsUp, + "installStatus": maps.Map{ + "isRunning": node.InstallStatus.IsRunning, + "isFinished": node.InstallStatus.IsFinished, + "isOk": node.InstallStatus.IsOk, + "error": node.InstallStatus.Error, + }, + "status": maps.Map{ + "isActive": status.IsActive, + "updatedAt": status.UpdatedAt, + "hostname": status.Hostname, + "cpuUsage": status.CPUUsage, + "cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100), + "memUsage": status.MemoryUsage, + "memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100), + "trafficInBytes": status.TrafficInBytes, + "trafficOutBytes": status.TrafficOutBytes, + "load1m": fmt.Sprintf("%.2f", status.Load1m), + }, + "cluster": maps.Map{ + "id": node.NodeCluster.Id, + "name": node.NodeCluster.Name, + }, + "secondaryClusters": secondaryClusterMaps, + "isSynced": isSynced, + "ipAddresses": ipAddresses, + "group": groupMap, + "region": regionMap, + "dnsRouteNames": dnsRouteNames, + "level": node.Level, + }) + } + this.Data["nodes"] = nodeMaps + + // 所有分组 + var groupMaps = []maps.Map{} + groupsResp, err := this.RPC().NodeGroupRPC().FindAllEnabledNodeGroupsWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodeGroupsWithNodeClusterIdRequest{ + NodeClusterId: params.ClusterId, + }) + if err != nil { + this.ErrorPage(err) + return + } + for _, group := range groupsResp.NodeGroups { + countNodesInGroupResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGroupId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGroupIdRequest{NodeGroupId: group.Id}) + if err != nil { + this.ErrorPage(err) + return + } + countNodes := countNodesInGroupResp.Count + groupName := group.Name + if countNodes > 0 { + groupName += "(" + strconv.FormatInt(countNodes, 10) + ")" + } + groupMaps = append(groupMaps, maps.Map{ + "id": group.Id, + "name": groupName, + "countNodes": countNodes, + }) + } + this.Data["groups"] = groupMaps + + // 所有区域 + regionsResp, err := this.RPC().NodeRegionRPC().FindAllEnabledAndOnNodeRegions(this.AdminContext(), &pb.FindAllEnabledAndOnNodeRegionsRequest{}) + if err != nil { + this.ErrorPage(err) + return + } + var regionMaps = []maps.Map{} + for _, region := range regionsResp.NodeRegions { + regionMaps = append(regionMaps, maps.Map{ + "id": region.Id, + "name": region.Name, + }) + } + this.Data["regions"] = regionMaps + + // 级别 + this.Data["levels"] = []maps.Map{} + if teaconst.IsPlus { + this.Data["levels"] = nodeconfigs.FindAllNodeLevels() + } + + // 集群总数 + totalClustersResp, err := this.RPC().NodeClusterRPC().CountAllEnabledNodeClusters(this.AdminContext(), &pb.CountAllEnabledNodeClustersRequest{}) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["totalNodeClusters"] = totalClustersResp.Count + + // 节点总数 + this.Data["totalNodes"] = countResp.Count + + this.Show() +} diff --git a/web/public/js/components/cluster/node-cluster-combo-box.js b/web/public/js/components/cluster/node-cluster-combo-box.js index d9d2957f..602cbc8a 100644 --- a/web/public/js/components/cluster/node-cluster-combo-box.js +++ b/web/public/js/components/cluster/node-cluster-combo-box.js @@ -20,7 +20,7 @@ Vue.component("node-cluster-combo-box", { } } }, - template: `
+ template: `
` }) \ No newline at end of file diff --git a/web/public/js/components/common/combo-box.js b/web/public/js/components/common/combo-box.js index 1f1db6a8..79945cdb 100644 --- a/web/public/js/components/common/combo-box.js +++ b/web/public/js/components/common/combo-box.js @@ -144,7 +144,7 @@ Vue.component("combo-box", {
- {{title}}:{{selectedItem.name}} + {{title}}:{{selectedItem.name}}
diff --git a/web/views/@default/@grids.less b/web/views/@default/@grids.less index 3ab551b2..db072c9f 100644 --- a/web/views/@default/@grids.less +++ b/web/views/@default/@grids.less @@ -26,7 +26,13 @@ } h4 { + color: grey; + position: relative; + a { + position: absolute; + right: 0.1em; + font-size: 1.26em; display: none; } @@ -35,7 +41,7 @@ } .column:hover { - background: rgba(0, 0, 0, .05)!important; + background: rgba(0, 0, 0, .03)!important; a { display: inline; diff --git a/web/views/@default/@layout.css b/web/views/@default/@layout.css index 6d380fb0..69f56fc7 100644 --- a/web/views/@default/@layout.css +++ b/web/views/@default/@layout.css @@ -128,14 +128,19 @@ body.expanded .right-box { border-right: 1px rgba(0, 0, 0, 0.1) solid; } .grid.counter-chart h4 { + color: grey; + position: relative; font-size: 1em; text-align: left; } .grid.counter-chart h4 a { + position: absolute; + right: 0.1em; + font-size: 1.26em; display: none; } .grid.counter-chart .column:hover { - background: rgba(0, 0, 0, 0.05) !important; + background: rgba(0, 0, 0, 0.03) !important; } .grid.counter-chart .column:hover a { display: inline; @@ -426,7 +431,7 @@ body.expanded .main { left: 22em; top: 5.6em; padding-bottom: 5em; - padding-right: 1em; + padding-right: 0.2em; right: 1em; } @media screen and (max-width: 512px) { diff --git a/web/views/@default/@layout.css.map b/web/views/@default/@layout.css.map index 234226c3..4cdcc6c6 100644 --- a/web/views/@default/@layout.css.map +++ b/web/views/@default/@layout.css.map @@ -1 +1 @@ -{"version":3,"sources":["@left_menu.less","@grids.less","@layout.less"],"names":[],"mappings":"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","file":"@layout.css"} \ No newline at end of file +{"version":3,"sources":["@left_menu.less","@grids.less","@layout.less"],"names":[],"mappings":"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","file":"@layout.css"} \ No newline at end of file diff --git a/web/views/@default/@layout.less b/web/views/@default/@layout.less index 4c33fda4..db8e6c2e 100644 --- a/web/views/@default/@layout.less +++ b/web/views/@default/@layout.less @@ -367,7 +367,7 @@ body.expanded .main { left: 22em; top: 5.6em; padding-bottom: 5em; - padding-right: 1em; + padding-right: 0.2em; right: 1em; diff --git a/web/views/@default/@layout_override.css b/web/views/@default/@layout_override.css index 341b4afa..53b9391f 100644 --- a/web/views/@default/@layout_override.css +++ b/web/views/@default/@layout_override.css @@ -48,4 +48,10 @@ select.dropdown { body.swal2-shown { overflow: auto !important; } +.grid { + margin-right: 0!important; +} +.fields button { + min-width: 5em; +} /*# sourceMappingURL=@layout_override.css.map */ \ No newline at end of file diff --git a/web/views/@default/@layout_override.css.map b/web/views/@default/@layout_override.css.map index cb7bc870..dace5f21 100644 --- a/web/views/@default/@layout_override.css.map +++ b/web/views/@default/@layout_override.css.map @@ -1 +1 @@ -{"version":3,"sources":["@layout_override.less"],"names":[],"mappings":"AACA,GAAG,OAAO,SAAU,MAAK,MAAM,QAAS,OAAM;AAAS,GAAG,OAAO,SAAU,MAAK,MAAM,QAAS,QAAO;EACrG,oCAAA;;AAGD,GAAG,OAAO,SAAU,MAAK,QAAS,OAAM;AAAS,GAAG,OAAO,SAAU,MAAK,QAAS,QAAO;EACzF,oCAAA;;AAGD,GAAG,MAAM;EACR,kCAAA;;AAGD,GAAG,MAAM,MAAM;EACd,iCAAA;;AAID,IACC;EACC,2BAAA;;AAKF,KAAK;EACJ,sBAAA;;AAGD,KAAK,KAAK;EACT,yBAAA;;AAID,KACC,GAAE;AADH,KACY,GAAE;EACZ,6BAAA;EACA,0BAAA;EACA,2BAAA;;AAJF,KAOC,GAAE;EACD,WAAA;;AARF,KAWC,GAAE;EACD,UAAA;;AAZF,KAeC,GAAE;EACD,UAAA;;AAKF,QAAQ;EACP,qBAAA;;AAID,MAAM;EACL,uBAAA;;AAID,QACC,MAAK;EACJ,yBAAA;;AAKF,IAAI;EACH,yBAAA","file":"@layout_override.css"} \ No newline at end of file +{"version":3,"sources":["@layout_override.less"],"names":[],"mappings":"AACA,GAAG,OAAO,SAAU,MAAK,MAAM,QAAS,OAAM;AAAS,GAAG,OAAO,SAAU,MAAK,MAAM,QAAS,QAAO;EACrG,yBAAA;;AAGD,GAAG,OAAO,SAAU,MAAK,QAAS,OAAM;AAAS,GAAG,OAAO,SAAU,MAAK,QAAS,QAAO;EACzF,yBAAA;;AAGD,GAAG,MAAM;EACR,kCAAA;;AAGD,GAAG,MAAM,MAAM;EACd,sBAAA;;AAID,IACC;EACC,2BAAA;;AAKF,KAAK;EACJ,sBAAA;;AAGD,KAAK,KAAK;EACT,cAAA;;AAID,KACC,GAAE;AADH,KACY,GAAE;EACZ,6BAAA;EACA,0BAAA;EACA,2BAAA;;AAJF,KAOC,GAAE;EACD,WAAA;;AARF,KAWC,GAAE;EACD,UAAA;;AAZF,KAeC,GAAE;EACD,UAAA;;AAKF,QAAQ;EACP,qBAAA;;AAID,MAAM;EACL,uBAAA;;AAID,QACC,MAAK;EACJ,yBAAA;;AAKF,IAAI;EACH,yBAAA;;AAID;EACC,yBAAA;;AAID,OACC;EACC,cAAA","file":"@layout_override.css"} \ No newline at end of file diff --git a/web/views/@default/@layout_override.less b/web/views/@default/@layout_override.less index d311e3c9..db994e9a 100644 --- a/web/views/@default/@layout_override.less +++ b/web/views/@default/@layout_override.less @@ -72,4 +72,16 @@ select.dropdown { // popup body.swal2-shown { overflow: auto !important; -} \ No newline at end of file +} + +// grid +.grid { + margin-right: 0!important; +} + +// fields +.fields { + button { + min-width: 5em; + } +} diff --git a/web/views/@default/clusters/@menu.html b/web/views/@default/clusters/@menu.html index 739b6ac1..d70f75e1 100644 --- a/web/views/@default/clusters/@menu.html +++ b/web/views/@default/clusters/@menu.html @@ -1,5 +1,6 @@ - 集群列表({{totalNodeClusters}}) - - 创建集群 + 集群 ({{totalNodeClusters}}) + 节点 ({{totalNodes}}) + | + [创建集群] diff --git a/web/views/@default/clusters/cluster/nodes.html b/web/views/@default/clusters/cluster/nodes.html index e31431bb..aa342e96 100644 --- a/web/views/@default/clusters/cluster/nodes.html +++ b/web/views/@default/clusters/cluster/nodes.html @@ -67,6 +67,7 @@ CPU 内存 下行流量 + 负载 状态 操作 @@ -117,7 +118,11 @@ - - {{teaweb.formatBytes(node.status.trafficOutBytes)}}
/分钟
+ {{teaweb.formatBytes(node.status.trafficOutBytes/60)}}
/分钟
+ - + + + {{node.status.load1m}}
- diff --git a/web/views/@default/clusters/index.html b/web/views/@default/clusters/index.html index 64e4cc9f..213cf32c 100644 --- a/web/views/@default/clusters/index.html +++ b/web/views/@default/clusters/index.html @@ -22,7 +22,7 @@ @@ -81,91 +81,4 @@
-
-

暂时还没有节点。

- - - - - - - - - - - - - - - - - - - - - - - - - - -
节点名称所属区域所属分组IPDNS线路CPU内存状态操作
{{node.name}} -
- -
-
- {{node.region.name}} - - - - {{node.group.name}} - - - - - -
-
-
{{addr.ip}} - ({{addr.name}},不可访问 - (不可访问) -
-
-
-
-
-
- {{routeName}} -
-
- - -
- {{node.status.cpuUsageText}} - - - - {{node.status.memUsageText}} - - - -
- 健康问题 -
-
- -
-
-
- 同步中 - 运行中 -
- 已断开 - 未连接 -
-
- 安装中 - 安装出错 - 未安装 -
-
- 详情 -
-
- \ No newline at end of file diff --git a/web/views/@default/clusters/nodes.html b/web/views/@default/clusters/nodes.html new file mode 100644 index 00000000..f2b570e7 --- /dev/null +++ b/web/views/@default/clusters/nodes.html @@ -0,0 +1,153 @@ +{$layout} +{$template "menu"} + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+   + [清除条件] +
+
+
+ +

暂时还没有节点。

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
节点名称IPDNS线路CPU内存下行流量负载状态操作
{{node.name}}  L{{node.level}} +
+ 区域:{{node.region.name}} +
+
+ 分组:{{node.group.name}} +
+
+ +
+
+ - +
+
+
{{addr.ip}} + ({{addr.name}},不可访问 + (不可访问) +
+
+
+
+
+
+ {{routeName}} +
+
+ [修改] +
+
+ + [设置] + + - +
+ {{node.status.cpuUsageText}} + - + + {{node.status.memUsageText}} + - + + {{teaweb.formatBytes(node.status.trafficOutBytes/60)}}
/s
+ - +
+ {{node.status.load1m}}
+ - +
+
+ 健康问题下线 +
+ [上线] +
+
+
+ +
+
+
+ 同步中 + 运行中 +
+ 已断开 + 未连接 +
+
+ 安装中 + 安装出错 + 未安装 +
+
+ 详情 +
+ + +
\ No newline at end of file diff --git a/web/views/@default/clusters/nodes.js b/web/views/@default/clusters/nodes.js new file mode 100644 index 00000000..d5c4f21f --- /dev/null +++ b/web/views/@default/clusters/nodes.js @@ -0,0 +1,37 @@ +Tea.context(function () { + this.teaweb = teaweb + + this.deleteNode = function (nodeId) { + teaweb.confirm("确定要删除这个节点吗?", function () { + this.$post("/cluster/nodes/delete") + .params({ + clusterId: this.clusterId, + nodeId: nodeId + }) + .refresh(); + }) + } + + this.upNode = function (nodeId) { + teaweb.confirm("确定要手动上线此节点吗?", function () { + this.$post("/clusters/cluster/node/up") + .params({ + nodeId: nodeId + }) + .refresh() + }) + } + + this.updateNodeDNS = function (nodeId) { + let that = this + teaweb.popup("/clusters/cluster/node/updateDNSPopup?clusterId=" + this.clusterId + "&nodeId=" + nodeId, { + width: "46em", + height: "26em", + callback: function () { + teaweb.success("保存成功", function () { + teaweb.reload() + }) + } + }) + } +}) \ No newline at end of file diff --git a/web/views/@default/dashboard/boards/index_plus.css b/web/views/@default/dashboard/boards/index_plus.css index 5505a3cc..2b2f75f3 100644 --- a/web/views/@default/dashboard/boards/index_plus.css +++ b/web/views/@default/dashboard/boards/index_plus.css @@ -5,17 +5,31 @@ } .grid.realtime-chart { margin-left: 0.4em !important; + position: relative; } .grid.realtime-chart .column { margin-bottom: 1em; - border-right: 1px rgba(0, 0, 0, 0.1) solid; + border: 1px rgba(0, 0, 0, 0.1) solid; + border-right: none; } -.grid.realtime-chart .column.no-border { - border-right: 0; +.grid.realtime-chart .column.with-border { + border-right: 1px rgba(0, 0, 0, 0.1) solid; } .grid.realtime-chart .chart { height: 10em; } +.grid.realtime-chart a { + display: none; + font-size: 0.85em; + position: absolute; + right: 1em; +} +.grid.realtime-chart .column:hover { + background: rgba(0, 0, 0, 0.03); +} +.grid.realtime-chart .column:hover a { + display: inline; +} .chart-box { height: 14em; } diff --git a/web/views/@default/dashboard/boards/index_plus.css.map b/web/views/@default/dashboard/boards/index_plus.css.map index 6001423d..7c4e9f78 100644 --- a/web/views/@default/dashboard/boards/index_plus.css.map +++ b/web/views/@default/dashboard/boards/index_plus.css.map @@ -1 +1 @@ -{"version":3,"sources":["index_plus.less"],"names":[],"mappings":"AAAA,GAAG,QACF,EACC,MAAK;EACJ,kBAAA;EACA,UAAA;EACA,QAAA;;AAKH,KAAK;EACJ,kBAAA;;AADD,KAAK,eAGJ;EACC,kBAAA;EACA,0CAAA;;AALF,KAAK,eAQJ,QAAO;EACN,eAAA;;AATF,KAAK,eAYJ;EACC,YAAA;;AAIF;EACC,YAAA;;AAGD;EACC,YAAA;;AADD,gBAGC,IAAG;EACF,UAAA;;AAIF,EACC;EACC,gBAAA;EACA,WAAA;;AAIF;EACC,kBAAA;EACA,kBAAA;;AAFD,YAIC;EACC,kBAAA;EACA,QAAA;EACA,gBAAA;EACA,OAAA;EACA,QAAA;EACA,gBAAA;;AAVF,YAIC,QAQC;EACC,WAAA;;AAbH,YAIC,QAYC;EACC,gBAAA;;AAjBH,YAqBC;EACC,WAAA;EACA,iBAAA;;AAvBF,YAqBC,SAIC;EACC,gBAAA;EACA,mBAAA;;AA3BH,YAqBC,SASC;EACC,gBAAA","file":"index_plus.css"} \ No newline at end of file +{"version":3,"sources":["index_plus.less"],"names":[],"mappings":"AAAA,GAAG,QACF,EACC,MAAK;EACJ,kBAAA;EACA,UAAA;EACA,QAAA;;AAKH,KAAK;EACJ,kBAAA;EACA,kBAAA;;AAFD,KAAK,eAIJ;EACC,kBAAA;EACA,oCAAA;EACA,kBAAA;;AAPF,KAAK,eAUJ,QAAO;EACN,0CAAA;;AAXF,KAAK,eAcJ;EACC,YAAA;;AAfF,KAAK,eAkBJ;EACC,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,UAAA;;AAtBF,KAAK,eAyBJ,QAAO;EACN,+BAAA;;AA1BF,KAAK,eAyBJ,QAAO,MAGN;EACC,eAAA;;AAKH;EACC,YAAA;;AAGD;EACC,YAAA;;AADD,gBAGC,IAAG;EACF,UAAA;;AAIF,EACC;EACC,gBAAA;EACA,WAAA;;AAIF;EACC,kBAAA;EACA,kBAAA;;AAFD,YAIC;EACC,kBAAA;EACA,QAAA;EACA,gBAAA;EACA,OAAA;EACA,QAAA;EACA,gBAAA;;AAVF,YAIC,QAQC;EACC,WAAA;;AAbH,YAIC,QAYC;EACC,gBAAA;;AAjBH,YAqBC;EACC,WAAA;EACA,iBAAA;;AAvBF,YAqBC,SAIC;EACC,gBAAA;EACA,mBAAA;;AA3BH,YAqBC,SASC;EACC,gBAAA","file":"index_plus.css"} \ No newline at end of file diff --git a/web/views/@default/dashboard/boards/index_plus.less b/web/views/@default/dashboard/boards/index_plus.less index b1f89fce..c54d16c4 100644 --- a/web/views/@default/dashboard/boards/index_plus.less +++ b/web/views/@default/dashboard/boards/index_plus.less @@ -10,19 +10,36 @@ .grid.realtime-chart { margin-left: 0.4em !important; + position: relative; .column { margin-bottom: 1em; - border-right: 1px rgba(0, 0, 0, .1) solid; + border: 1px rgba(0, 0, 0, .1) solid; + border-right: none; } - .column.no-border { - border-right: 0; + .column.with-border { + border-right: 1px rgba(0, 0, 0, .1) solid; } .chart { height: 10em; } + + a { + display: none; + font-size: 0.85em; + position: absolute; + right: 1em; + } + + .column:hover { + background: rgba(0, 0, 0, .03); + + a { + display: inline; + } + } } .chart-box { diff --git a/web/views/@default/dashboard/index.css b/web/views/@default/dashboard/index.css index 9522e987..ffdcc70c 100644 --- a/web/views/@default/dashboard/index.css +++ b/web/views/@default/dashboard/index.css @@ -3,30 +3,6 @@ right: 1em; top: 2em; } -.grid { - margin-top: 2em !important; - margin-left: 2em !important; -} -.grid .column { - margin-bottom: 2em; - border-right: 1px #eee solid; -} -.grid .column div.value { - margin-top: 1.5em; -} -.grid .column div.value span { - font-size: 2em; - margin-right: 0.2em; -} -.grid .column.no-border { - border-right: 0; -} -.grid h4 a { - display: none; -} -.grid .column:hover a { - display: inline; -} .chart-box { height: 14em; } diff --git a/web/views/@default/dashboard/index.css.map b/web/views/@default/dashboard/index.css.map index d231dbe6..7cd4413b 100644 --- a/web/views/@default/dashboard/index.css.map +++ b/web/views/@default/dashboard/index.css.map @@ -1 +1 @@ -{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,GAAG,QACF,EACC;EACC,kBAAA;EACA,UAAA;EACA,QAAA;;AAKH;EACC,0BAAA;EACA,2BAAA;;AAFD,KAIC;EACC,kBAAA;EACA,4BAAA;;AANF,KAIC,QAIC,IAAG;EACF,iBAAA;;AATH,KAIC,QAIC,IAAG,MAGF;EACC,cAAA;EACA,mBAAA;;AAbJ,KAkBC,QAAO;EACN,eAAA;;AAnBF,KAsBC,GACC;EACC,aAAA;;AAxBH,KA4BC,QAAO,MACN;EACC,eAAA;;AAKH;EACC,YAAA;;AAGD,EACC;EACC,gBAAA;EACA,WAAA","file":"index.css"} \ No newline at end of file +{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,GAAG,QACF,EACC;EACC,kBAAA;EACA,UAAA;EACA,QAAA;;AAKH;EACC,YAAA;;AAGD,EACC;EACC,gBAAA;EACA,WAAA","file":"index.css"} \ No newline at end of file diff --git a/web/views/@default/dashboard/index.html b/web/views/@default/dashboard/index.html index e1549212..037a1fd5 100644 --- a/web/views/@default/dashboard/index.html +++ b/web/views/@default/dashboard/index.html @@ -34,7 +34,7 @@ -
+

集群

{{dashboard.countNodeClusters}}
diff --git a/web/views/@default/dashboard/index.less b/web/views/@default/dashboard/index.less index 5d1c1815..fc641bd3 100644 --- a/web/views/@default/dashboard/index.less +++ b/web/views/@default/dashboard/index.less @@ -8,41 +8,6 @@ } } -.grid { - margin-top: 2em !important; - margin-left: 2em !important; - - .column { - margin-bottom: 2em; - border-right: 1px #eee solid; - - div.value { - margin-top: 1.5em; - - span { - font-size: 2em; - margin-right: 0.2em; - } - } - } - - .column.no-border { - border-right: 0; - } - - h4 { - a { - display: none; - } - } - - .column:hover { - a { - display: inline; - } - } -} - .chart-box { height: 14em; }