diff --git a/internal/web/actions/default/servers/server/board/index.go b/internal/web/actions/default/servers/server/board/index.go deleted file mode 100644 index fa413a77..00000000 --- a/internal/web/actions/default/servers/server/board/index.go +++ /dev/null @@ -1,16 +0,0 @@ -package board - -import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" - -type IndexAction struct { - actionutils.ParentAction -} - -func (this *IndexAction) Init() { - this.Nav("", "board", "") - this.SecondMenu("index") -} - -func (this *IndexAction) RunGet(params struct{}) { - this.Show() -} diff --git a/internal/web/actions/default/servers/server/boards/index.go b/internal/web/actions/default/servers/server/boards/index.go new file mode 100644 index 00000000..b94d759d --- /dev/null +++ b/internal/web/actions/default/servers/server/boards/index.go @@ -0,0 +1,131 @@ +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" + "strconv" +) + +type IndexAction struct { + actionutils.ParentAction +} + +func (this *IndexAction) Init() { + this.Nav("", "board", "") + this.SecondMenu("index") +} + +func (this *IndexAction) RunGet(params struct { + ServerId int64 +}) { + if !teaconst.IsPlus { + this.RedirectURL("/servers/server/stat?serverId=" + strconv.FormatInt(params.ServerId, 10)) + return + } + + resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatBoard(this.AdminContext(), &pb.ComposeServerStatBoardRequest{ServerId: params.ServerId}) + if err != nil { + this.ErrorPage(err) + return + } + + // 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.TopNodeStats { + statMaps = append(statMaps, maps.Map{ + "nodeId": stat.NodeId, + "nodeName": stat.NodeName, + "countRequests": stat.CountRequests, + "bytes": stat.Bytes, + }) + } + this.Data["topNodeStats"] = 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 + } + + // 指标 + { + 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/servers/server/board/init.go b/internal/web/actions/default/servers/server/boards/init.go similarity index 89% rename from internal/web/actions/default/servers/server/board/init.go rename to internal/web/actions/default/servers/server/boards/init.go index 82910025..72132347 100644 --- a/internal/web/actions/default/servers/server/board/init.go +++ b/internal/web/actions/default/servers/server/boards/init.go @@ -1,4 +1,4 @@ -package board +package boards import ( "github.com/TeaOSLab/EdgeAdmin/internal/configloaders" @@ -12,7 +12,7 @@ func init() { server. Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)). Helper(serverutils.NewServerHelper()). - Prefix("/servers/server/board"). + Prefix("/servers/server/boards"). Get("", new(IndexAction)). EndAll() }) diff --git a/internal/web/actions/default/servers/server/index.go b/internal/web/actions/default/servers/server/index.go index dd3caa22..fecf4715 100644 --- a/internal/web/actions/default/servers/server/index.go +++ b/internal/web/actions/default/servers/server/index.go @@ -1,6 +1,7 @@ package server 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" @@ -34,5 +35,9 @@ func (this *IndexAction) RunGet(params struct { } // HTTP跳转到访问日志 - this.RedirectURL("/servers/server/log?serverId=" + strconv.FormatInt(params.ServerId, 10)) + if teaconst.IsPlus { + this.RedirectURL("/servers/server/boards?serverId=" + strconv.FormatInt(params.ServerId, 10)) + } else { + this.RedirectURL("/servers/server/stat?serverId=" + strconv.FormatInt(params.ServerId, 10)) + } } diff --git a/internal/web/actions/default/servers/serverutils/server_helper.go b/internal/web/actions/default/servers/serverutils/server_helper.go index 7c835f6a..dc6ffa63 100644 --- a/internal/web/actions/default/servers/serverutils/server_helper.go +++ b/internal/web/actions/default/servers/serverutils/server_helper.go @@ -3,6 +3,7 @@ package serverutils import ( "encoding/json" "errors" + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" "github.com/TeaOSLab/EdgeAdmin/internal/rpc" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" @@ -92,13 +93,15 @@ func (this *ServerHelper) createLeftMenu(action *actions.ActionObject) { selectedTabbar, _ := action.Data["mainTab"] tabbar := actionutils.NewTabbar() tabbar.Add("服务列表", "", "/servers", "", false) - //tabbar.Add("看板", "", "/servers/server/board?serverId="+serverIdString, "dashboard", selectedTabbar == "board") - if family == "http" { - tabbar.Add("日志", "", "/servers/server/log?serverId="+serverIdString, "history", selectedTabbar == "log") + if teaconst.IsPlus { + tabbar.Add("看板", "", "/servers/server/boards?serverId="+serverIdString, "dashboard", selectedTabbar == "board") } if family == "http" { tabbar.Add("统计", "", "/servers/server/stat?serverId="+serverIdString, "chart area", selectedTabbar == "stat") } + if family == "http" { + tabbar.Add("日志", "", "/servers/server/log?serverId="+serverIdString, "history", selectedTabbar == "log") + } tabbar.Add("设置", "", "/servers/server/settings?serverId="+serverIdString, "setting", selectedTabbar == "setting") tabbar.Add("删除", "", "/servers/server/delete?serverId="+serverIdString, "trash", selectedTabbar == "delete") { diff --git a/internal/web/import.go b/internal/web/import.go index 76670b6c..288a70f2 100644 --- a/internal/web/import.go +++ b/internal/web/import.go @@ -60,8 +60,11 @@ import ( _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/groups" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/log" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/components/waf" + _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/iplists" + _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/metrics" + _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/metrics/charts" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server" - _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/board" + _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/boards" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/delete" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/log" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings" @@ -109,11 +112,6 @@ import ( _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/websocket" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/stat" - _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/iplists" - - _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/metrics" - _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/metrics/charts" - // 设置相关 _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/authority" diff --git a/web/views/@default/clusters/cluster/nodes.html b/web/views/@default/clusters/cluster/nodes.html index 01cad8bd..89f852e5 100644 --- a/web/views/@default/clusters/cluster/nodes.html +++ b/web/views/@default/clusters/cluster/nodes.html @@ -68,10 +68,10 @@ {{node.name}}
- 区域:{{node.region.name}} + 区域:{{node.region.name}}
- 分组:{{node.group.name}} + 分组:{{node.group.name}}
diff --git a/web/views/@default/servers/server/board/index.css b/web/views/@default/servers/server/board/index.css deleted file mode 100644 index e69de29b..00000000 diff --git a/web/views/@default/servers/server/board/index.css.map b/web/views/@default/servers/server/board/index.css.map deleted file mode 100644 index 66dc9051..00000000 --- a/web/views/@default/servers/server/board/index.css.map +++ /dev/null @@ -1 +0,0 @@ -undefined \ No newline at end of file diff --git a/web/views/@default/servers/server/board/index.html b/web/views/@default/servers/server/board/index.html deleted file mode 100644 index cc594e36..00000000 --- a/web/views/@default/servers/server/board/index.html +++ /dev/null @@ -1,6 +0,0 @@ -{$layout} - -{$template "/left_menu"} -
-
此功能暂未开放,敬请期待。
-
\ No newline at end of file diff --git a/web/views/@default/servers/server/board/index.less b/web/views/@default/servers/server/board/index.less deleted file mode 100644 index e69de29b..00000000 diff --git a/web/views/@default/servers/server/boards/index.css b/web/views/@default/servers/server/boards/index.css new file mode 100644 index 00000000..34f8ab5b --- /dev/null +++ b/web/views/@default/servers/server/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/servers/server/boards/index.css.map b/web/views/@default/servers/server/boards/index.css.map new file mode 100644 index 00000000..1f562066 --- /dev/null +++ b/web/views/@default/servers/server/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/servers/server/boards/index.html b/web/views/@default/servers/server/boards/index.html new file mode 100644 index 00000000..defb5443 --- /dev/null +++ b/web/views/@default/servers/server/boards/index.html @@ -0,0 +1,39 @@ +{$layout} +{$template "/echarts"} + + + + +
+ + +
+ +
+ + + +
+ + +
+ +
+ + +

域名访问排行 (24小时)

+
+ +
+ + +
+ + + \ No newline at end of file diff --git a/web/views/@default/servers/server/boards/index.js b/web/views/@default/servers/server/boards/index.js new file mode 100644 index 00000000..dac4c9ca --- /dev/null +++ b/web/views/@default/servers/server/boards/index.js @@ -0,0 +1,296 @@ +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.trafficTab = "hourly" + + this.$delay(function () { + this.reloadHourlyTrafficChart() + this.reloadHourlyRequestsChart() + this.reloadTopDomainsChart() + }) + + 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 + }) + } +}) \ No newline at end of file diff --git a/web/views/@default/servers/server/boards/index.less b/web/views/@default/servers/server/boards/index.less new file mode 100644 index 00000000..5a20c7a5 --- /dev/null +++ b/web/views/@default/servers/server/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