From e8b8e59bd7523abd7617202a6bfc0ad10c02f78f Mon Sep 17 00:00:00 2001 From: Ko no dio <38449861+lvgj-stack@users.noreply.github.com> Date: Mon, 12 Feb 2024 14:16:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20add=20network=20monitor=20hitory=20(#31?= =?UTF-8?q?6)=20=C2=B7=20=E4=B8=89=E7=BD=91ping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add network monitor hitory * fix: revert proto change and add indexStore * fix: update monitor delete unuse monitor history * fix: delete unuse monitor type --------- Co-authored-by: LvGJ --- cmd/dashboard/controller/api_v1.go | 29 +- cmd/dashboard/controller/common_page.go | 110 +++++++ cmd/dashboard/controller/member_api.go | 18 +- cmd/dashboard/rpc/rpc.go | 16 +- model/config.go | 8 + model/monitor.go | 5 +- model/monitor_history.go | 9 + resource/l10n/en-US.toml | 3 + resource/l10n/es-ES.toml | 3 + resource/l10n/zh-CN.toml | 5 +- resource/l10n/zh-TW.toml | 5 +- resource/template/common/menu.html | 3 +- resource/template/theme-daynight/home.html | 1 + resource/template/theme-daynight/network.html | 268 ++++++++++++++++++ resource/template/theme-daynight/service.html | 1 + resource/template/theme-default/network.html | 219 ++++++++++++++ .../theme-server-status/content-nav.html | 2 + .../template/theme-server-status/header.html | 3 +- .../template/theme-server-status/network.html | 217 ++++++++++++++ service/singleton/api.go | 68 ++++- service/singleton/servicesentinel.go | 69 ++++- service/singleton/singleton.go | 4 + 22 files changed, 1043 insertions(+), 23 deletions(-) create mode 100644 resource/template/theme-daynight/network.html create mode 100644 resource/template/theme-default/network.html create mode 100644 resource/template/theme-server-status/network.html diff --git a/cmd/dashboard/controller/api_v1.go b/cmd/dashboard/controller/api_v1.go index 999fd92..da24b68 100644 --- a/cmd/dashboard/controller/api_v1.go +++ b/cmd/dashboard/controller/api_v1.go @@ -1,11 +1,13 @@ package controller import ( - "github.com/gin-gonic/gin" - "github.com/naiba/nezha/pkg/mygin" - "github.com/naiba/nezha/service/singleton" "strconv" "strings" + + "github.com/gin-gonic/gin" + + "github.com/naiba/nezha/pkg/mygin" + "github.com/naiba/nezha/service/singleton" ) type apiV1 struct { @@ -25,7 +27,8 @@ func (v *apiV1) serve() { })) r.GET("/server/list", v.serverList) r.GET("/server/details", v.serverDetails) - + mr := v.r.Group("monitor") + mr.GET("/:id", v.monitorHistoriesById) } // serverList 获取服务器列表 不传入Query参数则获取全部 @@ -65,3 +68,21 @@ func (v *apiV1) serverDetails(c *gin.Context) { } c.JSON(200, singleton.ServerAPI.GetAllStatus()) } + +func (v *apiV1) monitorHistoriesById(c *gin.Context) { + idStr := c.Param("id") + id, err := strconv.ParseUint(idStr, 10, 64) + if err != nil { + c.AbortWithStatusJSON(400, gin.H{"code": 400, "message": "id参数错误"}) + return + } + server, ok := singleton.ServerList[id] + if !ok { + c.AbortWithStatusJSON(404, gin.H{ + "code": 404, + "message": "id不存在", + }) + return + } + c.JSON(200, singleton.MonitorAPI.GetMonitorHistories(map[string]any{"server_id": server.ID})) +} diff --git a/cmd/dashboard/controller/common_page.go b/cmd/dashboard/controller/common_page.go index 9f2d6c9..4478d84 100644 --- a/cmd/dashboard/controller/common_page.go +++ b/cmd/dashboard/controller/common_page.go @@ -5,6 +5,7 @@ import ( "log" "net/http" "regexp" + "strconv" "strings" "sync" "time" @@ -48,6 +49,9 @@ func (cp *commonPage) serve() { cr.Use(cp.checkViewPassword) // 前端查看密码鉴权 cr.GET("/", cp.home) cr.GET("/service", cp.service) + // TODO: 界面直接跳转使用该接口 + cr.GET("/network/:id", cp.network) + cr.GET("/network", cp.network) cr.GET("/ws", cp.ws) cr.POST("/terminal", cp.createTerminal) } @@ -127,6 +131,112 @@ func (p *commonPage) service(c *gin.Context) { })) } +func (cp *commonPage) network(c *gin.Context) { + var ( + monitorHistory *model.MonitorHistory + servers []*model.Server + serverIdsWithMonitor []uint64 + monitorInfos = []byte("{}") + id uint64 + ) + if len(singleton.SortedServerList) > 0 { + id = singleton.SortedServerList[0].ID + } + if err := singleton.DB.Model(&model.MonitorHistory{}).Select("monitor_id, server_id"). + Where("monitor_id != 0 and server_id != 0").Limit(1).First(&monitorHistory).Error; err != nil { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusForbidden, + Title: "请求失败", + Msg: "请求参数有误:" + "server monitor history not found", + Link: "/", + Btn: "返回重试", + }, true) + return + } else { + if monitorHistory == nil || monitorHistory.ServerID == 0 { + if len(singleton.SortedServerList) > 0 { + id = singleton.SortedServerList[0].ID + } + } else { + id = monitorHistory.ServerID + } + } + + idStr := c.Param("id") + if idStr != "" { + var err error + id, err = strconv.ParseUint(idStr, 10, 64) + if err != nil { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusForbidden, + Title: "请求失败", + Msg: "请求参数有误:" + err.Error(), + Link: "/", + Btn: "返回重试", + }, true) + return + } + _, ok := singleton.ServerList[id] + if !ok { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusForbidden, + Title: "请求失败", + Msg: "请求参数有误:" + "server id not found", + Link: "/", + Btn: "返回重试", + }, true) + return + } + } + monitorHistories := singleton.MonitorAPI.GetMonitorHistories(map[string]any{"server_id": id}) + monitorInfos, _ = utils.Json.Marshal(monitorHistories) + _, isMember := c.Get(model.CtxKeyAuthorizedUser) + _, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified) + + if err := singleton.DB.Model(&model.MonitorHistory{}). + Select("distinct(server_id)"). + Where("server_id != 0"). + Find(&serverIdsWithMonitor). + Error; err != nil { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusForbidden, + Title: "请求失败", + Msg: "请求参数有误:" + "no server with monitor histories", + Link: "/", + Btn: "返回重试", + }, true) + return + } + if isMember || isViewPasswordVerfied { + for _, server := range singleton.SortedServerList { + for _, id := range serverIdsWithMonitor { + if server.ID == id { + servers = append(servers, server) + } + } + } + } else { + for _, server := range singleton.SortedServerListForGuest { + for _, id := range serverIdsWithMonitor { + if server.ID == id { + servers = append(servers, server) + } + } + } + } + serversBytes, _ := utils.Json.Marshal(Data{ + Now: time.Now().Unix() * 1000, + Servers: servers, + }) + + c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/network", mygin.CommonEnvironment(c, gin.H{ + "Servers": string(serversBytes), + "MonitorInfos": string(monitorInfos), + "CustomCode": singleton.Conf.Site.CustomCode, + "MaxTCPPingValue": singleton.Conf.MaxTCPPingValue, + })) +} + func (cp *commonPage) getServerStat(c *gin.Context) ([]byte, error) { v, err, _ := cp.requestGroup.Do("serverStats", func() (any, error) { singleton.SortedServerLock.RLock() diff --git a/cmd/dashboard/controller/member_api.go b/cmd/dashboard/controller/member_api.go index 671bc00..3eef98d 100644 --- a/cmd/dashboard/controller/member_api.go +++ b/cmd/dashboard/controller/member_api.go @@ -11,6 +11,7 @@ import ( "github.com/gin-gonic/gin" "github.com/jinzhu/copier" + "gorm.io/gorm" "github.com/naiba/nezha/model" "github.com/naiba/nezha/pkg/mygin" @@ -185,7 +186,17 @@ func (ma *memberAPI) delete(c *gin.Context) { var err error switch c.Param("model") { case "server": - err = singleton.DB.Unscoped().Delete(&model.Server{}, "id = ?", id).Error + err := singleton.DB.Transaction(func(tx *gorm.DB) error { + err = singleton.DB.Unscoped().Delete(&model.Server{}, "id = ?", id).Error + if err != nil { + return err + } + err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "server_id = ?", id).Error + if err != nil { + return err + } + return nil + }) if err == nil { // 删除服务器 singleton.ServerLock.Lock() @@ -427,6 +438,11 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) { err = singleton.DB.Save(&m).Error } } + if m.Cover == 0 { + err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error + } else { + err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error + } } if err == nil { err = singleton.ServiceSentinelShared.OnMonitorUpdate(m) diff --git a/cmd/dashboard/rpc/rpc.go b/cmd/dashboard/rpc/rpc.go index 2820c95..540c4ad 100644 --- a/cmd/dashboard/rpc/rpc.go +++ b/cmd/dashboard/rpc/rpc.go @@ -49,10 +49,20 @@ func DispatchTask(serviceSentinelDispatchBus <-chan model.Monitor) { workedServerIndex++ continue } + if task.Cover == model.MonitorCoverIgnoreAll && task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] { + singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB()) + workedServerIndex++ + continue + } + if task.Cover == model.MonitorCoverAll && !task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] { + singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB()) + workedServerIndex++ + continue + } // 找到合适机器执行任务,跳出循环 - singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB()) - workedServerIndex++ - break + // singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB()) + // workedServerIndex++ + // break } singleton.SortedServerLock.RUnlock() } diff --git a/model/config.go b/model/config.go index d10792c..fb4ad91 100644 --- a/model/config.go +++ b/model/config.go @@ -110,6 +110,8 @@ type Config struct { v *viper.Viper IgnoredIPNotificationServerIDs map[uint64]bool // [ServerID] -> bool(值为true代表当前ServerID在特定服务器列表内) + MaxTCPPingValue int32 + AvgPingCount int } // Read 读取配置文件并应用 @@ -144,6 +146,12 @@ func (c *Config) Read(path string) error { if c.Location == "" { c.Location = "Asia/Shanghai" } + if c.MaxTCPPingValue == 0 { + c.MaxTCPPingValue = 300 + } + if c.AvgPingCount == 0 { + c.AvgPingCount = 2 + } c.updateIgnoredIPNotificationID() return nil diff --git a/model/monitor.go b/model/monitor.go index 7f24267..b32fd3d 100644 --- a/model/monitor.go +++ b/model/monitor.go @@ -4,10 +4,11 @@ import ( "fmt" "log" - "github.com/naiba/nezha/pkg/utils" - pb "github.com/naiba/nezha/proto" "github.com/robfig/cron/v3" "gorm.io/gorm" + + "github.com/naiba/nezha/pkg/utils" + pb "github.com/naiba/nezha/proto" ) const ( diff --git a/model/monitor_history.go b/model/monitor_history.go index 042629d..56bd498 100644 --- a/model/monitor_history.go +++ b/model/monitor_history.go @@ -1,9 +1,18 @@ package model +const ( + Cycle = iota + Hour + Day + Week + Month +) + // MonitorHistory 历史监控记录 type MonitorHistory struct { Common MonitorID uint64 + ServerID uint64 AvgDelay float32 // 平均延迟,毫秒 Up uint64 // 检查状态良好计数 Down uint64 // 检查状态异常计数 diff --git a/resource/l10n/en-US.toml b/resource/l10n/en-US.toml index f646166..d7161fe 100644 --- a/resource/l10n/en-US.toml +++ b/resource/l10n/en-US.toml @@ -609,3 +609,6 @@ other = "Hide for Guest" [Menu] other = "Menu" + +[NetworkSpiter] +other = "Network Monitor" \ No newline at end of file diff --git a/resource/l10n/es-ES.toml b/resource/l10n/es-ES.toml index 3838037..5c5f318 100644 --- a/resource/l10n/es-ES.toml +++ b/resource/l10n/es-ES.toml @@ -609,3 +609,6 @@ other = "Ocultar para Invitados" [Menu] other = "Menú" + +[NetworkSpiter] +other = "Supervisión De Redes" diff --git a/resource/l10n/zh-CN.toml b/resource/l10n/zh-CN.toml index 672e30a..ab7bfbb 100644 --- a/resource/l10n/zh-CN.toml +++ b/resource/l10n/zh-CN.toml @@ -608,4 +608,7 @@ other = "信息" other = "对游客隐藏" [Menu] -other = "菜单" \ No newline at end of file +other = "菜单" + +[NetworkSpiter] +other = "网络监控" \ No newline at end of file diff --git a/resource/l10n/zh-TW.toml b/resource/l10n/zh-TW.toml index 36e15dc..4bf8974 100644 --- a/resource/l10n/zh-TW.toml +++ b/resource/l10n/zh-TW.toml @@ -608,4 +608,7 @@ other = "信息" other = "對遊客隱藏" [Menu] -other = "菜單" \ No newline at end of file +other = "菜單" + +[NetworkSpiter] +other = "網絡監控" \ No newline at end of file diff --git a/resource/template/common/menu.html b/resource/template/common/menu.html index ba746c4..098aef9 100644 --- a/resource/template/common/menu.html +++ b/resource/template/common/menu.html @@ -15,6 +15,7 @@ {{else}} {{tr "Home"}} {{tr "Services"}} + {{tr "NetworkSpiter"}} {{end}} {{template "component/confirm" .}} -{{end}} \ No newline at end of file +{{end}} diff --git a/resource/template/theme-daynight/home.html b/resource/template/theme-daynight/home.html index 63cfb03..92f58fd 100644 --- a/resource/template/theme-daynight/home.html +++ b/resource/template/theme-daynight/home.html @@ -39,6 +39,7 @@