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