Skip to content

Commit

Permalink
feat: add network monitor hitory (#316) · 三网ping
Browse files Browse the repository at this point in the history
* 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 <lvgj1998@gmail.com>
  • Loading branch information
lvgj-stack and LvGJ authored Feb 12, 2024
1 parent c9bcba6 commit e8b8e59
Show file tree
Hide file tree
Showing 22 changed files with 1,042 additions and 22 deletions.
27 changes: 24 additions & 3 deletions cmd/dashboard/controller/api_v1.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package controller

import (
"strconv"
"strings"

"github.com/gin-gonic/gin"

"github.com/naiba/nezha/pkg/mygin"
"github.com/naiba/nezha/service/singleton"
"strconv"
"strings"
)

type apiV1 struct {
Expand All @@ -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参数则获取全部
Expand Down Expand Up @@ -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}))
}
110 changes: 110 additions & 0 deletions cmd/dashboard/controller/common_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"net/http"
"regexp"
"strconv"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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()
Expand Down
18 changes: 17 additions & 1 deletion cmd/dashboard/controller/member_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
16 changes: 13 additions & 3 deletions cmd/dashboard/rpc/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
8 changes: 8 additions & 0 deletions model/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ type Config struct {

v *viper.Viper
IgnoredIPNotificationServerIDs map[uint64]bool // [ServerID] -> bool(值为true代表当前ServerID在特定服务器列表内)
MaxTCPPingValue int32
AvgPingCount int
}

// Read 读取配置文件并应用
Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions model/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
9 changes: 9 additions & 0 deletions model/monitor_history.go
Original file line number Diff line number Diff line change
@@ -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 // 检查状态异常计数
Expand Down
3 changes: 3 additions & 0 deletions resource/l10n/en-US.toml
Original file line number Diff line number Diff line change
Expand Up @@ -609,3 +609,6 @@ other = "Hide for Guest"

[Menu]
other = "Menu"

[NetworkSpiter]
other = "Network Monitor"
3 changes: 3 additions & 0 deletions resource/l10n/es-ES.toml
Original file line number Diff line number Diff line change
Expand Up @@ -609,3 +609,6 @@ other = "Ocultar para Invitados"

[Menu]
other = "Menú"

[NetworkSpiter]
other = "Supervisión De Redes"
5 changes: 4 additions & 1 deletion resource/l10n/zh-CN.toml
Original file line number Diff line number Diff line change
Expand Up @@ -608,4 +608,7 @@ other = "信息"
other = "对游客隐藏"

[Menu]
other = "菜单"
other = "菜单"

[NetworkSpiter]
other = "网络监控"
5 changes: 4 additions & 1 deletion resource/l10n/zh-TW.toml
Original file line number Diff line number Diff line change
Expand Up @@ -608,4 +608,7 @@ other = "信息"
other = "對遊客隱藏"

[Menu]
other = "菜單"
other = "菜單"

[NetworkSpiter]
other = "網絡監控"
3 changes: 2 additions & 1 deletion resource/template/common/menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
{{else}}
<a class='item{{if eq .MatchedPath "/"}} active{{end}}' href="/"><i class="home icon"></i>{{tr "Home"}}</a>
<a class='item{{if eq .MatchedPath "/service"}} active{{end}}' href="/service"><i class="rss icon"></i>{{tr "Services"}}</a>
<a class='item{{if eq .MatchedPath "/network"}} active{{end}}' href="/network"><i class="server icon"></i>{{tr "NetworkSpiter"}}</a>
{{end}}
<div class="right menu">
<div class="item">
Expand Down Expand Up @@ -50,4 +51,4 @@
</div>
</div>
{{template "component/confirm" .}}
{{end}}
{{end}}
1 change: 1 addition & 0 deletions resource/template/theme-daynight/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<ul>
<li><a href="/">{{tr "Home"}}</a></li>
<li><a href="/service">{{tr "Services"}}</a></li>
<li><a href="/network">{{tr "NetworkSpiter"}}</a></li>
{{if .Admin}}
<li><a href="/server">{{tr "AdminPanel"}}</a></li>
{{else}}
Expand Down
Loading

0 comments on commit e8b8e59

Please sign in to comment.