diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go index 3962a09e..8a3bed0b 100644 --- a/internal/rpc/rpc_client.go +++ b/internal/rpc/rpc_client.go @@ -388,6 +388,10 @@ func (this *RPCClient) NSAccessLogRPC() pb.NSAccessLogServiceClient { return pb.NewNSAccessLogServiceClient(this.pickConn()) } +func (this *RPCClient) NSRPC() pb.NSServiceClient { + return pb.NewNSServiceClient(this.pickConn()) +} + func (this *RPCClient) MetricItemRPC() pb.MetricItemServiceClient { return pb.NewMetricItemServiceClient(this.pickConn()) } diff --git a/internal/web/actions/default/dashboard/boards/dns.go b/internal/web/actions/default/dashboard/boards/dns.go index e1b48a29..1ccccce0 100644 --- a/internal/web/actions/default/dashboard/boards/dns.go +++ b/internal/web/actions/default/dashboard/boards/dns.go @@ -2,7 +2,14 @@ package boards -import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" +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/iwind/TeaGo/maps" + "github.com/iwind/TeaGo/types" + timeutil "github.com/iwind/TeaGo/utils/time" +) type DnsAction struct { actionutils.ParentAction @@ -13,5 +20,114 @@ func (this *DnsAction) Init() { } func (this *DnsAction) RunGet(params struct{}) { + if !teaconst.IsPlus { + this.RedirectURL("/dashboard") + return + } + + resp, err := this.RPC().NSRPC().ComposeNSBoard(this.AdminContext(), &pb.ComposeNSBoardRequest{}) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["board"] = maps.Map{ + "countDomains": resp.CountNSDomains, + "countRecords": resp.CountNSRecords, + "countClusters": resp.CountNSClusters, + "countNodes": resp.CountNSNodes, + "countOfflineNodes": resp.CountOfflineNSNodes, + } + + // 流量排行 + { + var statMaps = []maps.Map{} + for _, stat := range resp.HourlyTrafficStats { + statMaps = append(statMaps, maps.Map{ + "day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日", + "hour": stat.Hour[8:], + "countRequests": stat.CountRequests, + "bytes": stat.Bytes, + }) + } + this.Data["hourlyStats"] = statMaps + } + + { + var statMaps = []maps.Map{} + for _, stat := range resp.DailyTrafficStats { + statMaps = append(statMaps, maps.Map{ + "day": stat.Day[4:6] + "月" + stat.Day[6:] + "日", + "countRequests": stat.CountRequests, + "bytes": stat.Bytes, + }) + } + this.Data["dailyStats"] = statMaps + } + + // 域名排行 + { + var statMaps = []maps.Map{} + for _, stat := range resp.TopNSDomainStats { + statMaps = append(statMaps, maps.Map{ + "domainId": stat.NsDomainId, + "domainName": stat.NsDomainName, + "countRequests": stat.CountRequests, + "bytes": stat.Bytes, + }) + } + this.Data["topDomainStats"] = statMaps + } + + // 节点排行 + { + var statMaps = []maps.Map{} + for _, stat := range resp.TopNSNodeStats { + statMaps = append(statMaps, maps.Map{ + "clusterId": stat.NsClusterId, + "nodeId": stat.NsNodeId, + "nodeName": stat.NsNodeName, + "countRequests": stat.CountRequests, + "bytes": stat.Bytes, + }) + } + this.Data["topNodeStats"] = 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 + } + this.Show() } diff --git a/internal/web/actions/default/dashboard/boards/user.go b/internal/web/actions/default/dashboard/boards/user.go index d990126f..b56a1908 100644 --- a/internal/web/actions/default/dashboard/boards/user.go +++ b/internal/web/actions/default/dashboard/boards/user.go @@ -3,6 +3,7 @@ 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/iwind/TeaGo/maps" @@ -19,6 +20,11 @@ func (this *UserAction) Init() { } func (this *UserAction) RunGet(params struct{}) { + if !teaconst.IsPlus { + this.RedirectURL("/dashboard") + return + } + resp, err := this.RPC().UserRPC().ComposeUserGlobalBoard(this.AdminContext(), &pb.ComposeUserGlobalBoardRequest{}) if err != nil { this.ErrorPage(err) diff --git a/internal/web/actions/default/dashboard/boards/waf.go b/internal/web/actions/default/dashboard/boards/waf.go index c9911241..fcf111f9 100644 --- a/internal/web/actions/default/dashboard/boards/waf.go +++ b/internal/web/actions/default/dashboard/boards/waf.go @@ -2,7 +2,10 @@ package boards -import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" +import ( + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" +) type WafAction struct { actionutils.ParentAction @@ -13,5 +16,10 @@ func (this *WafAction) Init() { } func (this *WafAction) RunGet(params struct{}) { + if !teaconst.IsPlus { + this.RedirectURL("/dashboard") + return + } + this.Show() } diff --git a/web/public/js/utils.js b/web/public/js/utils.js index 51fb2393..8e9dc3ff 100644 --- a/web/public/js/utils.js +++ b/web/public/js/utils.js @@ -395,7 +395,7 @@ window.teaweb = { left: 40, top: 10, right: 20, - bottom: 20 + bottom: 24 }, series: [ { diff --git a/web/views/@default/dashboard/boards/dns.html b/web/views/@default/dashboard/boards/dns.html index c8483a70..4e308e1b 100644 --- a/web/views/@default/dashboard/boards/dns.html +++ b/web/views/@default/dashboard/boards/dns.html @@ -1,29 +1,61 @@ {$layout} {$template "menu"} +{$template "/echarts"}
-

域名

-
+

域名

+
{{board.countDomains}}
-

记录

-
+

记录

+
{{board.countRecords}}
-

集群

-
+

集群

+
{{board.countClusters}}
-

节点

-
+

节点

+
{{board.countNodes}} + {{board.countOfflineNodes}}离线 + +
+ + + + +
+ + +
+
+

域名访问排行 (24小时)

+
+
- + +

节点访问排行 (24小时)

+
+
- \ No newline at end of file + +
+ + +
+
+
\ No newline at end of file diff --git a/web/views/@default/dashboard/boards/dns.js b/web/views/@default/dashboard/boards/dns.js new file mode 100644 index 00000000..02adeded --- /dev/null +++ b/web/views/@default/dashboard/boards/dns.js @@ -0,0 +1,265 @@ +Tea.context(function () { + this.$delay(function () { + this.reloadHourlyTrafficChart() + this.reloadTopDomainsChart() + this.reloadTopNodesChart() + this.reloadCPUChart() + }) + + /** + * 流量统计 + */ + this.trafficTab = "hourly" + + 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" + } + } + ], + animation: true + } + chart.setOption(option) + chart.resize() + } + + // 域名排行 + this.reloadTopDomainsChart = function () { + let that = this + 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.domainName + }, + tooltip: function (args, stats) { + return stats[args.dataIndex].domainName + "
请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "
流量:" + teaweb.formatBytes(stats[args.dataIndex].bytes) + }, + value: function (v) { + return v.countRequests / axis.divider; + }, + axis: axis, + click: function (args, stats) { + window.location = "/ns/domains/domain?domainId=" + stats[args.dataIndex].domainId + } + }) + } + + // 节点排行 + this.reloadTopNodesChart = function () { + let that = this + let axis = teaweb.countAxis(this.topNodeStats, function (v) { + return v.countRequests + }) + teaweb.renderBarChart({ + id: "top-nodes-chart", + name: "节点", + values: this.topNodeStats, + x: function (v) { + return v.nodeName + }, + tooltip: function (args, stats) { + return stats[args.dataIndex].nodeName + "
请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "
流量:" + teaweb.formatBytes(stats[args.dataIndex].bytes) + }, + value: function (v) { + return v.countRequests / axis.divider; + }, + axis: axis, + click: function (args, stats) { + window.location = "/ns/clusters/cluster/node?nodeId=" + stats[args.dataIndex].nodeId + "&clusterId=" + stats[args.dataIndex].clusterId + } + }) + } + + /** + * 系统信息 + */ + 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/dashboard/boards/waf.html b/web/views/@default/dashboard/boards/waf.html index e896c845..6ad5ba32 100644 --- a/web/views/@default/dashboard/boards/waf.html +++ b/web/views/@default/dashboard/boards/waf.html @@ -1,2 +1,3 @@ {$layout} -{$template "menu"} \ No newline at end of file +{$template "menu"} +{$template "/echarts"} \ No newline at end of file