diff --git a/README.md b/README.md index 5f6b6532..444d6722 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GoEdge目标 做一款人人用得起的CDN & WAF系统。 -![架构](doc/screenshot.png) +![截图](doc/screenshot.png) ## 特性 * `免费` - 开源、免费、自由、开放 diff --git a/internal/rpc/context_interface.go b/internal/rpc/context_interface.go deleted file mode 100644 index a311f1f5..00000000 --- a/internal/rpc/context_interface.go +++ /dev/null @@ -1,7 +0,0 @@ -package rpc - -import "context" - -type ContextInterface interface { - AdminContext() context.Context -} diff --git a/internal/web/actions/actionutils/action_interface.go b/internal/web/actions/actionutils/action_interface.go new file mode 100644 index 00000000..83bdb8a0 --- /dev/null +++ b/internal/web/actions/actionutils/action_interface.go @@ -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 +} diff --git a/internal/web/actions/actionutils/parent_action.go b/internal/web/actions/actionutils/parent_action.go index 3fbf2310..3ef8fed4 100644 --- a/internal/web/actions/actionutils/parent_action.go +++ b/internal/web/actions/actionutils/parent_action.go @@ -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 +} diff --git a/internal/web/actions/default/clusters/cluster/boards/index.go b/internal/web/actions/default/clusters/cluster/boards/index.go index 666d382a..7b572016 100644 --- a/internal/web/actions/default/clusters/cluster/boards/index.go +++ b/internal/web/actions/default/clusters/cluster/boards/index.go @@ -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() } diff --git a/internal/web/actions/default/clusters/cluster/init.go b/internal/web/actions/default/clusters/cluster/init.go index c5cd0bc0..ed04ec4b 100644 --- a/internal/web/actions/default/clusters/cluster/init.go +++ b/internal/web/actions/default/clusters/cluster/init.go @@ -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() }) diff --git a/internal/web/actions/default/clusters/cluster/node/boards/index.go b/internal/web/actions/default/clusters/cluster/node/boards/index.go new file mode 100644 index 00000000..0f61b31d --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/node/boards/index.go @@ -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() +} diff --git a/internal/web/actions/default/clusters/cluster/node/detail.go b/internal/web/actions/default/clusters/cluster/node/detail.go new file mode 100644 index 00000000..d5f7b398 --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/node/detail.go @@ -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() +} diff --git a/internal/web/actions/default/clusters/cluster/node/index.go b/internal/web/actions/default/clusters/cluster/node/index.go index 2e358c2f..a0e7fe06 100644 --- a/internal/web/actions/default/clusters/cluster/node/index.go +++ b/internal/web/actions/default/clusters/cluster/node/index.go @@ -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"] = "" + this.RedirectURL("/clusters/cluster/node/detail?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + strconv.FormatInt(params.NodeId, 10)) } - - // 分组 - 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() } diff --git a/internal/web/actions/default/clusters/cluster/node/logs.go b/internal/web/actions/default/clusters/cluster/node/logs.go index 09cf715f..aa6b28de 100644 --- a/internal/web/actions/default/clusters/cluster/node/logs.go +++ b/internal/web/actions/default/clusters/cluster/node/logs.go @@ -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 diff --git a/internal/web/actions/default/clusters/cluster/node/monitor/index.go b/internal/web/actions/default/clusters/cluster/node/monitor/index.go index cdef3fd6..061cd9c9 100644 --- a/internal/web/actions/default/clusters/cluster/node/monitor/index.go +++ b/internal/web/actions/default/clusters/cluster/node/monitor/index.go @@ -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() } diff --git a/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go new file mode 100644 index 00000000..d5439b17 --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go @@ -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 +} diff --git a/internal/web/actions/default/clusters/cluster/node/thresholds/index.go b/internal/web/actions/default/clusters/cluster/node/thresholds/index.go index e4254402..18e99c58 100644 --- a/internal/web/actions/default/clusters/cluster/node/thresholds/index.go +++ b/internal/web/actions/default/clusters/cluster/node/thresholds/index.go @@ -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", diff --git a/internal/web/actions/default/clusters/clusterutils/cluster_helper.go b/internal/web/actions/default/clusters/clusterutils/cluster_helper.go index 43c98831..be4928f5 100644 --- a/internal/web/actions/default/clusters/clusterutils/cluster_helper.go +++ b/internal/web/actions/default/clusters/clusterutils/cluster_helper.go @@ -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) diff --git a/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go b/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go index 209b03f1..87b28537 100644 --- a/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go +++ b/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go @@ -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 { diff --git a/web/public/js/components/server/metric-charts.js b/web/public/js/components/server/metric-charts.js index e06af056..6fc4b1f8 100644 --- a/web/public/js/components/server/metric-charts.js +++ b/web/public/js/components/server/metric-charts.js @@ -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() }, @@ -88,7 +88,7 @@ Vue.component("metric-chart", { if (stat.total > 0) { percent = Math.round((stat.value * 100 / stat.total) * 100) / 100 } - return stat.keys[0] + ": " + stat.value + ",占比:" + percent + "%" + return stat.keys[0] + ":" + stat.value + ",占比:" + percent + "%" } }, series: [ @@ -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: `
-
{{chart.name}}
+

{{chart.name}} (指标)

-
+
` }) diff --git a/web/public/js/utils.js b/web/public/js/utils.js index 0a655daf..c33fa1b5 100644 --- a/web/public/js/utils.js +++ b/web/public/js/utils.js @@ -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) { diff --git a/web/views/@default/@layout.css b/web/views/@default/@layout.css index 348f2488..5f057501 100644 --- a/web/views/@default/@layout.css +++ b/web/views/@default/@layout.css @@ -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; } diff --git a/web/views/@default/@layout.css.map b/web/views/@default/@layout.css.map index ea1f2c38..45b41098 100644 --- a/web/views/@default/@layout.css.map +++ b/web/views/@default/@layout.css.map @@ -1 +1 @@ -{"version":3,"sources":["@left_menu.less","@layout.less"],"names":[],"mappings":"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","file":"@layout.css"} \ No newline at end of file +{"version":3,"sources":["@left_menu.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 b26d99fb..85b4ebe4 100644 --- a/web/views/@default/@layout.less +++ b/web/views/@default/@layout.less @@ -747,6 +747,10 @@ td { } // 表格 +.scroll-box { + overflow-y: auto; +} + .scroll-box::-webkit-scrollbar { width: 4px; } diff --git a/web/views/@default/clusters/cluster/boards/index.html b/web/views/@default/clusters/cluster/boards/index.html index 551bbb98..999819c4 100644 --- a/web/views/@default/clusters/cluster/boards/index.html +++ b/web/views/@default/clusters/cluster/boards/index.html @@ -47,13 +47,13 @@
-

域名排行 (24小时)

+

域名访问排行 (24小时)

-

节点排行 (24小时)

+

节点访问排行 (24小时)

@@ -66,4 +66,10 @@
-
\ No newline at end of file +
+ + +
+ + + \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/boards/index.js b/web/views/@default/clusters/cluster/boards/index.js index dd6f8cb7..4f963500 100644 --- a/web/views/@default/clusters/cluster/boards/index.js +++ b/web/views/@default/clusters/cluster/boards/index.js @@ -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: "内存", diff --git a/web/views/@default/clusters/cluster/node/@node_menu.html b/web/views/@default/clusters/cluster/node/@node_menu.html index 8226377c..2131f68b 100644 --- a/web/views/@default/clusters/cluster/node/@node_menu.html +++ b/web/views/@default/clusters/cluster/node/@node_menu.html @@ -1,10 +1,12 @@ 节点列表 | - 节点详情 - 监控图表 - 阈值设置 - 运行日志 - 修改设置 - 安装节点 + "{{node.name}}"节点详情 + "{{node.name}}" 节点看板 + 节点详情 + 阈值设置 + 运行日志 + 修改设置 + 安装节点 \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/boards/index.css b/web/views/@default/clusters/cluster/node/boards/index.css new file mode 100644 index 00000000..34f8ab5b --- /dev/null +++ b/web/views/@default/clusters/cluster/node/boards/index.css @@ -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 */ \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/boards/index.css.map b/web/views/@default/clusters/cluster/node/boards/index.css.map new file mode 100644 index 00000000..1f562066 --- /dev/null +++ b/web/views/@default/clusters/cluster/node/boards/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"} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/boards/index.html b/web/views/@default/clusters/cluster/node/boards/index.html new file mode 100644 index 00000000..6661436f --- /dev/null +++ b/web/views/@default/clusters/cluster/node/boards/index.html @@ -0,0 +1,109 @@ +{$layout} +{$template "../node_menu"} +{$template "/echarts"} + + +
+
+

在线状态

+
+ 在线 + 离线 +
+
+
+

下行流量

+
{{board.trafficOutBytes}}/分钟
+
+
+

上行流量

+
{{board.trafficInBytes}}/分钟
+
+
+

连接数

+
{{board.countConnections}}/分钟
+
+
+

当前访问量

+
{{board.countRequests}}/分钟
+
+
+

当前攻击访问量

+
{{board.countAttackRequests}}/分钟
+
+
+

磁盘缓存用量

+
{{board.cacheDiskSize}}
+
+
+

内存缓存用量

+
{{board.cacheMemorySize}}
+
+
+

CPU

+
{{board.cpuUsage}}%
+
+
+

内存

+
{{board.memoryUsage}}%
+
+
+

总内存

+
{{board.memoryTotalSize}}G
+
+
+

负载

+
{{board.load}}/分钟
+
+
+ +
+ + + + +
+ + +
+ +
+ + + + +
+ + +
+ +
+ + +

域名访问排行 (24小时)

+
+ +
+ + + + +
+
+
+ + +
+ + + \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/boards/index.js b/web/views/@default/clusters/cluster/node/boards/index.js new file mode 100644 index 00000000..c37d2f95 --- /dev/null +++ b/web/views/@default/clusters/cluster/node/boards/index.js @@ -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 + "
请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "
流量:" + 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 + }) + } +}) \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/boards/index.less b/web/views/@default/clusters/cluster/node/boards/index.less new file mode 100644 index 00000000..5a20c7a5 --- /dev/null +++ b/web/views/@default/clusters/cluster/node/boards/index.less @@ -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; + } +} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/index.css b/web/views/@default/clusters/cluster/node/detail.css similarity index 58% rename from web/views/@default/clusters/cluster/node/index.css rename to web/views/@default/clusters/cluster/node/detail.css index 1b23bc85..d7800af0 100644 --- a/web/views/@default/clusters/cluster/node/index.css +++ b/web/views/@default/clusters/cluster/node/detail.css @@ -1,4 +1,4 @@ a.underline { border-bottom: 1px #db2828 dashed; } -/*# sourceMappingURL=index.css.map */ \ No newline at end of file +/*# sourceMappingURL=detail.css.map */ \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/detail.css.map b/web/views/@default/clusters/cluster/node/detail.css.map new file mode 100644 index 00000000..7440dc5a --- /dev/null +++ b/web/views/@default/clusters/cluster/node/detail.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["detail.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"detail.css"} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/index.html b/web/views/@default/clusters/cluster/node/detail.html similarity index 100% rename from web/views/@default/clusters/cluster/node/index.html rename to web/views/@default/clusters/cluster/node/detail.html diff --git a/web/views/@default/clusters/cluster/node/index.js b/web/views/@default/clusters/cluster/node/detail.js similarity index 100% rename from web/views/@default/clusters/cluster/node/index.js rename to web/views/@default/clusters/cluster/node/detail.js diff --git a/web/views/@default/clusters/cluster/node/index.less b/web/views/@default/clusters/cluster/node/detail.less similarity index 100% rename from web/views/@default/clusters/cluster/node/index.less rename to web/views/@default/clusters/cluster/node/detail.less diff --git a/web/views/@default/clusters/cluster/node/index.css.map b/web/views/@default/clusters/cluster/node/index.css.map deleted file mode 100644 index 31fa6632..00000000 --- a/web/views/@default/clusters/cluster/node/index.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"index.css"} \ No newline at end of file