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 @@