mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 21:50:28 +08:00 
			
		
		
		
	实现节点看板(仅对企业版开放)
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
			
		||||
# GoEdge目标
 | 
			
		||||
做一款人人用得起的CDN & WAF系统。
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
## 特性
 | 
			
		||||
* `免费` - 开源、免费、自由、开放
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
package rpc
 | 
			
		||||
 | 
			
		||||
import "context"
 | 
			
		||||
 | 
			
		||||
type ContextInterface interface {
 | 
			
		||||
	AdminContext() context.Context
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								internal/web/actions/actionutils/action_interface.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								internal/web/actions/actionutils/action_interface.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
package actionutils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ActionInterface interface {
 | 
			
		||||
	RPC() *rpc.RPCClient
 | 
			
		||||
 | 
			
		||||
	AdminContext() context.Context
 | 
			
		||||
 | 
			
		||||
	ViewData() maps.Map
 | 
			
		||||
}
 | 
			
		||||
@@ -10,6 +10,7 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
 | 
			
		||||
	"github.com/iwind/TeaGo/actions"
 | 
			
		||||
	"github.com/iwind/TeaGo/logs"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
@@ -126,3 +127,8 @@ func (this *ParentAction) AdminContext() context.Context {
 | 
			
		||||
	}
 | 
			
		||||
	return this.rpcClient.Context(this.AdminId())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ViewData 视图里可以使用的数据
 | 
			
		||||
func (this *ParentAction) ViewData() maps.Map {
 | 
			
		||||
	return this.Data
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,14 @@
 | 
			
		||||
package boards
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type IndexAction struct {
 | 
			
		||||
@@ -21,6 +24,11 @@ func (this *IndexAction) Init() {
 | 
			
		||||
func (this *IndexAction) RunGet(params struct {
 | 
			
		||||
	ClusterId int64
 | 
			
		||||
}) {
 | 
			
		||||
	if !teaconst.IsPlus {
 | 
			
		||||
		this.RedirectURL("/clusters/cluster?clusterId=" + strconv.FormatInt(params.ClusterId, 10))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeClusterBoard(this.AdminContext(), &pb.ComposeServerStatNodeClusterBoardRequest{NodeClusterId: params.ClusterId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
@@ -128,5 +136,41 @@ func (this *IndexAction) RunGet(params struct {
 | 
			
		||||
		this.Data["loadValues"] = statMaps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 指标
 | 
			
		||||
	{
 | 
			
		||||
		var chartMaps = []maps.Map{}
 | 
			
		||||
		for _, chart := range resp.MetricCharts {
 | 
			
		||||
			var statMaps = []maps.Map{}
 | 
			
		||||
			for _, stat := range chart.MetricStats {
 | 
			
		||||
				statMaps = append(statMaps, maps.Map{
 | 
			
		||||
					"keys":  stat.Keys,
 | 
			
		||||
					"time":  stat.Time,
 | 
			
		||||
					"value": stat.Value,
 | 
			
		||||
					"count": stat.SumCount,
 | 
			
		||||
					"total": stat.SumTotal,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			chartMaps = append(chartMaps, maps.Map{
 | 
			
		||||
				"chart": maps.Map{
 | 
			
		||||
					"id":       chart.MetricChart.Id,
 | 
			
		||||
					"name":     chart.MetricChart.Name,
 | 
			
		||||
					"widthDiv": chart.MetricChart.WidthDiv,
 | 
			
		||||
					"isOn":     chart.MetricChart.IsOn,
 | 
			
		||||
					"maxItems": chart.MetricChart.MaxItems,
 | 
			
		||||
					"type":     chart.MetricChart.Type,
 | 
			
		||||
				},
 | 
			
		||||
				"item": maps.Map{
 | 
			
		||||
					"id":         chart.MetricChart.MetricItem.Id,
 | 
			
		||||
					"name":       chart.MetricChart.MetricItem.Name,
 | 
			
		||||
					"period":     chart.MetricChart.MetricItem.Period,
 | 
			
		||||
					"periodUnit": chart.MetricChart.MetricItem.PeriodUnit,
 | 
			
		||||
					"valueType":  serverconfigs.FindMetricValueType(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
 | 
			
		||||
				},
 | 
			
		||||
				"stats": statMaps,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["metricCharts"] = chartMaps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/boards"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/groups"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node"
 | 
			
		||||
	nodeboards "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/boards"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/monitor"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/thresholds"
 | 
			
		||||
	clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
 | 
			
		||||
@@ -32,34 +33,39 @@ func init() {
 | 
			
		||||
			GetPost("/installManual", new(InstallManualAction)).
 | 
			
		||||
 | 
			
		||||
			// 节点相关
 | 
			
		||||
			Get("/node", new(node.IndexAction)).
 | 
			
		||||
			GetPost("/node/update", new(node.UpdateAction)).
 | 
			
		||||
			GetPost("/node/install", new(node.InstallAction)).
 | 
			
		||||
			Post("/node/updateInstallStatus", new(node.UpdateInstallStatusAction)).
 | 
			
		||||
			Post("/node/status", new(node.StatusAction)).
 | 
			
		||||
			Get("/node/logs", new(node.LogsAction)).
 | 
			
		||||
			Post("/node/start", new(node.StartAction)).
 | 
			
		||||
			Post("/node/stop", new(node.StopAction)).
 | 
			
		||||
			Post("/node/up", new(node.UpAction)).
 | 
			
		||||
			Get("/node/monitor", new(monitor.IndexAction)).
 | 
			
		||||
			Post("/node/monitor/cpu", new(monitor.CpuAction)).
 | 
			
		||||
			Post("/node/monitor/memory", new(monitor.MemoryAction)).
 | 
			
		||||
			Post("/node/monitor/load", new(monitor.LoadAction)).
 | 
			
		||||
			Post("/node/monitor/trafficIn", new(monitor.TrafficInAction)).
 | 
			
		||||
			Post("/node/monitor/trafficOut", new(monitor.TrafficOutAction)).
 | 
			
		||||
			Post("/node/monitor/connections", new(monitor.ConnectionsAction)).
 | 
			
		||||
			Get("/node/thresholds", new(thresholds.IndexAction)).
 | 
			
		||||
			Prefix("/clusters/cluster/node").
 | 
			
		||||
			Get("", new(node.IndexAction)).
 | 
			
		||||
			GetPost("/update", new(node.UpdateAction)).
 | 
			
		||||
			GetPost("/install", new(node.InstallAction)).
 | 
			
		||||
			Post("/updateInstallStatus", new(node.UpdateInstallStatusAction)).
 | 
			
		||||
			Post("/status", new(node.StatusAction)).
 | 
			
		||||
			Get("/logs", new(node.LogsAction)).
 | 
			
		||||
			Post("/start", new(node.StartAction)).
 | 
			
		||||
			Post("/stop", new(node.StopAction)).
 | 
			
		||||
			Post("/up", new(node.UpAction)).
 | 
			
		||||
			Get("/monitor", new(monitor.IndexAction)).
 | 
			
		||||
			Post("/monitor/cpu", new(monitor.CpuAction)).
 | 
			
		||||
			Post("/monitor/memory", new(monitor.MemoryAction)).
 | 
			
		||||
			Post("/monitor/load", new(monitor.LoadAction)).
 | 
			
		||||
			Post("/monitor/trafficIn", new(monitor.TrafficInAction)).
 | 
			
		||||
			Post("/monitor/trafficOut", new(monitor.TrafficOutAction)).
 | 
			
		||||
			Post("/monitor/connections", new(monitor.ConnectionsAction)).
 | 
			
		||||
			Get("/thresholds", new(thresholds.IndexAction)).
 | 
			
		||||
			Get("/detail", new(node.DetailAction)).
 | 
			
		||||
			Get("/boards", new(nodeboards.IndexAction)).
 | 
			
		||||
 | 
			
		||||
			// 分组相关
 | 
			
		||||
			Get("/groups", new(groups.IndexAction)).
 | 
			
		||||
			GetPost("/groups/createPopup", new(groups.CreatePopupAction)).
 | 
			
		||||
			GetPost("/groups/updatePopup", new(groups.UpdatePopupAction)).
 | 
			
		||||
			Post("/groups/delete", new(groups.DeleteAction)).
 | 
			
		||||
			Post("/groups/sort", new(groups.SortAction)).
 | 
			
		||||
			GetPost("/groups/selectPopup", new(groups.SelectPopupAction)).
 | 
			
		||||
			Prefix("/clusters/cluster/groups").
 | 
			
		||||
			Get("", new(groups.IndexAction)).
 | 
			
		||||
			GetPost("/createPopup", new(groups.CreatePopupAction)).
 | 
			
		||||
			GetPost("/updatePopup", new(groups.UpdatePopupAction)).
 | 
			
		||||
			Post("/delete", new(groups.DeleteAction)).
 | 
			
		||||
			Post("/sort", new(groups.SortAction)).
 | 
			
		||||
			GetPost("/selectPopup", new(groups.SelectPopupAction)).
 | 
			
		||||
 | 
			
		||||
			// 看板相关
 | 
			
		||||
			Get("/boards", new(boards.IndexAction)).
 | 
			
		||||
			Prefix("/clusters/cluster/boards").
 | 
			
		||||
			Get("", new(boards.IndexAction)).
 | 
			
		||||
 | 
			
		||||
			EndAll()
 | 
			
		||||
	})
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,180 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package boards
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type IndexAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IndexAction) Init() {
 | 
			
		||||
	this.Nav("", "node", "board")
 | 
			
		||||
	this.SecondMenu("nodes")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IndexAction) RunGet(params struct {
 | 
			
		||||
	ClusterId int64
 | 
			
		||||
	NodeId    int64
 | 
			
		||||
}) {
 | 
			
		||||
	err := nodeutils.InitNodeInfo(this, params.NodeId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !teaconst.IsPlus {
 | 
			
		||||
		this.RedirectURL("/clusters/cluster/node?clusterId=" + strconv.FormatInt(params.ClusterId, 10) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeBoard(this.AdminContext(), &pb.ComposeServerStatNodeBoardRequest{NodeId: params.NodeId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Data["board"] = maps.Map{
 | 
			
		||||
		"isActive":            resp.IsActive,
 | 
			
		||||
		"trafficInBytes":      resp.TrafficInBytes,
 | 
			
		||||
		"trafficOutBytes":     resp.TrafficOutBytes,
 | 
			
		||||
		"countConnections":    resp.CountConnections,
 | 
			
		||||
		"countRequests":       resp.CountRequests,
 | 
			
		||||
		"countAttackRequests": resp.CountAttackRequests,
 | 
			
		||||
		"cpuUsage":            resp.CpuUsage,
 | 
			
		||||
		"memoryUsage":         resp.MemoryUsage,
 | 
			
		||||
		"memoryTotalSize":     resp.MemoryTotalSize,
 | 
			
		||||
		"load":                resp.Load,
 | 
			
		||||
		"cacheDiskSize":       resp.CacheDiskSize,
 | 
			
		||||
		"cacheMemorySize":     resp.CacheMemorySize,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 24小时流量趋势
 | 
			
		||||
	{
 | 
			
		||||
		var statMaps = []maps.Map{}
 | 
			
		||||
		for _, stat := range resp.HourlyTrafficStats {
 | 
			
		||||
			statMaps = append(statMaps, maps.Map{
 | 
			
		||||
				"bytes":               stat.Bytes,
 | 
			
		||||
				"cachedBytes":         stat.CachedBytes,
 | 
			
		||||
				"countRequests":       stat.CountRequests,
 | 
			
		||||
				"countCachedRequests": stat.CountCachedRequests,
 | 
			
		||||
				"day":                 stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
 | 
			
		||||
				"hour":                stat.Hour[8:],
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["hourlyStats"] = statMaps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 15天流量趋势
 | 
			
		||||
	{
 | 
			
		||||
		var statMaps = []maps.Map{}
 | 
			
		||||
		for _, stat := range resp.DailyTrafficStats {
 | 
			
		||||
			statMaps = append(statMaps, maps.Map{
 | 
			
		||||
				"bytes":               stat.Bytes,
 | 
			
		||||
				"cachedBytes":         stat.CachedBytes,
 | 
			
		||||
				"countRequests":       stat.CountRequests,
 | 
			
		||||
				"countCachedRequests": stat.CountCachedRequests,
 | 
			
		||||
				"day":                 stat.Day[4:6] + "月" + stat.Day[6:] + "日",
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["dailyStats"] = statMaps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 域名排行
 | 
			
		||||
	{
 | 
			
		||||
		var statMaps = []maps.Map{}
 | 
			
		||||
		for _, stat := range resp.TopDomainStats {
 | 
			
		||||
			statMaps = append(statMaps, maps.Map{
 | 
			
		||||
				"serverId":      stat.ServerId,
 | 
			
		||||
				"domain":        stat.Domain,
 | 
			
		||||
				"countRequests": stat.CountRequests,
 | 
			
		||||
				"bytes":         stat.Bytes,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["topDomainStats"] = statMaps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// CPU
 | 
			
		||||
	{
 | 
			
		||||
		var statMaps = []maps.Map{}
 | 
			
		||||
		for _, stat := range resp.CpuNodeValues {
 | 
			
		||||
			statMaps = append(statMaps, maps.Map{
 | 
			
		||||
				"time":  timeutil.FormatTime("H:i", stat.CreatedAt),
 | 
			
		||||
				"value": types.Float32(string(stat.ValueJSON)),
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["cpuValues"] = statMaps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Memory
 | 
			
		||||
	{
 | 
			
		||||
		var statMaps = []maps.Map{}
 | 
			
		||||
		for _, stat := range resp.MemoryNodeValues {
 | 
			
		||||
			statMaps = append(statMaps, maps.Map{
 | 
			
		||||
				"time":  timeutil.FormatTime("H:i", stat.CreatedAt),
 | 
			
		||||
				"value": types.Float32(string(stat.ValueJSON)),
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["memoryValues"] = statMaps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Load
 | 
			
		||||
	{
 | 
			
		||||
		var statMaps = []maps.Map{}
 | 
			
		||||
		for _, stat := range resp.LoadNodeValues {
 | 
			
		||||
			statMaps = append(statMaps, maps.Map{
 | 
			
		||||
				"time":  timeutil.FormatTime("H:i", stat.CreatedAt),
 | 
			
		||||
				"value": types.Float32(string(stat.ValueJSON)),
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["loadValues"] = statMaps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 指标
 | 
			
		||||
	{
 | 
			
		||||
		var chartMaps = []maps.Map{}
 | 
			
		||||
		for _, chart := range resp.MetricCharts {
 | 
			
		||||
			var statMaps = []maps.Map{}
 | 
			
		||||
			for _, stat := range chart.MetricStats {
 | 
			
		||||
				statMaps = append(statMaps, maps.Map{
 | 
			
		||||
					"keys":  stat.Keys,
 | 
			
		||||
					"time":  stat.Time,
 | 
			
		||||
					"value": stat.Value,
 | 
			
		||||
					"count": stat.SumCount,
 | 
			
		||||
					"total": stat.SumTotal,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
			chartMaps = append(chartMaps, maps.Map{
 | 
			
		||||
				"chart": maps.Map{
 | 
			
		||||
					"id":       chart.MetricChart.Id,
 | 
			
		||||
					"name":     chart.MetricChart.Name,
 | 
			
		||||
					"widthDiv": chart.MetricChart.WidthDiv,
 | 
			
		||||
					"isOn":     chart.MetricChart.IsOn,
 | 
			
		||||
					"maxItems": chart.MetricChart.MaxItems,
 | 
			
		||||
					"type":     chart.MetricChart.Type,
 | 
			
		||||
				},
 | 
			
		||||
				"item": maps.Map{
 | 
			
		||||
					"id":         chart.MetricChart.MetricItem.Id,
 | 
			
		||||
					"name":       chart.MetricChart.MetricItem.Name,
 | 
			
		||||
					"period":     chart.MetricChart.MetricItem.Period,
 | 
			
		||||
					"periodUnit": chart.MetricChart.MetricItem.PeriodUnit,
 | 
			
		||||
					"valueType":  serverconfigs.FindMetricValueType(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
 | 
			
		||||
				},
 | 
			
		||||
				"stats": statMaps,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["metricCharts"] = chartMaps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										259
									
								
								internal/web/actions/default/clusters/cluster/node/detail.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								internal/web/actions/default/clusters/cluster/node/detail.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,259 @@
 | 
			
		||||
package node
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DetailAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *DetailAction) Init() {
 | 
			
		||||
	this.Nav("", "node", "node")
 | 
			
		||||
	this.SecondMenu("nodes")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *DetailAction) RunGet(params struct {
 | 
			
		||||
	NodeId int64
 | 
			
		||||
}) {
 | 
			
		||||
	this.Data["nodeId"] = params.NodeId
 | 
			
		||||
 | 
			
		||||
	nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	node := nodeResp.Node
 | 
			
		||||
	if node == nil {
 | 
			
		||||
		this.WriteString("找不到要操作的节点")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var clusterMap maps.Map = nil
 | 
			
		||||
	if node.NodeCluster != nil {
 | 
			
		||||
		clusterId := node.NodeCluster.Id
 | 
			
		||||
		clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.ErrorPage(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		cluster := clusterResp.NodeCluster
 | 
			
		||||
		if cluster != nil {
 | 
			
		||||
			clusterMap = maps.Map{
 | 
			
		||||
				"id":         cluster.Id,
 | 
			
		||||
				"name":       cluster.Name,
 | 
			
		||||
				"installDir": cluster.InstallDir,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// IP地址
 | 
			
		||||
	ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
 | 
			
		||||
		NodeId: params.NodeId,
 | 
			
		||||
		Role:   nodeconfigs.NodeRoleNode,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ipAddressMaps := []maps.Map{}
 | 
			
		||||
	for _, addr := range ipAddressesResp.Addresses {
 | 
			
		||||
		ipAddressMaps = append(ipAddressMaps, maps.Map{
 | 
			
		||||
			"id":        addr.Id,
 | 
			
		||||
			"name":      addr.Name,
 | 
			
		||||
			"ip":        addr.Ip,
 | 
			
		||||
			"canAccess": addr.CanAccess,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// DNS相关
 | 
			
		||||
	dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	dnsRouteMaps := []maps.Map{}
 | 
			
		||||
	recordName := ""
 | 
			
		||||
	recordValue := ""
 | 
			
		||||
	if dnsInfoResp.Node != nil {
 | 
			
		||||
		recordName = dnsInfoResp.Node.NodeClusterDNSName + "." + dnsInfoResp.Node.DnsDomainName
 | 
			
		||||
		recordValue = dnsInfoResp.Node.IpAddr
 | 
			
		||||
		for _, dnsInfo := range dnsInfoResp.Node.Routes {
 | 
			
		||||
			dnsRouteMaps = append(dnsRouteMaps, maps.Map{
 | 
			
		||||
				"name": dnsInfo.Name,
 | 
			
		||||
				"code": dnsInfo.Code,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(dnsRouteMaps) == 0 {
 | 
			
		||||
		dnsRouteMaps = append(dnsRouteMaps, maps.Map{
 | 
			
		||||
			"name": "",
 | 
			
		||||
			"code": "",
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["dnsRoutes"] = dnsRouteMaps
 | 
			
		||||
	this.Data["dnsRecordName"] = recordName
 | 
			
		||||
	this.Data["dnsRecordValue"] = recordValue
 | 
			
		||||
 | 
			
		||||
	// 登录信息
 | 
			
		||||
	var loginMap maps.Map = nil
 | 
			
		||||
	if node.Login != nil {
 | 
			
		||||
		loginParams := maps.Map{}
 | 
			
		||||
		if len(node.Login.Params) > 0 {
 | 
			
		||||
			err = json.Unmarshal(node.Login.Params, &loginParams)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.ErrorPage(err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		grantMap := maps.Map{}
 | 
			
		||||
		grantId := loginParams.GetInt64("grantId")
 | 
			
		||||
		if grantId > 0 {
 | 
			
		||||
			grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.ErrorPage(err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if grantResp.NodeGrant != nil {
 | 
			
		||||
				grantMap = maps.Map{
 | 
			
		||||
					"id":         grantResp.NodeGrant.Id,
 | 
			
		||||
					"name":       grantResp.NodeGrant.Name,
 | 
			
		||||
					"method":     grantResp.NodeGrant.Method,
 | 
			
		||||
					"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		loginMap = maps.Map{
 | 
			
		||||
			"id":     node.Login.Id,
 | 
			
		||||
			"name":   node.Login.Name,
 | 
			
		||||
			"type":   node.Login.Type,
 | 
			
		||||
			"params": loginParams,
 | 
			
		||||
			"grant":  grantMap,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 运行状态
 | 
			
		||||
	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秒之内认为活跃
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检查是否有新版本
 | 
			
		||||
	if len(status.OS) > 0 {
 | 
			
		||||
		checkVersionResp, err := this.RPC().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{
 | 
			
		||||
			Os:             status.OS,
 | 
			
		||||
			Arch:           status.Arch,
 | 
			
		||||
			CurrentVersion: status.BuildVersion,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.ErrorPage(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion
 | 
			
		||||
		this.Data["newVersion"] = checkVersionResp.NewVersion
 | 
			
		||||
	} else {
 | 
			
		||||
		this.Data["shouldUpgrade"] = false
 | 
			
		||||
		this.Data["newVersion"] = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 分组
 | 
			
		||||
	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,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 缓存硬盘 & 内存容量
 | 
			
		||||
	var maxCacheDiskCapacity maps.Map = nil
 | 
			
		||||
	if node.MaxCacheDiskCapacity != nil {
 | 
			
		||||
		maxCacheDiskCapacity = maps.Map{
 | 
			
		||||
			"count": node.MaxCacheDiskCapacity.Count,
 | 
			
		||||
			"unit":  node.MaxCacheDiskCapacity.Unit,
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		maxCacheDiskCapacity = maps.Map{
 | 
			
		||||
			"count": 0,
 | 
			
		||||
			"unit":  "gb",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var maxCacheMemoryCapacity maps.Map = nil
 | 
			
		||||
	if node.MaxCacheMemoryCapacity != nil {
 | 
			
		||||
		maxCacheMemoryCapacity = maps.Map{
 | 
			
		||||
			"count": node.MaxCacheMemoryCapacity.Count,
 | 
			
		||||
			"unit":  node.MaxCacheMemoryCapacity.Unit,
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		maxCacheMemoryCapacity = maps.Map{
 | 
			
		||||
			"count": 0,
 | 
			
		||||
			"unit":  "gb",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Data["node"] = maps.Map{
 | 
			
		||||
		"id":          node.Id,
 | 
			
		||||
		"name":        node.Name,
 | 
			
		||||
		"ipAddresses": ipAddressMaps,
 | 
			
		||||
		"cluster":     clusterMap,
 | 
			
		||||
		"login":       loginMap,
 | 
			
		||||
		"installDir":  node.InstallDir,
 | 
			
		||||
		"isInstalled": node.IsInstalled,
 | 
			
		||||
		"uniqueId":    node.UniqueId,
 | 
			
		||||
		"secret":      node.Secret,
 | 
			
		||||
		"maxCPU":      node.MaxCPU,
 | 
			
		||||
		"isOn":        node.IsOn,
 | 
			
		||||
 | 
			
		||||
		"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),
 | 
			
		||||
			"connectionCount":      status.ConnectionCount,
 | 
			
		||||
			"buildVersion":         status.BuildVersion,
 | 
			
		||||
			"cpuPhysicalCount":     status.CPUPhysicalCount,
 | 
			
		||||
			"cpuLogicalCount":      status.CPULogicalCount,
 | 
			
		||||
			"load1m":               fmt.Sprintf("%.2f", status.Load1m),
 | 
			
		||||
			"load5m":               fmt.Sprintf("%.2f", status.Load5m),
 | 
			
		||||
			"load15m":              fmt.Sprintf("%.2f", status.Load15m),
 | 
			
		||||
			"cacheTotalDiskSize":   numberutils.FormatBytes(status.CacheTotalDiskSize),
 | 
			
		||||
			"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		"group":  groupMap,
 | 
			
		||||
		"region": regionMap,
 | 
			
		||||
 | 
			
		||||
		"maxCacheDiskCapacity":   maxCacheDiskCapacity,
 | 
			
		||||
		"maxCacheMemoryCapacity": maxCacheMemoryCapacity,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +1,11 @@
 | 
			
		||||
package node
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"time"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type IndexAction struct {
 | 
			
		||||
@@ -24,236 +20,15 @@ func (this *IndexAction) Init() {
 | 
			
		||||
func (this *IndexAction) RunGet(params struct {
 | 
			
		||||
	NodeId int64
 | 
			
		||||
}) {
 | 
			
		||||
	this.Data["nodeId"] = params.NodeId
 | 
			
		||||
 | 
			
		||||
	nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
 | 
			
		||||
	err := nodeutils.InitNodeInfo(this, params.NodeId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	node := nodeResp.Node
 | 
			
		||||
	if node == nil {
 | 
			
		||||
		this.WriteString("找不到要操作的节点")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var clusterMap maps.Map = nil
 | 
			
		||||
	if node.NodeCluster != nil {
 | 
			
		||||
		clusterId := node.NodeCluster.Id
 | 
			
		||||
		clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.ErrorPage(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		cluster := clusterResp.NodeCluster
 | 
			
		||||
		if cluster != nil {
 | 
			
		||||
			clusterMap = maps.Map{
 | 
			
		||||
				"id":         cluster.Id,
 | 
			
		||||
				"name":       cluster.Name,
 | 
			
		||||
				"installDir": cluster.InstallDir,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// IP地址
 | 
			
		||||
	ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
 | 
			
		||||
		NodeId: params.NodeId,
 | 
			
		||||
		Role:   nodeconfigs.NodeRoleNode,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ipAddressMaps := []maps.Map{}
 | 
			
		||||
	for _, addr := range ipAddressesResp.Addresses {
 | 
			
		||||
		ipAddressMaps = append(ipAddressMaps, maps.Map{
 | 
			
		||||
			"id":        addr.Id,
 | 
			
		||||
			"name":      addr.Name,
 | 
			
		||||
			"ip":        addr.Ip,
 | 
			
		||||
			"canAccess": addr.CanAccess,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// DNS相关
 | 
			
		||||
	dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	dnsRouteMaps := []maps.Map{}
 | 
			
		||||
	recordName := ""
 | 
			
		||||
	recordValue := ""
 | 
			
		||||
	if dnsInfoResp.Node != nil {
 | 
			
		||||
		recordName = dnsInfoResp.Node.NodeClusterDNSName + "." + dnsInfoResp.Node.DnsDomainName
 | 
			
		||||
		recordValue = dnsInfoResp.Node.IpAddr
 | 
			
		||||
		for _, dnsInfo := range dnsInfoResp.Node.Routes {
 | 
			
		||||
			dnsRouteMaps = append(dnsRouteMaps, maps.Map{
 | 
			
		||||
				"name": dnsInfo.Name,
 | 
			
		||||
				"code": dnsInfo.Code,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(dnsRouteMaps) == 0 {
 | 
			
		||||
		dnsRouteMaps = append(dnsRouteMaps, maps.Map{
 | 
			
		||||
			"name": "",
 | 
			
		||||
			"code": "",
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["dnsRoutes"] = dnsRouteMaps
 | 
			
		||||
	this.Data["dnsRecordName"] = recordName
 | 
			
		||||
	this.Data["dnsRecordValue"] = recordValue
 | 
			
		||||
 | 
			
		||||
	// 登录信息
 | 
			
		||||
	var loginMap maps.Map = nil
 | 
			
		||||
	if node.Login != nil {
 | 
			
		||||
		loginParams := maps.Map{}
 | 
			
		||||
		if len(node.Login.Params) > 0 {
 | 
			
		||||
			err = json.Unmarshal(node.Login.Params, &loginParams)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.ErrorPage(err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		grantMap := maps.Map{}
 | 
			
		||||
		grantId := loginParams.GetInt64("grantId")
 | 
			
		||||
		if grantId > 0 {
 | 
			
		||||
			grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.ErrorPage(err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if grantResp.NodeGrant != nil {
 | 
			
		||||
				grantMap = maps.Map{
 | 
			
		||||
					"id":         grantResp.NodeGrant.Id,
 | 
			
		||||
					"name":       grantResp.NodeGrant.Name,
 | 
			
		||||
					"method":     grantResp.NodeGrant.Method,
 | 
			
		||||
					"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		loginMap = maps.Map{
 | 
			
		||||
			"id":     node.Login.Id,
 | 
			
		||||
			"name":   node.Login.Name,
 | 
			
		||||
			"type":   node.Login.Type,
 | 
			
		||||
			"params": loginParams,
 | 
			
		||||
			"grant":  grantMap,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 运行状态
 | 
			
		||||
	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秒之内认为活跃
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检查是否有新版本
 | 
			
		||||
	if len(status.OS) > 0 {
 | 
			
		||||
		checkVersionResp, err := this.RPC().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{
 | 
			
		||||
			Os:             status.OS,
 | 
			
		||||
			Arch:           status.Arch,
 | 
			
		||||
			CurrentVersion: status.BuildVersion,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.ErrorPage(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion
 | 
			
		||||
		this.Data["newVersion"] = checkVersionResp.NewVersion
 | 
			
		||||
	if teaconst.IsPlus {
 | 
			
		||||
		this.RedirectURL("/clusters/cluster/node/boards?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
 | 
			
		||||
	} else {
 | 
			
		||||
		this.Data["shouldUpgrade"] = false
 | 
			
		||||
		this.Data["newVersion"] = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 分组
 | 
			
		||||
	var groupMap maps.Map = nil
 | 
			
		||||
	if node.NodeGroup != nil {
 | 
			
		||||
		groupMap = maps.Map{
 | 
			
		||||
			"id":   node.NodeGroup.Id,
 | 
			
		||||
			"name": node.NodeGroup.Name,
 | 
			
		||||
		this.RedirectURL("/clusters/cluster/node/detail?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + strconv.FormatInt(params.NodeId, 10))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	// 区域
 | 
			
		||||
	var regionMap maps.Map = nil
 | 
			
		||||
	if node.NodeRegion != nil {
 | 
			
		||||
		regionMap = maps.Map{
 | 
			
		||||
			"id":   node.NodeRegion.Id,
 | 
			
		||||
			"name": node.NodeRegion.Name,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 缓存硬盘 & 内存容量
 | 
			
		||||
	var maxCacheDiskCapacity maps.Map = nil
 | 
			
		||||
	if node.MaxCacheDiskCapacity != nil {
 | 
			
		||||
		maxCacheDiskCapacity = maps.Map{
 | 
			
		||||
			"count": node.MaxCacheDiskCapacity.Count,
 | 
			
		||||
			"unit":  node.MaxCacheDiskCapacity.Unit,
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		maxCacheDiskCapacity = maps.Map{
 | 
			
		||||
			"count": 0,
 | 
			
		||||
			"unit":  "gb",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var maxCacheMemoryCapacity maps.Map = nil
 | 
			
		||||
	if node.MaxCacheMemoryCapacity != nil {
 | 
			
		||||
		maxCacheMemoryCapacity = maps.Map{
 | 
			
		||||
			"count": node.MaxCacheMemoryCapacity.Count,
 | 
			
		||||
			"unit":  node.MaxCacheMemoryCapacity.Unit,
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		maxCacheMemoryCapacity = maps.Map{
 | 
			
		||||
			"count": 0,
 | 
			
		||||
			"unit":  "gb",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Data["node"] = maps.Map{
 | 
			
		||||
		"id":          node.Id,
 | 
			
		||||
		"name":        node.Name,
 | 
			
		||||
		"ipAddresses": ipAddressMaps,
 | 
			
		||||
		"cluster":     clusterMap,
 | 
			
		||||
		"login":       loginMap,
 | 
			
		||||
		"installDir":  node.InstallDir,
 | 
			
		||||
		"isInstalled": node.IsInstalled,
 | 
			
		||||
		"uniqueId":    node.UniqueId,
 | 
			
		||||
		"secret":      node.Secret,
 | 
			
		||||
		"maxCPU":      node.MaxCPU,
 | 
			
		||||
		"isOn":        node.IsOn,
 | 
			
		||||
 | 
			
		||||
		"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),
 | 
			
		||||
			"connectionCount":      status.ConnectionCount,
 | 
			
		||||
			"buildVersion":         status.BuildVersion,
 | 
			
		||||
			"cpuPhysicalCount":     status.CPUPhysicalCount,
 | 
			
		||||
			"cpuLogicalCount":      status.CPULogicalCount,
 | 
			
		||||
			"load1m":               fmt.Sprintf("%.2f", status.Load1m),
 | 
			
		||||
			"load5m":               fmt.Sprintf("%.2f", status.Load5m),
 | 
			
		||||
			"load15m":              fmt.Sprintf("%.2f", status.Load15m),
 | 
			
		||||
			"cacheTotalDiskSize":   numberutils.FormatBytes(status.CacheTotalDiskSize),
 | 
			
		||||
			"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		"group":  groupMap,
 | 
			
		||||
		"region": regionMap,
 | 
			
		||||
 | 
			
		||||
		"maxCacheDiskCapacity":   maxCacheDiskCapacity,
 | 
			
		||||
		"maxCacheMemoryCapacity": maxCacheMemoryCapacity,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package node
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
@@ -23,6 +24,13 @@ func (this *LogsAction) RunGet(params struct {
 | 
			
		||||
	Keyword string
 | 
			
		||||
	Level   string
 | 
			
		||||
}) {
 | 
			
		||||
	// 初始化节点信息(用于菜单)
 | 
			
		||||
	err := nodeutils.InitNodeInfo(this, params.NodeId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Data["nodeId"] = params.NodeId
 | 
			
		||||
	this.Data["dayFrom"] = params.DayFrom
 | 
			
		||||
	this.Data["dayTo"] = params.DayTo
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,10 @@
 | 
			
		||||
 | 
			
		||||
package monitor
 | 
			
		||||
 | 
			
		||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type IndexAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
@@ -17,5 +20,12 @@ func (this *IndexAction) RunGet(params struct {
 | 
			
		||||
}) {
 | 
			
		||||
	this.Data["nodeId"] = params.NodeId
 | 
			
		||||
 | 
			
		||||
	// 初始化节点信息(用于菜单)
 | 
			
		||||
	err := nodeutils.InitNodeInfo(this, params.NodeId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package nodeutils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// InitNodeInfo 初始化节点信息
 | 
			
		||||
func InitNodeInfo(action actionutils.ActionInterface, nodeId int64) error {
 | 
			
		||||
	// 节点信息(用于菜单)
 | 
			
		||||
	nodeResp, err := action.RPC().NodeRPC().FindEnabledNode(action.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: nodeId})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if nodeResp.Node == nil {
 | 
			
		||||
		return errors.New("node '" + strconv.FormatInt(nodeId, 10) + "' not found")
 | 
			
		||||
	}
 | 
			
		||||
	var node = nodeResp.Node
 | 
			
		||||
	action.ViewData()["node"] = maps.Map{
 | 
			
		||||
		"id":   node.Id,
 | 
			
		||||
		"name": node.Name,
 | 
			
		||||
	}
 | 
			
		||||
	if node.NodeCluster != nil {
 | 
			
		||||
		action.ViewData()["clusterId"] = node.NodeCluster.Id
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,7 @@ package thresholds
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
@@ -23,6 +24,13 @@ func (this *IndexAction) RunGet(params struct {
 | 
			
		||||
}) {
 | 
			
		||||
	this.Data["nodeId"] = params.NodeId
 | 
			
		||||
 | 
			
		||||
	// 初始化节点信息(用于菜单)
 | 
			
		||||
	err := nodeutils.InitNodeInfo(this, params.NodeId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 列出所有阈值
 | 
			
		||||
	thresholdsResp, err := this.RPC().NodeThresholdRPC().FindAllEnabledNodeThresholds(this.AdminContext(), &pb.FindAllEnabledNodeThresholdsRequest{
 | 
			
		||||
		Role:          "node",
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ package clusterutils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
 | 
			
		||||
@@ -36,7 +35,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
 | 
			
		||||
	action.Data["clusterId"] = clusterId
 | 
			
		||||
 | 
			
		||||
	if clusterId > 0 {
 | 
			
		||||
		var ctx = actionPtr.(rpc.ContextInterface).AdminContext()
 | 
			
		||||
		var ctx = actionPtr.(actionutils.ActionInterface).AdminContext()
 | 
			
		||||
		cluster, err := dao.SharedNodeClusterDAO.FindEnabledNodeCluster(ctx, clusterId)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logs.Error(err)
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
 | 
			
		||||
			logs.Error(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		clusterResp, err := rpcClient.NSClusterRPC().FindEnabledNSCluster(actionPtr.(rpc.ContextInterface).AdminContext(), &pb.FindEnabledNSClusterRequest{
 | 
			
		||||
		clusterResp, err := rpcClient.NSClusterRPC().FindEnabledNSCluster(actionPtr.(actionutils.ActionInterface).AdminContext(), &pb.FindEnabledNSClusterRequest{
 | 
			
		||||
			NsClusterId: clusterId,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
// 指标图表
 | 
			
		||||
Vue.component("metric-chart", {
 | 
			
		||||
	props: ["v-chart", "v-stats", "v-period-unit"],
 | 
			
		||||
	props: ["v-chart", "v-stats", "v-period-unit", "v-value-type"],
 | 
			
		||||
	mounted: function () {
 | 
			
		||||
		this.load()
 | 
			
		||||
	},
 | 
			
		||||
@@ -108,6 +108,21 @@ Vue.component("metric-chart", {
 | 
			
		||||
			let values = this.stats.map(function (v) {
 | 
			
		||||
				return v.value
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			let axis = {unit: "", divider: 1}
 | 
			
		||||
			switch (this.vValueType) {
 | 
			
		||||
				case "count":
 | 
			
		||||
					axis = teaweb.countAxis(values, function (v) {
 | 
			
		||||
						return v
 | 
			
		||||
					})
 | 
			
		||||
					break
 | 
			
		||||
				case "bytes":
 | 
			
		||||
					axis = teaweb.bytesAxis(values, function (v) {
 | 
			
		||||
						return v
 | 
			
		||||
					})
 | 
			
		||||
					break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			let that = this
 | 
			
		||||
			chart.setOption({
 | 
			
		||||
				xAxis: {
 | 
			
		||||
@@ -115,7 +130,13 @@ Vue.component("metric-chart", {
 | 
			
		||||
						return that.formatTime(v.time)
 | 
			
		||||
					})
 | 
			
		||||
				},
 | 
			
		||||
				yAxis: {},
 | 
			
		||||
				yAxis: {
 | 
			
		||||
					axisLabel: {
 | 
			
		||||
						formatter: function (value) {
 | 
			
		||||
							return value + axis.unit
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				tooltip: {
 | 
			
		||||
					show: true,
 | 
			
		||||
					trigger: "item",
 | 
			
		||||
@@ -134,7 +155,9 @@ Vue.component("metric-chart", {
 | 
			
		||||
					{
 | 
			
		||||
						name: name,
 | 
			
		||||
						type: "bar",
 | 
			
		||||
						data: values,
 | 
			
		||||
						data: values.map(function (v){
 | 
			
		||||
							return v/axis.divider
 | 
			
		||||
						}),
 | 
			
		||||
						itemStyle: {
 | 
			
		||||
							color: "#9DD3E8"
 | 
			
		||||
						},
 | 
			
		||||
@@ -151,6 +174,21 @@ Vue.component("metric-chart", {
 | 
			
		||||
			let values = this.stats.map(function (v) {
 | 
			
		||||
				return v.value
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			let axis = {unit: "", divider: 1}
 | 
			
		||||
			switch (this.vValueType) {
 | 
			
		||||
				case "count":
 | 
			
		||||
					axis = teaweb.countAxis(values, function (v) {
 | 
			
		||||
						return v
 | 
			
		||||
					})
 | 
			
		||||
					break
 | 
			
		||||
				case "bytes":
 | 
			
		||||
					axis = teaweb.bytesAxis(values, function (v) {
 | 
			
		||||
						return v
 | 
			
		||||
					})
 | 
			
		||||
					break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			let that = this
 | 
			
		||||
			chart.setOption({
 | 
			
		||||
				xAxis: {
 | 
			
		||||
@@ -158,7 +196,13 @@ Vue.component("metric-chart", {
 | 
			
		||||
						return that.formatTime(v.time)
 | 
			
		||||
					})
 | 
			
		||||
				},
 | 
			
		||||
				yAxis: {},
 | 
			
		||||
				yAxis: {
 | 
			
		||||
					axisLabel: {
 | 
			
		||||
						formatter: function (value) {
 | 
			
		||||
							return value + axis.unit
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				tooltip: {
 | 
			
		||||
					show: true,
 | 
			
		||||
					trigger: "item",
 | 
			
		||||
@@ -177,7 +221,9 @@ Vue.component("metric-chart", {
 | 
			
		||||
					{
 | 
			
		||||
						name: name,
 | 
			
		||||
						type: "line",
 | 
			
		||||
						data: values,
 | 
			
		||||
						data: values.map(function (v) {
 | 
			
		||||
							return v / axis.divider
 | 
			
		||||
						}),
 | 
			
		||||
						itemStyle: {
 | 
			
		||||
							color: "#9DD3E8"
 | 
			
		||||
						},
 | 
			
		||||
@@ -190,6 +236,20 @@ Vue.component("metric-chart", {
 | 
			
		||||
			let values = this.stats.map(function (v) {
 | 
			
		||||
				return v.value
 | 
			
		||||
			})
 | 
			
		||||
			let axis = {unit: "", divider: 1}
 | 
			
		||||
			switch (this.vValueType) {
 | 
			
		||||
				case "count":
 | 
			
		||||
					axis = teaweb.countAxis(values, function (v) {
 | 
			
		||||
						return v
 | 
			
		||||
					})
 | 
			
		||||
					break
 | 
			
		||||
				case "bytes":
 | 
			
		||||
					axis = teaweb.bytesAxis(values, function (v) {
 | 
			
		||||
						return v
 | 
			
		||||
					})
 | 
			
		||||
					break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			let that = this
 | 
			
		||||
			chart.setOption({
 | 
			
		||||
				xAxis: {
 | 
			
		||||
@@ -212,7 +272,13 @@ Vue.component("metric-chart", {
 | 
			
		||||
						return stat.keys[0] + ": " + stat.value + ",占比:" + percent + "%"
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				yAxis: {},
 | 
			
		||||
				yAxis: {
 | 
			
		||||
					axisLabel: {
 | 
			
		||||
						formatter: function (value) {
 | 
			
		||||
							return value + axis.unit
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				grid: {
 | 
			
		||||
					left: 40,
 | 
			
		||||
					top: 10,
 | 
			
		||||
@@ -223,7 +289,9 @@ Vue.component("metric-chart", {
 | 
			
		||||
					{
 | 
			
		||||
						name: name,
 | 
			
		||||
						type: "bar",
 | 
			
		||||
						data: values,
 | 
			
		||||
						data: values.map(function (v) {
 | 
			
		||||
							return v / axis.divider
 | 
			
		||||
						}),
 | 
			
		||||
						itemStyle: {
 | 
			
		||||
							color: "#9DD3E8"
 | 
			
		||||
						},
 | 
			
		||||
@@ -272,9 +340,9 @@ Vue.component("metric-chart", {
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	template: `<div style="float: left" :style="{'width': width}">
 | 
			
		||||
	<div>{{chart.name}}</div>
 | 
			
		||||
	<h4>{{chart.name}} <span>(指标)</span></h4>
 | 
			
		||||
	<div class="ui divider"></div>
 | 
			
		||||
	<div style="height: 20em; overflow-y: auto" :id="chartId" class="scroll-box"></div>
 | 
			
		||||
	<div style="height: 20em; padding-bottom: 1em; " :id="chartId" :class="{'scroll-box': chart.type == 'table'}"></div>
 | 
			
		||||
</div>`
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -362,6 +362,7 @@ window.teaweb = {
 | 
			
		||||
		let tooltipFunc = options.tooltip
 | 
			
		||||
		let axis = options.axis
 | 
			
		||||
		let valueFunc = options.value
 | 
			
		||||
		let click = options.click
 | 
			
		||||
 | 
			
		||||
		let chartBox = document.getElementById(chartId)
 | 
			
		||||
		if (chartBox == null) {
 | 
			
		||||
@@ -406,9 +407,14 @@ window.teaweb = {
 | 
			
		||||
					barWidth: "20em"
 | 
			
		||||
				}
 | 
			
		||||
			],
 | 
			
		||||
			animation: true
 | 
			
		||||
			animation: true,
 | 
			
		||||
		}
 | 
			
		||||
		chart.setOption(option)
 | 
			
		||||
		if (click != null) {
 | 
			
		||||
			chart.on("click", function (args) {
 | 
			
		||||
				click.call(this, args, values)
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
		chart.resize()
 | 
			
		||||
	},
 | 
			
		||||
	renderLineChart: function (options) {
 | 
			
		||||
 
 | 
			
		||||
@@ -669,6 +669,9 @@ td {
 | 
			
		||||
.source-code-box .CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
 | 
			
		||||
  border-radius: 2px;
 | 
			
		||||
}
 | 
			
		||||
.scroll-box {
 | 
			
		||||
  overflow-y: auto;
 | 
			
		||||
}
 | 
			
		||||
.scroll-box::-webkit-scrollbar {
 | 
			
		||||
  width: 4px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -747,6 +747,10 @@ td {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 表格
 | 
			
		||||
.scroll-box {
 | 
			
		||||
	overflow-y: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.scroll-box::-webkit-scrollbar {
 | 
			
		||||
	width: 4px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -47,13 +47,13 @@
 | 
			
		||||
<div class="ui divider"></div>
 | 
			
		||||
 | 
			
		||||
<!-- 域名排行 -->
 | 
			
		||||
<h4>域名排行 <span>(24小时)</span></h4>
 | 
			
		||||
<h4>域名访问排行 <span>(24小时)</span></h4>
 | 
			
		||||
<div class="chart-box" id="top-domains-chart"></div>
 | 
			
		||||
 | 
			
		||||
<div class="ui divider"></div>
 | 
			
		||||
 | 
			
		||||
<!-- 节点排行 -->
 | 
			
		||||
<h4>节点排行 <span>(24小时)</span></h4>
 | 
			
		||||
<h4>节点访问排行 <span>(24小时)</span></h4>
 | 
			
		||||
<div class="chart-box" id="top-nodes-chart"></div>
 | 
			
		||||
 | 
			
		||||
<div class="ui divider"></div>
 | 
			
		||||
@@ -67,3 +67,9 @@
 | 
			
		||||
<div class="chart-box" id="cpu-chart" v-show="nodeStatusTab == 'cpu'"></div>
 | 
			
		||||
<div class="chart-box" id="memory-chart" v-show="nodeStatusTab == 'memory'"></div>
 | 
			
		||||
<div class="chart-box" id="load-chart" v-show="nodeStatusTab == 'load'"></div>
 | 
			
		||||
 | 
			
		||||
<!-- 指标 -->
 | 
			
		||||
<div class="ui divider" v-if="metricCharts.length > 0"></div>
 | 
			
		||||
<metric-board>
 | 
			
		||||
    <metric-chart v-for="chart in metricCharts" :key="chart.id" :v-chart="chart.chart" :v-stats="chart.stats" :v-period="chart.item.period" :v-period-unit="chart.item.periodUnit" :v-value-type="chart.item.valueType"></metric-chart>
 | 
			
		||||
</metric-board>
 | 
			
		||||
@@ -7,7 +7,7 @@ Tea.context(function () {
 | 
			
		||||
	this.$delay(function () {
 | 
			
		||||
		this.reloadHourlyTrafficChart()
 | 
			
		||||
		this.reloadHourlyRequestsChart()
 | 
			
		||||
		this.reloadTopNodeStatsChart()
 | 
			
		||||
		this.reloadTopNodesChart()
 | 
			
		||||
		this.reloadTopDomainsChart()
 | 
			
		||||
		this.reloadCPUChart()
 | 
			
		||||
	})
 | 
			
		||||
@@ -265,7 +265,7 @@ Tea.context(function () {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 节点排行
 | 
			
		||||
	this.reloadTopNodeStatsChart = function () {
 | 
			
		||||
	this.reloadTopNodesChart = function () {
 | 
			
		||||
		let that = this
 | 
			
		||||
		let axis = teaweb.countAxis(this.topNodeStats, function (v) {
 | 
			
		||||
			return v.countRequests
 | 
			
		||||
@@ -283,7 +283,10 @@ Tea.context(function () {
 | 
			
		||||
			value: function (v) {
 | 
			
		||||
				return v.countRequests / axis.divider;
 | 
			
		||||
			},
 | 
			
		||||
			axis: axis
 | 
			
		||||
			axis: axis,
 | 
			
		||||
			click: function (args, stats) {
 | 
			
		||||
				window.location = "/clusters/cluster/node?nodeId=" + stats[args.dataIndex].nodeId + "&clusterId=" + that.clusterId
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -332,7 +335,7 @@ Tea.context(function () {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadCPUChart = function () {
 | 
			
		||||
		let axis = {unit: "", divider: 1}
 | 
			
		||||
		let axis = {unit: "%", divider: 1}
 | 
			
		||||
		teaweb.renderLineChart({
 | 
			
		||||
			id: "cpu-chart",
 | 
			
		||||
			name: "CPU",
 | 
			
		||||
@@ -352,7 +355,7 @@ Tea.context(function () {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadMemoryChart = function () {
 | 
			
		||||
		let axis = {unit: "", divider: 1}
 | 
			
		||||
		let axis = {unit: "%", divider: 1}
 | 
			
		||||
		teaweb.renderLineChart({
 | 
			
		||||
			id: "memory-chart",
 | 
			
		||||
			name: "内存",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,12 @@
 | 
			
		||||
<second-menu>
 | 
			
		||||
	<menu-item :href="'/clusters/cluster/nodes?clusterId=' + clusterId">节点列表</menu-item>
 | 
			
		||||
	<span class="item">|</span>
 | 
			
		||||
	<menu-item :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + nodeId" code="node">节点详情</menu-item>
 | 
			
		||||
    <menu-item :href="'/clusters/cluster/node/monitor?clusterId=' + clusterId + '&nodeId=' + nodeId" code="monitor" v-if="teaIsPlus">监控图表</menu-item>
 | 
			
		||||
    <menu-item :href="'/clusters/cluster/node/thresholds?clusterId=' + clusterId + '&nodeId=' + nodeId" code="threshold" v-if="teaIsPlus">阈值设置</menu-item>
 | 
			
		||||
    <menu-item :href="'/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + nodeId" code="log">运行日志</menu-item>
 | 
			
		||||
	<menu-item :href="'/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + nodeId" code="update">修改设置</menu-item>
 | 
			
		||||
	<menu-item :href="'/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + nodeId" code="install">安装节点</menu-item>
 | 
			
		||||
	<menu-item :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id" code="node"
 | 
			
		||||
               v-if="!teaIsPlus">"{{node.name}}"节点详情</menu-item>
 | 
			
		||||
    <menu-item :href="'/clusters/cluster/node/boards?clusterId=' + clusterId + '&nodeId=' + node.id" code="board" v-if="teaIsPlus">"{{node.name}}" 节点看板</menu-item>
 | 
			
		||||
    <menu-item :href="'/clusters/cluster/node/detail?clusterId=' + clusterId + '&nodeId=' + node.id" code="node" v-if="teaIsPlus">节点详情</menu-item>
 | 
			
		||||
    <menu-item :href="'/clusters/cluster/node/thresholds?clusterId=' + clusterId + '&nodeId=' + node.id" code="threshold" v-if="teaIsPlus">阈值设置</menu-item>
 | 
			
		||||
    <menu-item :href="'/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + node.id" code="log">运行日志</menu-item>
 | 
			
		||||
	<menu-item :href="'/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + node.id" code="update">修改设置</menu-item>
 | 
			
		||||
	<menu-item :href="'/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id" code="install">安装节点</menu-item>
 | 
			
		||||
</second-menu>
 | 
			
		||||
							
								
								
									
										32
									
								
								web/views/@default/clusters/cluster/node/boards/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web/views/@default/clusters/cluster/node/boards/index.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
.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: 20em;
 | 
			
		||||
}
 | 
			
		||||
h4 span {
 | 
			
		||||
  font-size: 0.8em;
 | 
			
		||||
  color: grey;
 | 
			
		||||
}
 | 
			
		||||
/*# sourceMappingURL=index.css.map */
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;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"}
 | 
			
		||||
							
								
								
									
										109
									
								
								web/views/@default/clusters/cluster/node/boards/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								web/views/@default/clusters/cluster/node/boards/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
{$layout}
 | 
			
		||||
{$template "../node_menu"}
 | 
			
		||||
{$template "/echarts"}
 | 
			
		||||
 | 
			
		||||
<!-- 概况 -->
 | 
			
		||||
<div class="ui four columns grid">
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>在线状态</h4>
 | 
			
		||||
        <div class="value">
 | 
			
		||||
            <span class="green" v-if="board.isActive">在线</span>
 | 
			
		||||
            <span class="red" v-else>离线</span>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>下行流量</h4>
 | 
			
		||||
        <div class="value"><span>{{board.trafficOutBytes}}</span>/分钟</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>上行流量</h4>
 | 
			
		||||
        <div class="value"><span>{{board.trafficInBytes}}</span>/分钟</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>连接数</h4>
 | 
			
		||||
        <div class="value"><span>{{board.countConnections}}</span>/分钟</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>当前访问量</h4>
 | 
			
		||||
        <div class="value"><span>{{board.countRequests}}</span>/分钟</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>当前攻击访问量</h4>
 | 
			
		||||
        <div class="value"><span :class="{red: board.countAttackRequests != '0'}">{{board.countAttackRequests}}</span>/分钟</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>磁盘缓存用量</h4>
 | 
			
		||||
        <div class="value"><span>{{board.cacheDiskSize}}</span></div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>内存缓存用量</h4>
 | 
			
		||||
        <div class="value"><span>{{board.cacheMemorySize}}</span></div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>CPU</h4>
 | 
			
		||||
        <div class="value"><span :class="{red: board.cpuUsage > 80}">{{board.cpuUsage}}</span>%</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>内存</h4>
 | 
			
		||||
        <div class="value"><span :class="{red: board.memoryUsage > 80}">{{board.memoryUsage}}</span>%</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>总内存</h4>
 | 
			
		||||
        <div class="value"><span>{{board.memoryTotalSize}}</span>G</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui column">
 | 
			
		||||
        <h4>负载</h4>
 | 
			
		||||
        <div class="value"><span :class="{red: board.load > 20}">{{board.load}}</span>/分钟</div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="ui divider"></div>
 | 
			
		||||
 | 
			
		||||
<div class="ui menu tabular">
 | 
			
		||||
    <a href="" class="item" :class="{active: trafficTab == 'hourly'}" @click.prevent="selectTrafficTab('hourly')">24小时流量趋势</a>
 | 
			
		||||
    <a href="" class="item" :class="{active: trafficTab == 'daily'}" @click.prevent="selectTrafficTab('daily')">15天流量趋势</a>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- 按小时统计流量 -->
 | 
			
		||||
<div class="chart-box" id="hourly-traffic-chart" v-show="trafficTab == 'hourly'"></div>
 | 
			
		||||
 | 
			
		||||
<!-- 按日统计流量 -->
 | 
			
		||||
<div class="chart-box" id="daily-traffic-chart" v-show="trafficTab == 'daily'"></div>
 | 
			
		||||
 | 
			
		||||
<div class="ui divider"></div>
 | 
			
		||||
 | 
			
		||||
<div class="ui menu tabular">
 | 
			
		||||
    <a href="" class="item" :class="{active: requestsTab == 'hourly'}" @click.prevent="selectRequestsTab('hourly')">24小时访问量趋势</a>
 | 
			
		||||
    <a href="" class="item" :class="{active: requestsTab == 'daily'}" @click.prevent="selectRequestsTab('daily')">15天访问量趋势</a>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- 按小时统计访问量 -->
 | 
			
		||||
<div class="chart-box" id="hourly-requests-chart" v-show="requestsTab == 'hourly'"></div>
 | 
			
		||||
 | 
			
		||||
<!-- 按日统计访问量 -->
 | 
			
		||||
<div class="chart-box" id="daily-requests-chart" v-show="requestsTab == 'daily'"></div>
 | 
			
		||||
 | 
			
		||||
<div class="ui divider"></div>
 | 
			
		||||
 | 
			
		||||
<!-- 域名排行 -->
 | 
			
		||||
<h4>域名访问排行 <span>(24小时)</span></h4>
 | 
			
		||||
<div class="chart-box" id="top-domains-chart"></div>
 | 
			
		||||
 | 
			
		||||
<div class="ui divider"></div>
 | 
			
		||||
 | 
			
		||||
<!-- CPU、内存、负载 -->
 | 
			
		||||
<div class="ui menu tabular">
 | 
			
		||||
    <a href="" class="item" :class="{active: nodeStatusTab == 'cpu'}" @click.prevent="selectNodeStatusTab('cpu')">节点CPU</a>
 | 
			
		||||
    <a href="" class="item" :class="{active: nodeStatusTab == 'memory'}" @click.prevent="selectNodeStatusTab('memory')">节点内存</a>
 | 
			
		||||
    <a href="" class="item" :class="{active: nodeStatusTab == 'load'}" @click.prevent="selectNodeStatusTab('load')">节点负载</a>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="chart-box" id="cpu-chart" v-show="nodeStatusTab == 'cpu'"></div>
 | 
			
		||||
<div class="chart-box" id="memory-chart" v-show="nodeStatusTab == 'memory'"></div>
 | 
			
		||||
<div class="chart-box" id="load-chart" v-show="nodeStatusTab == 'load'"></div>
 | 
			
		||||
 | 
			
		||||
<!-- 指标 -->
 | 
			
		||||
<div class="ui divider" v-if="metricCharts.length > 0"></div>
 | 
			
		||||
<metric-board>
 | 
			
		||||
    <metric-chart v-for="chart in metricCharts" :key="chart.id" :v-chart="chart.chart" :v-stats="chart.stats" :v-period="chart.item.period" :v-period-unit="chart.item.periodUnit" :v-value-type="chart.item.valueType"></metric-chart>
 | 
			
		||||
</metric-board>
 | 
			
		||||
							
								
								
									
										403
									
								
								web/views/@default/clusters/cluster/node/boards/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										403
									
								
								web/views/@default/clusters/cluster/node/boards/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,403 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
	this.formatCount = function (count) {
 | 
			
		||||
		if (count < 1000) {
 | 
			
		||||
			return count.toString()
 | 
			
		||||
		}
 | 
			
		||||
		if (count < 1000 * 1000) {
 | 
			
		||||
			return (Math.round(count / 1000 * 100) / 100) + "K"
 | 
			
		||||
		}
 | 
			
		||||
		return (Math.round(count / 1000 / 1000 * 100) / 100) + "M"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.board.trafficInBytes = teaweb.formatBytes(this.board.trafficInBytes)
 | 
			
		||||
	this.board.trafficOutBytes = teaweb.formatBytes(this.board.trafficOutBytes)
 | 
			
		||||
	this.board.countConnections = this.formatCount(this.board.countConnections)
 | 
			
		||||
	this.board.countRequests = this.formatCount(this.board.countRequests)
 | 
			
		||||
	this.board.countAttackRequests = this.formatCount(this.board.countAttackRequests)
 | 
			
		||||
	this.board.cpuUsage = Math.round(this.board.cpuUsage * 100 * 100) / 100
 | 
			
		||||
	this.board.memoryUsage = Math.round(this.board.memoryUsage * 100 * 100) / 100
 | 
			
		||||
	this.board.memoryTotalSize = Math.round(this.board.memoryTotalSize / 1024 / 1024 / 1024)
 | 
			
		||||
	this.board.load = Math.round(this.board.load * 100) / 100
 | 
			
		||||
	this.board.cacheDiskSize = teaweb.formatBytes(this.board.cacheDiskSize)
 | 
			
		||||
	this.board.cacheMemorySize = teaweb.formatBytes(this.board.cacheMemorySize)
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 流量统计
 | 
			
		||||
	 */
 | 
			
		||||
	this.trafficTab = "hourly"
 | 
			
		||||
 | 
			
		||||
	this.$delay(function () {
 | 
			
		||||
		this.reloadHourlyTrafficChart()
 | 
			
		||||
		this.reloadHourlyRequestsChart()
 | 
			
		||||
		this.reloadTopDomainsChart()
 | 
			
		||||
		this.reloadCPUChart()
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	this.selectTrafficTab = function (tab) {
 | 
			
		||||
		this.trafficTab = tab
 | 
			
		||||
		if (tab == "hourly") {
 | 
			
		||||
			this.$delay(function () {
 | 
			
		||||
				this.reloadHourlyTrafficChart()
 | 
			
		||||
			})
 | 
			
		||||
		} else if (tab == "daily") {
 | 
			
		||||
			this.$delay(function () {
 | 
			
		||||
				this.reloadDailyTrafficChart()
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadHourlyTrafficChart = function () {
 | 
			
		||||
		let stats = this.hourlyStats
 | 
			
		||||
		this.reloadTrafficChart("hourly-traffic-chart", "流量统计", stats, function (args) {
 | 
			
		||||
			if (args.seriesIndex == 0) {
 | 
			
		||||
				return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时 流量: " + teaweb.formatBytes(stats[args.dataIndex].bytes)
 | 
			
		||||
			}
 | 
			
		||||
			if (args.seriesIndex == 1) {
 | 
			
		||||
				let ratio = 0
 | 
			
		||||
				if (stats[args.dataIndex].bytes > 0) {
 | 
			
		||||
					ratio = Math.round(stats[args.dataIndex].cachedBytes * 10000 / stats[args.dataIndex].bytes) / 100
 | 
			
		||||
				}
 | 
			
		||||
				return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时 缓存流量: " + teaweb.formatBytes(stats[args.dataIndex].cachedBytes) + ", 命中率:" + ratio + "%"
 | 
			
		||||
			}
 | 
			
		||||
			return ""
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadDailyTrafficChart = function () {
 | 
			
		||||
		let stats = this.dailyStats
 | 
			
		||||
		this.reloadTrafficChart("daily-traffic-chart", "流量统计", stats, function (args) {
 | 
			
		||||
			if (args.seriesIndex == 0) {
 | 
			
		||||
				return stats[args.dataIndex].day + " 流量: " + teaweb.formatBytes(stats[args.dataIndex].bytes)
 | 
			
		||||
			}
 | 
			
		||||
			if (args.seriesIndex == 1) {
 | 
			
		||||
				let ratio = 0
 | 
			
		||||
				if (stats[args.dataIndex].bytes > 0) {
 | 
			
		||||
					ratio = Math.round(stats[args.dataIndex].cachedBytes * 10000 / stats[args.dataIndex].bytes) / 100
 | 
			
		||||
				}
 | 
			
		||||
				return stats[args.dataIndex].day + " 缓存流量: " + teaweb.formatBytes(stats[args.dataIndex].cachedBytes) + ", 命中率:" + ratio + "%"
 | 
			
		||||
			}
 | 
			
		||||
			return ""
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadTrafficChart = function (chartId, name, stats, tooltipFunc) {
 | 
			
		||||
		let chartBox = document.getElementById(chartId)
 | 
			
		||||
		if (chartBox == null) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let axis = teaweb.bytesAxis(stats, function (v) {
 | 
			
		||||
			return Math.max(v.bytes, v.cachedBytes)
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		let chart = echarts.init(chartBox)
 | 
			
		||||
		let option = {
 | 
			
		||||
			xAxis: {
 | 
			
		||||
				data: stats.map(function (v) {
 | 
			
		||||
					if (v.hour != null) {
 | 
			
		||||
						return v.hour
 | 
			
		||||
					}
 | 
			
		||||
					return v.day
 | 
			
		||||
				})
 | 
			
		||||
			},
 | 
			
		||||
			yAxis: {
 | 
			
		||||
				axisLabel: {
 | 
			
		||||
					formatter: function (value) {
 | 
			
		||||
						return value + axis.unit
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			tooltip: {
 | 
			
		||||
				show: true,
 | 
			
		||||
				trigger: "item",
 | 
			
		||||
				formatter: tooltipFunc
 | 
			
		||||
			},
 | 
			
		||||
			grid: {
 | 
			
		||||
				left: 50,
 | 
			
		||||
				top: 40,
 | 
			
		||||
				right: 20,
 | 
			
		||||
				bottom: 20
 | 
			
		||||
			},
 | 
			
		||||
			series: [
 | 
			
		||||
				{
 | 
			
		||||
					name: "流量",
 | 
			
		||||
					type: "line",
 | 
			
		||||
					data: stats.map(function (v) {
 | 
			
		||||
						return v.bytes / axis.divider
 | 
			
		||||
					}),
 | 
			
		||||
					itemStyle: {
 | 
			
		||||
						color: "#9DD3E8"
 | 
			
		||||
					},
 | 
			
		||||
					areaStyle: {
 | 
			
		||||
						color: "#9DD3E8"
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "缓存流量",
 | 
			
		||||
					type: "line",
 | 
			
		||||
					data: stats.map(function (v) {
 | 
			
		||||
						return v.cachedBytes / axis.divider
 | 
			
		||||
					}),
 | 
			
		||||
					itemStyle: {
 | 
			
		||||
						color: "#61A0A8"
 | 
			
		||||
					},
 | 
			
		||||
					areaStyle: {
 | 
			
		||||
						color: "#61A0A8"
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			],
 | 
			
		||||
			legend: {
 | 
			
		||||
				data: ['流量', '缓存流量']
 | 
			
		||||
			},
 | 
			
		||||
			animation: true
 | 
			
		||||
		}
 | 
			
		||||
		chart.setOption(option)
 | 
			
		||||
		chart.resize()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 请求数统计
 | 
			
		||||
	 */
 | 
			
		||||
	this.requestsTab = "hourly"
 | 
			
		||||
 | 
			
		||||
	this.selectRequestsTab = function (tab) {
 | 
			
		||||
		this.requestsTab = tab
 | 
			
		||||
		if (tab == "hourly") {
 | 
			
		||||
			this.$delay(function () {
 | 
			
		||||
				this.reloadHourlyRequestsChart()
 | 
			
		||||
			})
 | 
			
		||||
		} else if (tab == "daily") {
 | 
			
		||||
			this.$delay(function () {
 | 
			
		||||
				this.reloadDailyRequestsChart()
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadHourlyRequestsChart = function () {
 | 
			
		||||
		let stats = this.hourlyStats
 | 
			
		||||
		this.reloadRequestsChart("hourly-requests-chart", "请求数统计", stats, function (args) {
 | 
			
		||||
			if (args.seriesIndex == 0) {
 | 
			
		||||
				return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时 请求数: " + teaweb.formatNumber(stats[args.dataIndex].countRequests)
 | 
			
		||||
			}
 | 
			
		||||
			if (args.seriesIndex == 1) {
 | 
			
		||||
				let ratio = 0
 | 
			
		||||
				if (stats[args.dataIndex].countRequests > 0) {
 | 
			
		||||
					ratio = Math.round(stats[args.dataIndex].countCachedRequests * 10000 / stats[args.dataIndex].countRequests) / 100
 | 
			
		||||
				}
 | 
			
		||||
				return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时 缓存请求数: " + teaweb.formatNumber(stats[args.dataIndex].countCachedRequests) + ", 命中率:" + ratio + "%"
 | 
			
		||||
			}
 | 
			
		||||
			return ""
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadDailyRequestsChart = function () {
 | 
			
		||||
		let stats = this.dailyStats
 | 
			
		||||
		this.reloadRequestsChart("daily-requests-chart", "请求数统计", stats, function (args) {
 | 
			
		||||
			if (args.seriesIndex == 0) {
 | 
			
		||||
				return stats[args.dataIndex].day + " 请求数: " + teaweb.formatNumber(stats[args.dataIndex].countRequests)
 | 
			
		||||
			}
 | 
			
		||||
			if (args.seriesIndex == 1) {
 | 
			
		||||
				let ratio = 0
 | 
			
		||||
				if (stats[args.dataIndex].countRequests > 0) {
 | 
			
		||||
					ratio = Math.round(stats[args.dataIndex].countCachedRequests * 10000 / stats[args.dataIndex].countRequests) / 100
 | 
			
		||||
				}
 | 
			
		||||
				return stats[args.dataIndex].day + " 缓存请求数: " + teaweb.formatNumber(stats[args.dataIndex].countCachedRequests) + ", 命中率:" + ratio + "%"
 | 
			
		||||
			}
 | 
			
		||||
			return ""
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadRequestsChart = function (chartId, name, stats, tooltipFunc) {
 | 
			
		||||
		let chartBox = document.getElementById(chartId)
 | 
			
		||||
		if (chartBox == null) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let axis = teaweb.countAxis(stats, function (v) {
 | 
			
		||||
			return Math.max(v.countRequests, v.countCachedRequests)
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		let chart = echarts.init(chartBox)
 | 
			
		||||
		let option = {
 | 
			
		||||
			xAxis: {
 | 
			
		||||
				data: stats.map(function (v) {
 | 
			
		||||
					if (v.hour != null) {
 | 
			
		||||
						return v.hour
 | 
			
		||||
					}
 | 
			
		||||
					if (v.day != null) {
 | 
			
		||||
						return v.day
 | 
			
		||||
					}
 | 
			
		||||
					return ""
 | 
			
		||||
				})
 | 
			
		||||
			},
 | 
			
		||||
			yAxis: {
 | 
			
		||||
				axisLabel: {
 | 
			
		||||
					formatter: function (value) {
 | 
			
		||||
						return value + axis.unit
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			tooltip: {
 | 
			
		||||
				show: true,
 | 
			
		||||
				trigger: "item",
 | 
			
		||||
				formatter: tooltipFunc
 | 
			
		||||
			},
 | 
			
		||||
			grid: {
 | 
			
		||||
				left: 50,
 | 
			
		||||
				top: 40,
 | 
			
		||||
				right: 20,
 | 
			
		||||
				bottom: 20
 | 
			
		||||
			},
 | 
			
		||||
			series: [
 | 
			
		||||
				{
 | 
			
		||||
					name: "请求数",
 | 
			
		||||
					type: "line",
 | 
			
		||||
					data: stats.map(function (v) {
 | 
			
		||||
						return v.countRequests / axis.divider
 | 
			
		||||
					}),
 | 
			
		||||
					itemStyle: {
 | 
			
		||||
						color: "#9DD3E8"
 | 
			
		||||
					},
 | 
			
		||||
					areaStyle: {
 | 
			
		||||
						color: "#9DD3E8"
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name: "缓存请求数",
 | 
			
		||||
					type: "line",
 | 
			
		||||
					data: stats.map(function (v) {
 | 
			
		||||
						return v.countCachedRequests / axis.divider
 | 
			
		||||
					}),
 | 
			
		||||
					itemStyle: {
 | 
			
		||||
						color: "#61A0A8"
 | 
			
		||||
					},
 | 
			
		||||
					areaStyle: {
 | 
			
		||||
						color: "#61A0A8"
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			],
 | 
			
		||||
			legend: {
 | 
			
		||||
				data: ['请求数', '缓存请求数']
 | 
			
		||||
			},
 | 
			
		||||
			animation: true
 | 
			
		||||
		}
 | 
			
		||||
		chart.setOption(option)
 | 
			
		||||
		chart.resize()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 域名排行
 | 
			
		||||
	this.reloadTopDomainsChart = function () {
 | 
			
		||||
		let axis = teaweb.countAxis(this.topDomainStats, function (v) {
 | 
			
		||||
			return v.countRequests
 | 
			
		||||
		})
 | 
			
		||||
		teaweb.renderBarChart({
 | 
			
		||||
			id: "top-domains-chart",
 | 
			
		||||
			name: "域名",
 | 
			
		||||
			values: this.topDomainStats,
 | 
			
		||||
			x: function (v) {
 | 
			
		||||
				return v.domain
 | 
			
		||||
			},
 | 
			
		||||
			tooltip: function (args, stats) {
 | 
			
		||||
				return stats[args.dataIndex].domain + "<br/>请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "<br/>流量:" + teaweb.formatBytes(stats[args.dataIndex].bytes)
 | 
			
		||||
			},
 | 
			
		||||
			value: function (v) {
 | 
			
		||||
				return v.countRequests / axis.divider;
 | 
			
		||||
			},
 | 
			
		||||
			axis: axis
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 系统信息
 | 
			
		||||
	 */
 | 
			
		||||
	this.nodeStatusTab = "cpu"
 | 
			
		||||
 | 
			
		||||
	this.selectNodeStatusTab = function (tab) {
 | 
			
		||||
		this.nodeStatusTab = tab
 | 
			
		||||
		this.$delay(function () {
 | 
			
		||||
			switch (tab) {
 | 
			
		||||
				case "cpu":
 | 
			
		||||
					this.reloadCPUChart()
 | 
			
		||||
					break
 | 
			
		||||
				case "memory":
 | 
			
		||||
					this.reloadMemoryChart()
 | 
			
		||||
					break
 | 
			
		||||
				case "load":
 | 
			
		||||
					this.reloadLoadChart()
 | 
			
		||||
					break
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadCPUChart = function () {
 | 
			
		||||
		let axis = {unit: "%", divider: 1}
 | 
			
		||||
		teaweb.renderLineChart({
 | 
			
		||||
			id: "cpu-chart",
 | 
			
		||||
			name: "CPU",
 | 
			
		||||
			values: this.cpuValues,
 | 
			
		||||
			x: function (v) {
 | 
			
		||||
				return v.time
 | 
			
		||||
			},
 | 
			
		||||
			tooltip: function (args, stats) {
 | 
			
		||||
				return stats[args.dataIndex].time + ":" + (Math.ceil(stats[args.dataIndex].value * 100 * 100) / 100) + "%"
 | 
			
		||||
			},
 | 
			
		||||
			value: function (v) {
 | 
			
		||||
				return v.value * 100;
 | 
			
		||||
			},
 | 
			
		||||
			axis: axis,
 | 
			
		||||
			max: 100
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadMemoryChart = function () {
 | 
			
		||||
		let axis = {unit: "%", divider: 1}
 | 
			
		||||
		teaweb.renderLineChart({
 | 
			
		||||
			id: "memory-chart",
 | 
			
		||||
			name: "内存",
 | 
			
		||||
			values: this.memoryValues,
 | 
			
		||||
			x: function (v) {
 | 
			
		||||
				return v.time
 | 
			
		||||
			},
 | 
			
		||||
			tooltip: function (args, stats) {
 | 
			
		||||
				return stats[args.dataIndex].time + ":" + (Math.ceil(stats[args.dataIndex].value * 100 * 100) / 100) + "%"
 | 
			
		||||
			},
 | 
			
		||||
			value: function (v) {
 | 
			
		||||
				return v.value * 100;
 | 
			
		||||
			},
 | 
			
		||||
			axis: axis,
 | 
			
		||||
			max: 100
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.reloadLoadChart = function () {
 | 
			
		||||
		let axis = {unit: "", divider: 1}
 | 
			
		||||
		let max = this.loadValues.$map(function (k, v) {
 | 
			
		||||
			return v.value
 | 
			
		||||
		}).$max()
 | 
			
		||||
		if (max < 10) {
 | 
			
		||||
			max = 10
 | 
			
		||||
		} else if (max < 20) {
 | 
			
		||||
			max = 20
 | 
			
		||||
		} else if (max < 100) {
 | 
			
		||||
			max = 100
 | 
			
		||||
		} else {
 | 
			
		||||
			max = null
 | 
			
		||||
		}
 | 
			
		||||
		teaweb.renderLineChart({
 | 
			
		||||
			id: "load-chart",
 | 
			
		||||
			name: "负载",
 | 
			
		||||
			values: this.loadValues,
 | 
			
		||||
			x: function (v) {
 | 
			
		||||
				return v.time
 | 
			
		||||
			},
 | 
			
		||||
			tooltip: function (args, stats) {
 | 
			
		||||
				return stats[args.dataIndex].time + ":" + (Math.ceil(stats[args.dataIndex].value * 100) / 100)
 | 
			
		||||
			},
 | 
			
		||||
			value: function (v) {
 | 
			
		||||
				return v.value;
 | 
			
		||||
			},
 | 
			
		||||
			axis: axis,
 | 
			
		||||
			max: max
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										45
									
								
								web/views/@default/clusters/cluster/node/boards/index.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								web/views/@default/clusters/cluster/node/boards/index.less
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
.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: 20em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h4 {
 | 
			
		||||
	span {
 | 
			
		||||
		font-size: 0.8em;
 | 
			
		||||
		color: grey;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
a.underline {
 | 
			
		||||
  border-bottom: 1px #db2828 dashed;
 | 
			
		||||
}
 | 
			
		||||
/*# sourceMappingURL=index.css.map */
 | 
			
		||||
/*# sourceMappingURL=detail.css.map */
 | 
			
		||||
							
								
								
									
										1
									
								
								web/views/@default/clusters/cluster/node/detail.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								web/views/@default/clusters/cluster/node/detail.css.map
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
{"version":3,"sources":["detail.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"detail.css"}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"index.css"}
 | 
			
		||||
		Reference in New Issue
	
	Block a user