465 lines
14 KiB
Go
465 lines
14 KiB
Go
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
//go:build plus
|
|
|
|
package boards
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
|
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
|
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
|
|
"github.com/TeaOSLab/EdgeAdmin/internal/tasks"
|
|
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dashboard/boards/boardutils"
|
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dashboard/dashboardutils"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
|
"github.com/iwind/TeaGo/maps"
|
|
"github.com/iwind/TeaGo/types"
|
|
stringutil "github.com/iwind/TeaGo/utils/string"
|
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
|
"regexp"
|
|
"time"
|
|
)
|
|
|
|
type IndexAction struct {
|
|
actionutils.ParentAction
|
|
}
|
|
|
|
func (this *IndexAction) Init() {
|
|
this.Nav("", "", "index")
|
|
}
|
|
|
|
func (this *IndexAction) RunGet(params struct{}) {
|
|
if !teaconst.IsPlus {
|
|
this.RedirectURL("/dashboard")
|
|
return
|
|
}
|
|
|
|
uiConfig, err := configloaders.LoadAdminUIConfig()
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
if !uiConfig.ContainsModule(userconfigs.UserModuleCDN) {
|
|
if uiConfig.ContainsModule(userconfigs.UserModuleNS) {
|
|
this.RedirectURL("/ns")
|
|
return
|
|
}
|
|
|
|
this.View("@blank")
|
|
this.Show()
|
|
return
|
|
}
|
|
|
|
// 商业版错误
|
|
this.Data["plusErr"] = plus.ErrString
|
|
|
|
// 初始化
|
|
err = boardutils.InitBoard(this.Parent())
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
// 取得用户的权限
|
|
module, ok := configloaders.FindFirstAdminModule(this.AdminId())
|
|
if ok {
|
|
if module != "dashboard" {
|
|
for _, m := range configloaders.AllModuleMaps(this.LangCode()) {
|
|
if m.GetString("code") == module {
|
|
this.RedirectURL(m.GetString("url"))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 版本更新
|
|
this.Data["currentVersionCode"] = teaconst.Version
|
|
this.Data["newVersionCode"] = teaconst.NewVersionCode
|
|
this.Data["newVersionDownloadURL"] = teaconst.NewVersionDownloadURL
|
|
|
|
this.Show()
|
|
}
|
|
|
|
func (this *IndexAction) RunPost(params struct {
|
|
}) {
|
|
// 读取看板数据
|
|
resp, err := this.RPC().AdminRPC().ComposeAdminDashboard(this.AdminContext(), &pb.ComposeAdminDashboardRequest{
|
|
ApiVersion: teaconst.APINodeVersion,
|
|
})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
|
|
// 检查当前服务器空间
|
|
var diskUsageWarning = ""
|
|
diskPath, diskUsage, diskUsagePercent, shouldWarning := dashboardutils.CheckDiskPartitions(90)
|
|
if shouldWarning {
|
|
diskUsageWarning = codes.AdminDashboard_DiskUsageWarning.For(this.LangCode(), diskPath, diskUsage/(1<<30), diskUsagePercent, 100-diskUsagePercent)
|
|
}
|
|
|
|
this.Data["dashboard"] = maps.Map{
|
|
"defaultClusterId": resp.DefaultNodeClusterId,
|
|
|
|
"countServers": resp.CountServers,
|
|
"countAuditingServers": resp.CountAuditingServers,
|
|
"countNodeClusters": resp.CountNodeClusters,
|
|
"countNodes": resp.CountNodes,
|
|
"countOfflineNodes": resp.CountOfflineNodes,
|
|
"countUsers": resp.CountUsers,
|
|
"countAPINodes": resp.CountAPINodes,
|
|
"countOfflineAPINodes": resp.CountOfflineAPINodes,
|
|
"countDBNodes": resp.CountDBNodes,
|
|
"countOfflineDBNodes": resp.CountOfflineDBNodes,
|
|
"countUserNodes": resp.CountUserNodes,
|
|
"countOfflineUserNodes": resp.CountOfflineUserNodes,
|
|
|
|
"canGoServers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeServer),
|
|
"canGoNodes": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeNode),
|
|
"canGoSettings": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeSetting),
|
|
"canGoUsers": configloaders.AllowModule(this.AdminId(), configloaders.AdminModuleCodeUser),
|
|
|
|
"diskUsageWarning": diskUsageWarning,
|
|
}
|
|
|
|
// 今日流量和独立IP数
|
|
var todayTrafficBytes int64
|
|
var todayCountIPs int64
|
|
if len(resp.DailyTrafficStats) > 0 {
|
|
var lastDailyTrafficStat = resp.DailyTrafficStats[len(resp.DailyTrafficStats)-1]
|
|
todayTrafficBytes = lastDailyTrafficStat.Bytes
|
|
todayCountIPs = lastDailyTrafficStat.CountIPs
|
|
}
|
|
todayTrafficString := numberutils.FormatBytes(todayTrafficBytes)
|
|
result := regexp.MustCompile(`^(?U)(.+)([a-zA-Z]+)$`).FindStringSubmatch(todayTrafficString)
|
|
if len(result) > 2 {
|
|
this.Data["todayTraffic"] = result[1]
|
|
this.Data["todayTrafficUnit"] = result[2]
|
|
} else {
|
|
this.Data["todayTraffic"] = todayTrafficString
|
|
this.Data["todayTrafficUnit"] = ""
|
|
}
|
|
|
|
this.Data["todayCountIPs"] = todayCountIPs
|
|
|
|
var yesterdayTrafficBytes = int64(0)
|
|
if len(resp.DailyTrafficStats) > 1 {
|
|
yesterdayTrafficBytes = resp.DailyTrafficStats[len(resp.DailyTrafficStats)-2].Bytes
|
|
}
|
|
var yesterdayTrafficString = numberutils.FormatBytes(yesterdayTrafficBytes)
|
|
{
|
|
var result = regexp.MustCompile(`^(?U)(.+)([a-zA-Z]+)$`).FindStringSubmatch(yesterdayTrafficString)
|
|
if len(result) > 2 {
|
|
this.Data["yesterdayTraffic"] = result[1]
|
|
this.Data["yesterdayTrafficUnit"] = result[2]
|
|
} else {
|
|
this.Data["yesterdayTraffic"] = yesterdayTrafficString
|
|
this.Data["yesterdayTrafficUnit"] = ""
|
|
}
|
|
}
|
|
|
|
var weekTrafficBytes = int64(0)
|
|
var weekday = types.Int(timeutil.Format("w"))
|
|
if weekday == 0 {
|
|
weekday = 7
|
|
}
|
|
var weekDayBegin = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -weekday+1))
|
|
if len(resp.DailyTrafficStats) > 1 {
|
|
for i := len(resp.DailyTrafficStats) - 1; i >= len(resp.DailyTrafficStats)-7 && i >= 0; i-- {
|
|
var stat = resp.DailyTrafficStats[i]
|
|
if stat.Day >= weekDayBegin {
|
|
weekTrafficBytes += stat.Bytes
|
|
}
|
|
}
|
|
}
|
|
var weekTrafficString = numberutils.FormatBytes(weekTrafficBytes)
|
|
{
|
|
var result = regexp.MustCompile(`^(?U)(.+)([a-zA-Z]+)$`).FindStringSubmatch(weekTrafficString)
|
|
if len(result) > 2 {
|
|
this.Data["weekTraffic"] = result[1]
|
|
this.Data["weekTrafficUnit"] = result[2]
|
|
} else {
|
|
this.Data["weekTraffic"] = weekTrafficString
|
|
this.Data["weekTrafficUnit"] = ""
|
|
}
|
|
}
|
|
|
|
// 24小时流量趋势
|
|
{
|
|
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,
|
|
"countAttackRequests": stat.CountAttackRequests,
|
|
"attackBytes": stat.AttackBytes,
|
|
"day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日",
|
|
"hour": stat.Hour[8:],
|
|
})
|
|
}
|
|
this.Data["hourlyTrafficStats"] = statMaps
|
|
}
|
|
|
|
// 15天流量趋势
|
|
{
|
|
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,
|
|
"countAttackRequests": stat.CountAttackRequests,
|
|
"attackBytes": stat.AttackBytes,
|
|
"day": stat.Day[4:6] + "月" + stat.Day[6:] + "日",
|
|
})
|
|
}
|
|
this.Data["dailyTrafficStats"] = 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 countryMaps = []maps.Map{}
|
|
for _, stat := range resp.TopCountryStats {
|
|
countryMaps = append(countryMaps, maps.Map{
|
|
"name": stat.CountryName,
|
|
"bytes": stat.Bytes,
|
|
"formattedBytes": numberutils.FormatBytes(stat.Bytes),
|
|
"countRequests": stat.CountRequests,
|
|
"countAttackRequests": stat.CountAttackRequests,
|
|
"percent": fmt.Sprintf("%.2f", stat.Percent),
|
|
})
|
|
}
|
|
this.Data["topCountryStats"] = countryMaps
|
|
}
|
|
|
|
// 版本升级
|
|
if resp.NodeUpgradeInfo != nil {
|
|
this.Data["nodeUpgradeInfo"] = maps.Map{
|
|
"count": resp.NodeUpgradeInfo.CountNodes,
|
|
"version": resp.NodeUpgradeInfo.NewVersion,
|
|
}
|
|
} else {
|
|
this.Data["nodeUpgradeInfo"] = maps.Map{
|
|
"count": 0,
|
|
"version": "",
|
|
}
|
|
}
|
|
if resp.ApiNodeUpgradeInfo != nil {
|
|
this.Data["apiNodeUpgradeInfo"] = maps.Map{
|
|
"count": resp.ApiNodeUpgradeInfo.CountNodes,
|
|
"version": resp.ApiNodeUpgradeInfo.NewVersion,
|
|
}
|
|
} else {
|
|
this.Data["apiNodeUpgradeInfo"] = maps.Map{
|
|
"count": 0,
|
|
"version": "",
|
|
}
|
|
}
|
|
if resp.UserNodeUpgradeInfo != nil {
|
|
this.Data["userNodeUpgradeInfo"] = maps.Map{
|
|
"count": resp.UserNodeUpgradeInfo.CountNodes,
|
|
"version": resp.UserNodeUpgradeInfo.NewVersion,
|
|
}
|
|
} else {
|
|
this.Data["userNodeUpgradeInfo"] = maps.Map{
|
|
"count": 0,
|
|
"version": 0,
|
|
}
|
|
}
|
|
if resp.NsNodeUpgradeInfo != nil {
|
|
this.Data["nsNodeUpgradeInfo"] = maps.Map{
|
|
"count": resp.NsNodeUpgradeInfo.CountNodes,
|
|
"version": resp.NsNodeUpgradeInfo.NewVersion,
|
|
}
|
|
} else {
|
|
this.Data["nsNodeUpgradeInfo"] = maps.Map{
|
|
"count": 0,
|
|
"version": "",
|
|
}
|
|
}
|
|
if resp.ReportNodeUpgradeInfo != nil {
|
|
this.Data["reportNodeUpgradeInfo"] = maps.Map{
|
|
"count": resp.ReportNodeUpgradeInfo.CountNodes,
|
|
"version": resp.ReportNodeUpgradeInfo.NewVersion,
|
|
}
|
|
} else {
|
|
this.Data["reportNodeUpgradeInfo"] = maps.Map{
|
|
"count": 0,
|
|
"version": "",
|
|
}
|
|
}
|
|
|
|
// 指标
|
|
{
|
|
var chartMaps = []maps.Map{}
|
|
for _, chart := range resp.MetricDataCharts {
|
|
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),
|
|
"valueTypeName": serverconfigs.FindMetricValueName(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
|
|
"keys": chart.MetricChart.MetricItem.Keys,
|
|
},
|
|
"stats": statMaps,
|
|
})
|
|
}
|
|
this.Data["metricCharts"] = chartMaps
|
|
}
|
|
|
|
// Plus过期时间
|
|
authorityKeyResp, err := this.RPC().AuthorityKeyRPC().ReadAuthorityKey(this.AdminContext(), &pb.ReadAuthorityKeyRequest{})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
var authorityKey = authorityKeyResp.AuthorityKey
|
|
var plusExpireDay = ""
|
|
if authorityKey != nil && timeutil.Format("Y-m-d", time.Now().AddDate(0, 0, 10)) > authorityKey.DayTo {
|
|
plusExpireDay = authorityKey.DayTo
|
|
}
|
|
this.Data["plusExpireDay"] = plusExpireDay
|
|
tasks.NotifyAuthorityTask() // 通知状态变更
|
|
|
|
// 今日攻击信息
|
|
countBlocksResp, err := this.RPC().FirewallRPC().CountFirewallDailyBlocks(this.AdminContext(), &pb.CountFirewallDailyBlocksRequest{})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
boardutils.CountTodayAttacks = countBlocksResp.CountBlocks
|
|
this.Data["countTodayAttacks"] = countBlocksResp.CountBlocks
|
|
this.Data["countTodayAttacksFormat"] = numberutils.FormatCount(countBlocksResp.CountBlocks)
|
|
|
|
// 当前API节点版本
|
|
{
|
|
exePath, runtimeVersion, fileVersion, ok := dashboardutils.CheckLocalAPINode(this.RPC(), this.AdminContext())
|
|
if ok {
|
|
this.Data["localLowerVersionAPINode"] = maps.Map{
|
|
"exePath": exePath,
|
|
"runtimeVersion": runtimeVersion,
|
|
"fileVersion": fileVersion,
|
|
"isRestarting": false,
|
|
}
|
|
}
|
|
}
|
|
|
|
// 弱密码提示
|
|
countWeakAdminsResp, err := this.RPC().AdminRPC().CountAllEnabledAdmins(this.AdminContext(), &pb.CountAllEnabledAdminsRequest{HasWeakPassword: true})
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
this.Data["countWeakAdmins"] = countWeakAdminsResp.Count
|
|
|
|
upgradeConfig, err := configloaders.LoadUpgradeConfig()
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
this.Data["autoUpgrade"] = upgradeConfig.AutoUpgrade
|
|
|
|
httpdnsNodeUpgradeInfo, err := this.composeHTTPDNSNodeUpgradeInfo()
|
|
if err != nil {
|
|
this.ErrorPage(err)
|
|
return
|
|
}
|
|
this.Data["httpdnsNodeUpgradeInfo"] = httpdnsNodeUpgradeInfo
|
|
|
|
this.Success()
|
|
}
|
|
|
|
func (this *IndexAction) composeHTTPDNSNodeUpgradeInfo() (maps.Map, error) {
|
|
clustersResp, err := this.RPC().HTTPDNSClusterRPC().ListHTTPDNSClusters(this.AdminContext(), &pb.ListHTTPDNSClustersRequest{
|
|
Offset: 0,
|
|
Size: 10000,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
count := 0
|
|
version := ""
|
|
for _, cluster := range clustersResp.Clusters {
|
|
resp, err := this.RPC().HTTPDNSNodeRPC().FindAllUpgradeHTTPDNSNodesWithClusterId(this.AdminContext(), &pb.FindAllUpgradeHTTPDNSNodesWithClusterIdRequest{
|
|
ClusterId: cluster.Id,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
count += len(resp.Nodes)
|
|
for _, nodeUpgrade := range resp.Nodes {
|
|
if len(nodeUpgrade.NewVersion) == 0 {
|
|
continue
|
|
}
|
|
if len(version) == 0 || stringutil.VersionCompare(version, nodeUpgrade.NewVersion) < 0 {
|
|
version = nodeUpgrade.NewVersion
|
|
}
|
|
}
|
|
}
|
|
|
|
return maps.Map{
|
|
"count": count,
|
|
"version": version,
|
|
}, nil
|
|
}
|