💥 v2.0 必须更新面板,新增服务监控

This commit is contained in:
naiba 2021-01-16 00:45:49 +08:00
parent 0ce8017875
commit a41c792577
38 changed files with 1015 additions and 453 deletions

View File

@ -3,7 +3,7 @@ RUN apk --no-cache --no-progress add \
gcc git musl-dev gcc git musl-dev
WORKDIR /dashboard WORKDIR /dashboard
COPY . . COPY . .
RUN cd cmd/dashboard && go build -o app -ldflags="-s -w -X github.com/naiba/nezha/service/dao.Version=$(git rev-parse HEAD)" RUN cd cmd/dashboard && go build -o app -ldflags="-s -w"
FROM alpine:latest FROM alpine:latest
RUN apk --no-cache --no-progress add \ RUN apk --no-cache --no-progress add \

View File

@ -179,3 +179,13 @@ URL 里面也可放置占位符,请求时会进行简单的字符串替换。
- [哪吒面板,一个便携服务器状态监控面板搭建教程,不想拥有一个自己的探针吗?](https://haoduck.com/644.html) - [哪吒面板,一个便携服务器状态监控面板搭建教程,不想拥有一个自己的探针吗?](https://haoduck.com/644.html)
- [哪吒面板:小鸡们的最佳探针](https://www.zhujizixun.com/2843.html) *(已过时)* - [哪吒面板:小鸡们的最佳探针](https://www.zhujizixun.com/2843.html) *(已过时)*
- [>>更多教程](https://www.google.com/search?q=%22%E5%93%AA%E5%90%92%E9%9D%A2%E6%9D%BF%22+%22%E6%95%99%E7%A8%8B%22) (Google) - [>>更多教程](https://www.google.com/search?q=%22%E5%93%AA%E5%90%92%E9%9D%A2%E6%9D%BF%22+%22%E6%95%99%E7%A8%8B%22) (Google)
## 变更日志
- `0.2.0` **重大更新**
增加了服务监控TCP端口延迟、Ping、HTTP-SSL 证书)功能,此版本 Agent 与旧面板不兼容,而 Agent 是通过 GitHub Release 自动更新的 所以务必更新面板开启最新功能。
- `0.1.23` 新增 IP 变更通知功能
在后台设置界面启用。

View File

@ -2,13 +2,18 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io"
"log" "log"
"net"
"net/http"
"os" "os"
"strings"
"time" "time"
"github.com/blang/semver" "github.com/blang/semver"
"github.com/genkiroid/cert"
"github.com/go-ping/ping"
"github.com/p14yground/go-github-selfupdate/selfupdate" "github.com/p14yground/go-github-selfupdate/selfupdate"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc" "google.golang.org/grpc"
@ -105,7 +110,6 @@ func run(cmd *cobra.Command, args []string) {
var err error var err error
var conn *grpc.ClientConn var conn *grpc.ClientConn
var hc pb.NezhaService_HeartbeatClient
retry := func() { retry := func() {
log.Println("Error to close connection ...") log.Println("Error to close connection ...")
@ -125,43 +129,90 @@ func run(cmd *cobra.Command, args []string) {
} }
client = pb.NewNezhaServiceClient(conn) client = pb.NewNezhaServiceClient(conn)
// 第一步注册 // 第一步注册
_, err = client.Register(ctx, monitor.GetHost().PB()) _, err = client.ReportSystemInfo(ctx, monitor.GetHost().PB())
if err != nil { if err != nil {
log.Printf("client.Register err: %v", err) log.Printf("client.ReportSystemInfo err: %v", err)
retry() retry()
continue continue
} }
// 心跳接收控制命令 // 执行 Task
hc, err = client.Heartbeat(ctx, &pb.Beat{ tasks, err := client.RequestTask(ctx, monitor.GetHost().PB())
Timestamp: fmt.Sprintf("%v", time.Now()),
})
if err != nil { if err != nil {
log.Printf("client.Heartbeat err: %v", err) log.Printf("client.RequestTask err: %v", err)
retry() retry()
continue continue
} }
err = receiveCommand(hc) err = receiveTasks(tasks)
log.Printf("receiveCommand exit to main: %v", err) log.Printf("receiveCommand exit to main: %v", err)
retry() retry()
} }
} }
func receiveCommand(hc pb.NezhaService_HeartbeatClient) error { func receiveTasks(tasks pb.NezhaService_RequestTaskClient) error {
var err error var err error
var action *pb.Command var task *pb.Task
defer log.Printf("receiveCommand exit %v %v => %v", time.Now(), action, err) defer log.Printf("receiveTasks exit %v %v => %v", time.Now(), task, err)
for { for {
action, err = hc.Recv() task, err = tasks.Recv()
if err == io.EOF {
return nil
}
if err != nil { if err != nil {
return err return err
} }
switch action.GetType() { var result pb.TaskResult
result.Id = task.GetId()
switch task.GetType() {
case model.MonitorTypeHTTPGET:
start := time.Now()
resp, err := http.Get(task.GetData())
if err == nil {
result.Delay = float32(time.Now().Sub(start).Microseconds()) / 1000.0
if resp.StatusCode > 299 || resp.StatusCode < 200 {
err = errors.New("\n应用错误" + resp.Status)
}
}
var certs cert.Certs
if err == nil {
if strings.HasPrefix(task.GetData(), "https://") {
certs, err = cert.NewCerts([]string{task.GetData()})
}
}
if err == nil {
if len(certs) == 0 {
err = errors.New("\n获取SSL证书错误未获取到证书")
}
}
if err == nil {
result.Data = certs[0].Issuer
result.Successful = true
} else {
result.Data = err.Error()
}
case model.MonitorTypeICMPPing:
pinger, err := ping.NewPinger(task.GetData())
if err == nil {
pinger.Count = 10
err = pinger.Run() // Blocks until finished.
}
if err == nil {
stat := pinger.Statistics()
result.Delay = float32(stat.AvgRtt.Microseconds()) / 1000.0
result.Successful = true
} else {
result.Data = err.Error()
}
case model.MonitorTypeTCPPing:
start := time.Now()
conn, err := net.DialTimeout("tcp", task.GetData(), time.Second*10)
if err == nil {
conn.Close()
result.Delay = float32(time.Now().Sub(start).Microseconds()) / 1000.0
result.Successful = true
} else {
result.Data = err.Error()
}
default: default:
log.Printf("Unknown action: %v", action) log.Printf("Unknown action: %v", task)
} }
client.ReportTask(ctx, &result)
} }
} }
@ -172,14 +223,14 @@ func reportState() {
for { for {
if client != nil { if client != nil {
monitor.TrackNetworkSpeed() monitor.TrackNetworkSpeed()
_, err = client.ReportState(ctx, monitor.GetState(dao.ReportDelay).PB()) _, err = client.ReportSystemState(ctx, monitor.GetState(dao.ReportDelay).PB())
if err != nil { if err != nil {
log.Printf("reportState error %v", err) log.Printf("reportState error %v", err)
time.Sleep(delayWhenError) time.Sleep(delayWhenError)
} }
if lastReportHostInfo.Before(time.Now().Add(-10 * time.Minute)) { if lastReportHostInfo.Before(time.Now().Add(-10 * time.Minute)) {
lastReportHostInfo = time.Now() lastReportHostInfo = time.Now()
client.Register(ctx, monitor.GetHost().PB()) client.ReportSystemInfo(ctx, monitor.GetHost().PB())
} }
} }
} }

View File

@ -20,9 +20,64 @@ func (cp *commonPage) serve() {
cr := cp.r.Group("") cr := cp.r.Group("")
cr.Use(mygin.Authorize(mygin.AuthorizeOption{})) cr.Use(mygin.Authorize(mygin.AuthorizeOption{}))
cr.GET("/", cp.home) cr.GET("/", cp.home)
cr.GET("/service", cp.service)
cr.GET("/ws", cp.ws) cr.GET("/ws", cp.ws)
} }
type ServiceItem struct {
Monitor model.Monitor
TotalUp uint64
TotalDown uint64
Delay *[30]float32
Up *[30]int
Down *[30]int
}
func (p *commonPage) service(c *gin.Context) {
var ms []model.Monitor
dao.DB.Find(&ms)
year, month, day := time.Now().Date()
today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
var mhs []model.MonitorHistory
dao.DB.Where("created_at >= ?", today.AddDate(0, 0, -29)).Find(&mhs)
msm := make(map[uint64]*ServiceItem)
for i := 0; i < len(ms); i++ {
msm[ms[i].ID] = &ServiceItem{
Monitor: ms[i],
Delay: &[30]float32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
Up: &[30]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
Down: &[30]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}
}
// 整合数据
for i := 0; i < len(mhs); i++ {
dayIndex := 29
if mhs[i].CreatedAt.Before(today) {
dayIndex = 28 - (int(today.Sub(mhs[i].CreatedAt).Hours()) / 24)
}
if mhs[i].Successful {
msm[mhs[i].MonitorID].TotalUp++
msm[mhs[i].MonitorID].Delay[dayIndex] = (msm[mhs[i].MonitorID].Delay[dayIndex]*float32(msm[mhs[i].MonitorID].Up[dayIndex]) + mhs[i].Delay) / float32(msm[mhs[i].MonitorID].Up[dayIndex]+1)
msm[mhs[i].MonitorID].Up[dayIndex]++
} else {
msm[mhs[i].MonitorID].TotalDown++
msm[mhs[i].MonitorID].Down[dayIndex]++
}
}
u, ok := c.Get(model.CtxKeyAuthorizedUser)
data := mygin.CommonEnvironment(c, gin.H{
"Title": "服务状态",
"Services": msm,
})
if ok {
data["Admin"] = u
}
c.HTML(http.StatusOK, "theme-"+dao.Conf.Site.Theme+"/service", data)
}
func (cp *commonPage) home(c *gin.Context) { func (cp *commonPage) home(c *gin.Context) {
dao.ServerLock.RLock() dao.ServerLock.RLock()
defer dao.ServerLock.RUnlock() defer dao.ServerLock.RUnlock()

View File

@ -13,7 +13,6 @@ import (
"github.com/naiba/nezha/service/dao" "github.com/naiba/nezha/service/dao"
) )
// ServeWeb ..
func ServeWeb(port uint) { func ServeWeb(port uint) {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
if dao.Conf.Debug { if dao.Conf.Debug {
@ -43,6 +42,35 @@ func ServeWeb(port uint) {
"ts": func(s string) string { "ts": func(s string) string {
return strings.TrimSpace(s) return strings.TrimSpace(s)
}, },
"divU64": func(a, b uint64) float32 {
if b == 0 {
if a > 0 {
return 100
}
return 0
}
return float32(a) / float32(b) * 100
},
"div": func(a, b int) float32 {
if b == 0 {
if a > 0 {
return 100
}
return 0
}
return float32(a) / float32(b) * 100
},
"addU64": func(a, b uint64) uint64 {
return a + b
},
"add": func(a, b int) int {
return a + b
},
"dayBefore": func(i int) string {
year, month, day := time.Now().Date()
today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
return today.AddDate(0, 0, i-29).Format("1月2号")
},
}) })
r.Static("/static", "resource/static") r.Static("/static", "resource/static")
r.LoadHTMLGlob("resource/template/**/*") r.LoadHTMLGlob("resource/template/**/*")

View File

@ -33,6 +33,7 @@ func (ma *memberAPI) serve() {
mr.POST("/logout", ma.logout) mr.POST("/logout", ma.logout)
mr.POST("/server", ma.addOrEditServer) mr.POST("/server", ma.addOrEditServer)
mr.POST("/monitor", ma.addOrEditMonitor)
mr.POST("/notification", ma.addOrEditNotification) mr.POST("/notification", ma.addOrEditNotification)
mr.POST("/alert-rule", ma.addOrEditAlertRule) mr.POST("/alert-rule", ma.addOrEditAlertRule)
mr.POST("/setting", ma.updateSetting) mr.POST("/setting", ma.updateSetting)
@ -64,6 +65,8 @@ func (ma *memberAPI) delete(c *gin.Context) {
if err == nil { if err == nil {
alertmanager.OnDeleteNotification(id) alertmanager.OnDeleteNotification(id)
} }
case "monitor":
err = dao.DB.Delete(&model.Monitor{}, "id = ?", id).Error
case "alert-rule": case "alert-rule":
err = dao.DB.Delete(&model.AlertRule{}, "id = ?", id).Error err = dao.DB.Delete(&model.AlertRule{}, "id = ?", id).Error
if err == nil { if err == nil {
@ -125,7 +128,7 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
s.State = dao.ServerList[s.ID].State s.State = dao.ServerList[s.ID].State
} else { } else {
s.Host = &model.Host{} s.Host = &model.Host{}
s.State = &model.State{} s.State = &model.HostState{}
} }
dao.ServerList[s.ID] = &s dao.ServerList[s.ID] = &s
dao.ReSortServer() dao.ReSortServer()
@ -134,6 +137,42 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
}) })
} }
type monitorForm struct {
ID uint64
Name string
Target string
Type uint8
}
func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
var mf monitorForm
var m model.Monitor
err := c.ShouldBindJSON(&mf)
if err == nil {
m.Name = mf.Name
m.Target = mf.Target
m.Type = mf.Type
m.ID = mf.ID
}
if err == nil {
if m.ID == 0 {
err = dao.DB.Create(&m).Error
} else {
err = dao.DB.Save(&m).Error
}
}
if err != nil {
c.JSON(http.StatusOK, model.Response{
Code: http.StatusBadRequest,
Message: fmt.Sprintf("请求错误:%s", err),
})
return
}
c.JSON(http.StatusOK, model.Response{
Code: http.StatusOK,
})
}
type notificationForm struct { type notificationForm struct {
ID uint64 ID uint64
Name string Name string

View File

@ -23,6 +23,7 @@ func (mp *memberPage) serve() {
Redirect: "/login", Redirect: "/login",
})) }))
mr.GET("/server", mp.server) mr.GET("/server", mp.server)
mr.GET("/monitor", mp.monitor)
mr.GET("/notification", mp.notification) mr.GET("/notification", mp.notification)
mr.GET("/setting", mp.setting) mr.GET("/setting", mp.setting)
} }
@ -36,6 +37,15 @@ func (mp *memberPage) server(c *gin.Context) {
})) }))
} }
func (mp *memberPage) monitor(c *gin.Context) {
var monitors []model.Monitor
dao.DB.Find(&monitors)
c.HTML(http.StatusOK, "dashboard/monitor", mygin.CommonEnvironment(c, gin.H{
"Title": "服务监控",
"Monitors": monitors,
}))
}
func (mp *memberPage) notification(c *gin.Context) { func (mp *memberPage) notification(c *gin.Context) {
var nf []model.Notification var nf []model.Notification
dao.DB.Find(&nf) dao.DB.Find(&nf)

View File

@ -34,14 +34,16 @@ func init() {
} }
func initDB() { func initDB() {
dao.DB.AutoMigrate(model.Server{}, model.User{}, model.Notification{}, model.AlertRule{}) dao.DB.AutoMigrate(model.Server{}, model.User{},
model.Notification{}, model.AlertRule{}, model.Monitor{},
model.MonitorHistory{})
// load cache // load cache
var servers []model.Server var servers []model.Server
dao.DB.Find(&servers) dao.DB.Find(&servers)
for _, s := range servers { for _, s := range servers {
innerS := s innerS := s
innerS.Host = &model.Host{} innerS.Host = &model.Host{}
innerS.State = &model.State{} innerS.State = &model.HostState{}
dao.ServerList[innerS.ID] = &innerS dao.ServerList[innerS.ID] = &innerS
} }
dao.ReSortServer() dao.ReSortServer()
@ -50,5 +52,6 @@ func initDB() {
func main() { func main() {
go controller.ServeWeb(dao.Conf.HTTPPort) go controller.ServeWeb(dao.Conf.HTTPPort)
go rpc.ServeRPC(5555) go rpc.ServeRPC(5555)
go rpc.DispatchTask(time.Minute * 10)
alertmanager.Start() alertmanager.Start()
} }

View File

@ -2,15 +2,18 @@ package rpc
import ( import (
"fmt" "fmt"
"log"
"net" "net"
"time"
"google.golang.org/grpc" "google.golang.org/grpc"
"github.com/naiba/nezha/model"
pb "github.com/naiba/nezha/proto" pb "github.com/naiba/nezha/proto"
"github.com/naiba/nezha/service/dao"
rpcService "github.com/naiba/nezha/service/rpc" rpcService "github.com/naiba/nezha/service/rpc"
) )
// ServeRPC ...
func ServeRPC(port uint) { func ServeRPC(port uint) {
server := grpc.NewServer() server := grpc.NewServer()
pb.RegisterNezhaServiceServer(server, &rpcService.NezhaHandler{ pb.RegisterNezhaServiceServer(server, &rpcService.NezhaHandler{
@ -22,3 +25,34 @@ func ServeRPC(port uint) {
} }
server.Serve(listen) server.Serve(listen)
} }
func DispatchTask(duration time.Duration) {
var index uint64 = 0
for {
var tasks []model.Monitor
var hasAliveAgent bool
dao.DB.Find(&tasks)
dao.ServerLock.RLock()
for i := 0; i < len(tasks); i++ {
if index >= uint64(len(dao.SortedServerList)) {
index = 0
if !hasAliveAgent {
break
}
hasAliveAgent = false
}
if dao.SortedServerList[index].TaskStream == nil {
i--
index++
continue
}
hasAliveAgent = true
log.Println("DispatchTask 确认派发 >>>>>", i, index)
dao.SortedServerList[index].TaskStream.Send(tasks[i].PB())
log.Println("DispatchTask 确认派发 <<<<<", i, index)
index++
}
dao.ServerLock.RUnlock()
time.Sleep(duration)
}
}

View File

@ -1,13 +1,38 @@
package main package main
import ( import (
"fmt"
"log" "log"
"net"
"os/exec" "os/exec"
"time"
"github.com/genkiroid/cert"
"github.com/go-ping/ping"
"github.com/shirou/gopsutil/v3/disk" "github.com/shirou/gopsutil/v3/disk"
) )
func main() { func main() {
conn, err := net.DialTimeout("tcp", "example.com:80", time.Second*10)
if err != nil {
panic(err)
}
println(conn)
pinger, err := ping.NewPinger("example.com")
if err != nil {
panic(err)
}
pinger.Count = 3
err = pinger.Run() // Blocks until finished.
if err != nil {
panic(err)
}
fmt.Printf("%+v", pinger.Statistics())
certs, err := cert.NewCerts([]string{"example.com"})
if err != nil {
panic(err)
}
fmt.Println(certs)
dparts, _ := disk.Partitions(false) dparts, _ := disk.Partitions(false)
for _, part := range dparts { for _, part := range dparts {
u, _ := disk.Usage(part.Mountpoint) u, _ := disk.Usage(part.Mountpoint)
@ -18,12 +43,12 @@ func main() {
} }
func cmdExec() { func cmdExec() {
cmd := exec.Command("ping", "qiongbi.net", "-c2") cmd := exec.Command("ping", "example.com", "-c2")
output, err := cmd.Output() output, err := cmd.Output()
log.Println("output:", string(output)) log.Println("output:", string(output))
log.Println("err:", err) log.Println("err:", err)
cmd = exec.Command("ping", "qiongbi", "-c2") cmd = exec.Command("ping", "example", "-c2")
output, err = cmd.Output() output, err = cmd.Output()
log.Println("output:", string(output)) log.Println("output:", string(output))
log.Println("err:", err) log.Println("err:", err)

2
go.mod
View File

@ -6,7 +6,9 @@ require (
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48 code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48
github.com/blang/semver v3.5.1+incompatible github.com/blang/semver v3.5.1+incompatible
github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify v1.4.9
github.com/genkiroid/cert v0.0.0-20191007122723-897560fbbe50
github.com/gin-gonic/gin v1.6.3 github.com/gin-gonic/gin v1.6.3
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.2
github.com/google/go-github v17.0.0+incompatible github.com/google/go-github v17.0.0+incompatible
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2

6
go.sum
View File

@ -83,6 +83,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/genkiroid/cert v0.0.0-20191007122723-897560fbbe50 h1:vLwmYBduhnWWqShoUGbVgDulhcLdanoYtCQxYMzwaqQ=
github.com/genkiroid/cert v0.0.0-20191007122723-897560fbbe50/go.mod h1:Pb7nyGYAfDyE/IkU6AJeRshIFko0wJC9cOqeYzYQffk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
@ -96,6 +98,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663 h1:jI2GiiRh+pPbey52EVmbU6kuLiXqwy4CXZ4gwUBj8Y0=
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663/go.mod h1:35JbSyV/BYqHwwRA6Zr1uVDm1637YlNOU61wI797NPI=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
@ -417,6 +421,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

View File

@ -118,7 +118,7 @@ func (r *AlertRule) Check(points [][]interface{}) (int, string) {
fail++ fail++
} }
} }
if fail/total > 0.3 { if fail/total > 0.5 {
count++ count++
dist.WriteString(fmt.Sprintf("%+v\n", r.Rules[i])) dist.WriteString(fmt.Sprintf("%+v\n", r.Rules[i]))
} }

View File

@ -2,13 +2,10 @@ package model
import "time" import "time"
// CtxKeyAuthorizedUser ..
const CtxKeyAuthorizedUser = "ckau" const CtxKeyAuthorizedUser = "ckau"
// CtxKeyOauth2State ..
const CtxKeyOauth2State = "cko2s" const CtxKeyOauth2State = "cko2s"
// Common ..
type Common struct { type Common struct {
ID uint64 `gorm:"primary_key"` ID uint64 `gorm:"primary_key"`
CreatedAt time.Time CreatedAt time.Time
@ -16,7 +13,6 @@ type Common struct {
DeletedAt *time.Time `sql:"index"` DeletedAt *time.Time `sql:"index"`
} }
// Response ..
type Response struct { type Response struct {
Code uint64 `json:"code,omitempty"` Code uint64 `json:"code,omitempty"`
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`

View File

@ -29,7 +29,6 @@ type Config struct {
v *viper.Viper v *viper.Viper
} }
// ReadInConfig ..
func (c *Config) Read(path string) error { func (c *Config) Read(path string) error {
c.v = viper.New() c.v = viper.New()
c.v.SetConfigFile(path) c.v.SetConfigFile(path)

114
model/host.go Normal file
View File

@ -0,0 +1,114 @@
package model
import (
"fmt"
pb "github.com/naiba/nezha/proto"
)
const (
_ = iota
MTReportHostState
)
type HostState struct {
CPU float64
MemUsed uint64
SwapUsed uint64
DiskUsed uint64
NetInTransfer uint64
NetOutTransfer uint64
NetInSpeed uint64
NetOutSpeed uint64
Uptime uint64
}
func (s *HostState) PB() *pb.State {
return &pb.State{
Cpu: s.CPU,
MemUsed: s.MemUsed,
SwapUsed: s.SwapUsed,
DiskUsed: s.DiskUsed,
NetInTransfer: s.NetInTransfer,
NetOutTransfer: s.NetOutTransfer,
NetInSpeed: s.NetInSpeed,
NetOutSpeed: s.NetOutSpeed,
Uptime: s.Uptime,
}
}
func PB2State(s *pb.State) HostState {
return HostState{
CPU: s.GetCpu(),
MemUsed: s.GetMemUsed(),
SwapUsed: s.GetSwapUsed(),
DiskUsed: s.GetDiskUsed(),
NetInTransfer: s.GetNetInTransfer(),
NetOutTransfer: s.GetNetOutTransfer(),
NetInSpeed: s.GetNetInSpeed(),
NetOutSpeed: s.GetNetOutSpeed(),
Uptime: s.GetUptime(),
}
}
type Host struct {
Platform string
PlatformVersion string
CPU []string
MemTotal uint64
DiskTotal uint64
SwapTotal uint64
Arch string
Virtualization string
BootTime uint64
IP string `json:"-"`
CountryCode string
Version string
}
func (h *Host) PB() *pb.Host {
return &pb.Host{
Platform: h.Platform,
PlatformVersion: h.PlatformVersion,
Cpu: h.CPU,
MemTotal: h.MemTotal,
DiskTotal: h.DiskTotal,
SwapTotal: h.SwapTotal,
Arch: h.Arch,
Virtualization: h.Virtualization,
BootTime: h.BootTime,
Ip: h.IP,
CountryCode: h.CountryCode,
Version: h.Version,
}
}
func PB2Host(h *pb.Host) Host {
cpuCount := make(map[string]int, 0)
cpus := h.GetCpu()
for _, u := range cpus {
cpuCount[u]++
}
var distCpu []string
for u, num := range cpuCount {
distCpu = append(distCpu, fmt.Sprintf("%sx%d", u, num))
}
return Host{
Platform: h.GetPlatform(),
PlatformVersion: h.GetPlatformVersion(),
CPU: distCpu,
MemTotal: h.GetMemTotal(),
DiskTotal: h.GetDiskTotal(),
SwapTotal: h.GetSwapTotal(),
Arch: h.GetArch(),
Virtualization: h.GetVirtualization(),
BootTime: h.GetBootTime(),
IP: h.GetIp(),
CountryCode: h.GetCountryCode(),
Version: h.GetVersion(),
}
}

View File

@ -1,120 +1,27 @@
package model package model
import ( import (
"fmt"
pb "github.com/naiba/nezha/proto" pb "github.com/naiba/nezha/proto"
) )
const ( const (
_ = iota _ = iota
// MTReportState .. MonitorTypeHTTPGET
MTReportState MonitorTypeICMPPing
MonitorTypeTCPPing
) )
// State .. type Monitor struct {
type State struct { Common
CPU float64 Name string
MemUsed uint64 Type uint8
SwapUsed uint64 Target string
DiskUsed uint64
NetInTransfer uint64
NetOutTransfer uint64
NetInSpeed uint64
NetOutSpeed uint64
Uptime uint64
} }
// PB .. func (m *Monitor) PB() *pb.Task {
func (s *State) PB() *pb.State { return &pb.Task{
return &pb.State{ Id: m.ID,
Cpu: s.CPU, Type: uint64(m.Type),
MemUsed: s.MemUsed, Data: m.Target,
SwapUsed: s.SwapUsed,
DiskUsed: s.DiskUsed,
NetInTransfer: s.NetInTransfer,
NetOutTransfer: s.NetOutTransfer,
NetInSpeed: s.NetInSpeed,
NetOutSpeed: s.NetOutSpeed,
Uptime: s.Uptime,
}
}
// PB2State ..
func PB2State(s *pb.State) State {
return State{
CPU: s.GetCpu(),
MemUsed: s.GetMemUsed(),
SwapUsed: s.GetSwapUsed(),
DiskUsed: s.GetDiskUsed(),
NetInTransfer: s.GetNetInTransfer(),
NetOutTransfer: s.GetNetOutTransfer(),
NetInSpeed: s.GetNetInSpeed(),
NetOutSpeed: s.GetNetOutSpeed(),
Uptime: s.GetUptime(),
}
}
// Host ..
type Host struct {
Platform string
PlatformVersion string
CPU []string
MemTotal uint64
DiskTotal uint64
SwapTotal uint64
Arch string
Virtualization string
BootTime uint64
IP string `json:"-"`
CountryCode string
Version string
}
// PB ..
func (h *Host) PB() *pb.Host {
return &pb.Host{
Platform: h.Platform,
PlatformVersion: h.PlatformVersion,
Cpu: h.CPU,
MemTotal: h.MemTotal,
DiskTotal: h.DiskTotal,
SwapTotal: h.SwapTotal,
Arch: h.Arch,
Virtualization: h.Virtualization,
BootTime: h.BootTime,
Ip: h.IP,
CountryCode: h.CountryCode,
Version: h.Version,
}
}
// PB2Host ...
func PB2Host(h *pb.Host) Host {
cpuCount := make(map[string]int, 0)
cpus := h.GetCpu()
for _, u := range cpus {
cpuCount[u]++
}
var distCpu []string
for u, num := range cpuCount {
distCpu = append(distCpu, fmt.Sprintf("%sx%d", u, num))
}
return Host{
Platform: h.GetPlatform(),
PlatformVersion: h.GetPlatformVersion(),
CPU: distCpu,
MemTotal: h.GetMemTotal(),
DiskTotal: h.GetDiskTotal(),
SwapTotal: h.GetSwapTotal(),
Arch: h.GetArch(),
Virtualization: h.GetVirtualization(),
BootTime: h.GetBootTime(),
IP: h.GetIp(),
CountryCode: h.GetCountryCode(),
Version: h.GetVersion(),
} }
} }

22
model/monitor_history.go Normal file
View File

@ -0,0 +1,22 @@
package model
import (
pb "github.com/naiba/nezha/proto"
)
type MonitorHistory struct {
Common
MonitorID uint64
Delay float32 // 延迟,毫秒
Data string
Successful bool // 是否成功
}
func PB2MonitorHistory(r *pb.TaskResult) MonitorHistory {
return MonitorHistory{
Delay: r.GetDelay(),
Successful: r.GetSuccessful(),
MonitorID: r.GetId(),
Data: r.GetData(),
}
}

View File

@ -8,19 +8,18 @@ import (
pb "github.com/naiba/nezha/proto" pb "github.com/naiba/nezha/proto"
) )
// Server ..
type Server struct { type Server struct {
Common Common
Name string Name string
DisplayIndex int // 展示权重,越大越靠前 DisplayIndex int // 展示权重,越大越靠前
Secret string `json:"-"` Secret string `json:"-"`
Tag string Tag string
Host *Host `gorm:"-"` Host *Host `gorm:"-"`
State *State `gorm:"-"` State *HostState `gorm:"-"`
LastActive time.Time LastActive time.Time
Stream pb.NezhaService_HeartbeatServer `gorm:"-" json:"-"` TaskClose chan error `gorm:"-" json:"-"`
StreamClose chan<- error `gorm:"-" json:"-"` TaskStream pb.NezhaService_RequestTaskServer `gorm:"-" json:"-"`
} }
func (s Server) Marshal() template.JS { func (s Server) Marshal() template.JS {

View File

@ -8,7 +8,6 @@ import (
"github.com/naiba/com" "github.com/naiba/com"
) )
// User ...
type User struct { type User struct {
Common Common
Login string `gorm:"UNIQUE_INDEX" json:"login,omitempty"` // 登录名 Login string `gorm:"UNIQUE_INDEX" json:"login,omitempty"` // 登录名
@ -26,7 +25,6 @@ type User struct {
TeamsID []uint64 `gorm:"-"` TeamsID []uint64 `gorm:"-"`
} }
// NewUserFromGitHub ..
func NewUserFromGitHub(gu *github.User) User { func NewUserFromGitHub(gu *github.User) User {
var u User var u User
u.ID = uint64(gu.GetID()) u.ID = uint64(gu.GetID())
@ -45,7 +43,6 @@ func NewUserFromGitHub(gu *github.User) User {
return u return u
} }
// IssueNewToken ...
func (u *User) IssueNewToken() { func (u *User) IssueNewToken() {
u.Token = com.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login)) u.Token = com.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login))
u.TokenExpired = time.Now().AddDate(0, 2, 0) u.TokenExpired = time.Now().AddDate(0, 2, 0)

View File

@ -11,7 +11,6 @@ import (
"github.com/naiba/nezha/service/dao" "github.com/naiba/nezha/service/dao"
) )
// AuthorizeOption ..
type AuthorizeOption struct { type AuthorizeOption struct {
Guest bool Guest bool
Member bool Member bool
@ -21,7 +20,6 @@ type AuthorizeOption struct {
Btn string Btn string
} }
// Authorize ..
func Authorize(opt AuthorizeOption) func(*gin.Context) { func Authorize(opt AuthorizeOption) func(*gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
token, err := c.Cookie(dao.Conf.Site.CookieName) token, err := c.Cookie(dao.Conf.Site.CookieName)

View File

@ -8,7 +8,6 @@ import (
"github.com/naiba/nezha/model" "github.com/naiba/nezha/model"
) )
// ErrInfo ..
type ErrInfo struct { type ErrInfo struct {
Code uint64 Code uint64
Title string Title string
@ -17,7 +16,6 @@ type ErrInfo struct {
Btn string Btn string
} }
// ShowErrorPage ..
func ShowErrorPage(c *gin.Context, i ErrInfo, isPage bool) { func ShowErrorPage(c *gin.Context, i ErrInfo, isPage bool) {
if isPage { if isPage {
c.HTML(http.StatusOK, "dashboard/error", CommonEnvironment(c, gin.H{ c.HTML(http.StatusOK, "dashboard/error", CommonEnvironment(c, gin.H{

View File

@ -10,7 +10,6 @@ import (
"github.com/naiba/nezha/service/dao" "github.com/naiba/nezha/service/dao"
) )
// CommonEnvironment ..
func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H { func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H {
data["MatchedPath"] = c.MustGet("MatchedPath") data["MatchedPath"] = c.MustGet("MatchedPath")
data["Version"] = dao.Version data["Version"] = dao.Version
@ -27,7 +26,6 @@ func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H {
return data return data
} }
// RecordPath ..
func RecordPath(c *gin.Context) { func RecordPath(c *gin.Context) {
url := c.Request.URL.String() url := c.Request.URL.String()
for _, p := range c.Params { for _, p := range c.Params {

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.25.0 // protoc-gen-go v1.25.0
// protoc v3.13.0 // protoc v3.14.0
// source: proto/nezha.proto // source: proto/nezha.proto
package proto package proto
@ -275,6 +275,148 @@ func (x *State) GetUptime() uint64 {
return 0 return 0
} }
type Task struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Type uint64 `protobuf:"varint,2,opt,name=type,proto3" json:"type,omitempty"`
Data string `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
}
func (x *Task) Reset() {
*x = Task{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_nezha_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Task) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Task) ProtoMessage() {}
func (x *Task) ProtoReflect() protoreflect.Message {
mi := &file_proto_nezha_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Task.ProtoReflect.Descriptor instead.
func (*Task) Descriptor() ([]byte, []int) {
return file_proto_nezha_proto_rawDescGZIP(), []int{2}
}
func (x *Task) GetId() uint64 {
if x != nil {
return x.Id
}
return 0
}
func (x *Task) GetType() uint64 {
if x != nil {
return x.Type
}
return 0
}
func (x *Task) GetData() string {
if x != nil {
return x.Data
}
return ""
}
type TaskResult struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Type uint64 `protobuf:"varint,2,opt,name=type,proto3" json:"type,omitempty"`
Delay float32 `protobuf:"fixed32,3,opt,name=delay,proto3" json:"delay,omitempty"`
Data string `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
Successful bool `protobuf:"varint,5,opt,name=successful,proto3" json:"successful,omitempty"`
}
func (x *TaskResult) Reset() {
*x = TaskResult{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_nezha_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TaskResult) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TaskResult) ProtoMessage() {}
func (x *TaskResult) ProtoReflect() protoreflect.Message {
mi := &file_proto_nezha_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TaskResult.ProtoReflect.Descriptor instead.
func (*TaskResult) Descriptor() ([]byte, []int) {
return file_proto_nezha_proto_rawDescGZIP(), []int{3}
}
func (x *TaskResult) GetId() uint64 {
if x != nil {
return x.Id
}
return 0
}
func (x *TaskResult) GetType() uint64 {
if x != nil {
return x.Type
}
return 0
}
func (x *TaskResult) GetDelay() float32 {
if x != nil {
return x.Delay
}
return 0
}
func (x *TaskResult) GetData() string {
if x != nil {
return x.Data
}
return ""
}
func (x *TaskResult) GetSuccessful() bool {
if x != nil {
return x.Successful
}
return false
}
type Receipt struct { type Receipt struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -286,7 +428,7 @@ type Receipt struct {
func (x *Receipt) Reset() { func (x *Receipt) Reset() {
*x = Receipt{} *x = Receipt{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_proto_nezha_proto_msgTypes[2] mi := &file_proto_nezha_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -299,7 +441,7 @@ func (x *Receipt) String() string {
func (*Receipt) ProtoMessage() {} func (*Receipt) ProtoMessage() {}
func (x *Receipt) ProtoReflect() protoreflect.Message { func (x *Receipt) ProtoReflect() protoreflect.Message {
mi := &file_proto_nezha_proto_msgTypes[2] mi := &file_proto_nezha_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -312,7 +454,7 @@ func (x *Receipt) ProtoReflect() protoreflect.Message {
// Deprecated: Use Receipt.ProtoReflect.Descriptor instead. // Deprecated: Use Receipt.ProtoReflect.Descriptor instead.
func (*Receipt) Descriptor() ([]byte, []int) { func (*Receipt) Descriptor() ([]byte, []int) {
return file_proto_nezha_proto_rawDescGZIP(), []int{2} return file_proto_nezha_proto_rawDescGZIP(), []int{4}
} }
func (x *Receipt) GetProced() bool { func (x *Receipt) GetProced() bool {
@ -322,108 +464,6 @@ func (x *Receipt) GetProced() bool {
return false return false
} }
type Beat struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Timestamp string `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
}
func (x *Beat) Reset() {
*x = Beat{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_nezha_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Beat) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Beat) ProtoMessage() {}
func (x *Beat) ProtoReflect() protoreflect.Message {
mi := &file_proto_nezha_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Beat.ProtoReflect.Descriptor instead.
func (*Beat) Descriptor() ([]byte, []int) {
return file_proto_nezha_proto_rawDescGZIP(), []int{3}
}
func (x *Beat) GetTimestamp() string {
if x != nil {
return x.Timestamp
}
return ""
}
type Command struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type uint64 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"`
Data string `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
}
func (x *Command) Reset() {
*x = Command{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_nezha_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Command) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Command) ProtoMessage() {}
func (x *Command) ProtoReflect() protoreflect.Message {
mi := &file_proto_nezha_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Command.ProtoReflect.Descriptor instead.
func (*Command) Descriptor() ([]byte, []int) {
return file_proto_nezha_proto_rawDescGZIP(), []int{4}
}
func (x *Command) GetType() uint64 {
if x != nil {
return x.Type
}
return 0
}
func (x *Command) GetData() string {
if x != nil {
return x.Data
}
return ""
}
var File_proto_nezha_proto protoreflect.FileDescriptor var File_proto_nezha_proto protoreflect.FileDescriptor
var file_proto_nezha_proto_rawDesc = []byte{ var file_proto_nezha_proto_rawDesc = []byte{
@ -468,25 +508,36 @@ var file_proto_nezha_proto_rawDesc = []byte{
0x65, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x70,
0x65, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x4f, 0x75, 0x65, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x4f, 0x75,
0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x21, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x3e,
0x0a, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x63, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02,
0x64, 0x22, 0x24, 0x0a, 0x04, 0x42, 0x65, 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x7a,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x31, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0e, 0x0a, 0x02,
0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04,
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x96, 0x01, 0x0a, 0x0c, 0x4e, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52,
0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x09, 0x48, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04,
0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75,
0x2e, 0x42, 0x65, 0x61, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x00, 0x30, 0x01, 0x12, 0x2d, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x21, 0x0a, 0x07, 0x52, 0x65,
0x6f, 0x72, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x18,
0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x32, 0xd6, 0x01,
0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x29, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x0a, 0x0c, 0x4e, 0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33,
0x73, 0x74, 0x65, 0x72, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x74,
0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x61, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74,
0x74, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x65, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70,
0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73,
0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63,
0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74,
0x54, 0x61, 0x73, 0x6b, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x61, 0x73,
0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x0b, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x61,
0x73, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x42, 0x07, 0x5a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -503,21 +554,23 @@ func file_proto_nezha_proto_rawDescGZIP() []byte {
var file_proto_nezha_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_proto_nezha_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_proto_nezha_proto_goTypes = []interface{}{ var file_proto_nezha_proto_goTypes = []interface{}{
(*Host)(nil), // 0: proto.Host (*Host)(nil), // 0: proto.Host
(*State)(nil), // 1: proto.State (*State)(nil), // 1: proto.State
(*Receipt)(nil), // 2: proto.Receipt (*Task)(nil), // 2: proto.Task
(*Beat)(nil), // 3: proto.Beat (*TaskResult)(nil), // 3: proto.TaskResult
(*Command)(nil), // 4: proto.Command (*Receipt)(nil), // 4: proto.Receipt
} }
var file_proto_nezha_proto_depIdxs = []int32{ var file_proto_nezha_proto_depIdxs = []int32{
3, // 0: proto.NezhaService.Heartbeat:input_type -> proto.Beat 1, // 0: proto.NezhaService.ReportSystemState:input_type -> proto.State
1, // 1: proto.NezhaService.ReportState:input_type -> proto.State 0, // 1: proto.NezhaService.ReportSystemInfo:input_type -> proto.Host
0, // 2: proto.NezhaService.Register:input_type -> proto.Host 3, // 2: proto.NezhaService.ReportTask:input_type -> proto.TaskResult
4, // 3: proto.NezhaService.Heartbeat:output_type -> proto.Command 0, // 3: proto.NezhaService.RequestTask:input_type -> proto.Host
2, // 4: proto.NezhaService.ReportState:output_type -> proto.Receipt 4, // 4: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt
2, // 5: proto.NezhaService.Register:output_type -> proto.Receipt 4, // 5: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt
3, // [3:6] is the sub-list for method output_type 4, // 6: proto.NezhaService.ReportTask:output_type -> proto.Receipt
0, // [0:3] is the sub-list for method input_type 2, // 7: proto.NezhaService.RequestTask:output_type -> proto.Task
4, // [4:8] is the sub-list for method output_type
0, // [0:4] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name 0, // [0:0] is the sub-list for field type_name
@ -554,7 +607,7 @@ func file_proto_nezha_proto_init() {
} }
} }
file_proto_nezha_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { file_proto_nezha_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Receipt); i { switch v := v.(*Task); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -566,7 +619,7 @@ func file_proto_nezha_proto_init() {
} }
} }
file_proto_nezha_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { file_proto_nezha_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Beat); i { switch v := v.(*TaskResult); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -578,7 +631,7 @@ func file_proto_nezha_proto_init() {
} }
} }
file_proto_nezha_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { file_proto_nezha_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Command); i { switch v := v.(*Receipt); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -622,9 +675,10 @@ const _ = grpc.SupportPackageIsVersion6
// //
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type NezhaServiceClient interface { type NezhaServiceClient interface {
Heartbeat(ctx context.Context, in *Beat, opts ...grpc.CallOption) (NezhaService_HeartbeatClient, error) ReportSystemState(ctx context.Context, in *State, opts ...grpc.CallOption) (*Receipt, error)
ReportState(ctx context.Context, in *State, opts ...grpc.CallOption) (*Receipt, error) ReportSystemInfo(ctx context.Context, in *Host, opts ...grpc.CallOption) (*Receipt, error)
Register(ctx context.Context, in *Host, opts ...grpc.CallOption) (*Receipt, error) ReportTask(ctx context.Context, in *TaskResult, opts ...grpc.CallOption) (*Receipt, error)
RequestTask(ctx context.Context, in *Host, opts ...grpc.CallOption) (NezhaService_RequestTaskClient, error)
} }
type nezhaServiceClient struct { type nezhaServiceClient struct {
@ -635,12 +689,39 @@ func NewNezhaServiceClient(cc grpc.ClientConnInterface) NezhaServiceClient {
return &nezhaServiceClient{cc} return &nezhaServiceClient{cc}
} }
func (c *nezhaServiceClient) Heartbeat(ctx context.Context, in *Beat, opts ...grpc.CallOption) (NezhaService_HeartbeatClient, error) { func (c *nezhaServiceClient) ReportSystemState(ctx context.Context, in *State, opts ...grpc.CallOption) (*Receipt, error) {
stream, err := c.cc.NewStream(ctx, &_NezhaService_serviceDesc.Streams[0], "/proto.NezhaService/Heartbeat", opts...) out := new(Receipt)
err := c.cc.Invoke(ctx, "/proto.NezhaService/ReportSystemState", in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
x := &nezhaServiceHeartbeatClient{stream} return out, nil
}
func (c *nezhaServiceClient) ReportSystemInfo(ctx context.Context, in *Host, opts ...grpc.CallOption) (*Receipt, error) {
out := new(Receipt)
err := c.cc.Invoke(ctx, "/proto.NezhaService/ReportSystemInfo", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nezhaServiceClient) ReportTask(ctx context.Context, in *TaskResult, opts ...grpc.CallOption) (*Receipt, error) {
out := new(Receipt)
err := c.cc.Invoke(ctx, "/proto.NezhaService/ReportTask", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nezhaServiceClient) RequestTask(ctx context.Context, in *Host, opts ...grpc.CallOption) (NezhaService_RequestTaskClient, error) {
stream, err := c.cc.NewStream(ctx, &_NezhaService_serviceDesc.Streams[0], "/proto.NezhaService/RequestTask", opts...)
if err != nil {
return nil, err
}
x := &nezhaServiceRequestTaskClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil { if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err return nil, err
} }
@ -650,140 +731,148 @@ func (c *nezhaServiceClient) Heartbeat(ctx context.Context, in *Beat, opts ...gr
return x, nil return x, nil
} }
type NezhaService_HeartbeatClient interface { type NezhaService_RequestTaskClient interface {
Recv() (*Command, error) Recv() (*Task, error)
grpc.ClientStream grpc.ClientStream
} }
type nezhaServiceHeartbeatClient struct { type nezhaServiceRequestTaskClient struct {
grpc.ClientStream grpc.ClientStream
} }
func (x *nezhaServiceHeartbeatClient) Recv() (*Command, error) { func (x *nezhaServiceRequestTaskClient) Recv() (*Task, error) {
m := new(Command) m := new(Task)
if err := x.ClientStream.RecvMsg(m); err != nil { if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err return nil, err
} }
return m, nil return m, nil
} }
func (c *nezhaServiceClient) ReportState(ctx context.Context, in *State, opts ...grpc.CallOption) (*Receipt, error) {
out := new(Receipt)
err := c.cc.Invoke(ctx, "/proto.NezhaService/ReportState", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nezhaServiceClient) Register(ctx context.Context, in *Host, opts ...grpc.CallOption) (*Receipt, error) {
out := new(Receipt)
err := c.cc.Invoke(ctx, "/proto.NezhaService/Register", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// NezhaServiceServer is the server API for NezhaService service. // NezhaServiceServer is the server API for NezhaService service.
type NezhaServiceServer interface { type NezhaServiceServer interface {
Heartbeat(*Beat, NezhaService_HeartbeatServer) error ReportSystemState(context.Context, *State) (*Receipt, error)
ReportState(context.Context, *State) (*Receipt, error) ReportSystemInfo(context.Context, *Host) (*Receipt, error)
Register(context.Context, *Host) (*Receipt, error) ReportTask(context.Context, *TaskResult) (*Receipt, error)
RequestTask(*Host, NezhaService_RequestTaskServer) error
} }
// UnimplementedNezhaServiceServer can be embedded to have forward compatible implementations. // UnimplementedNezhaServiceServer can be embedded to have forward compatible implementations.
type UnimplementedNezhaServiceServer struct { type UnimplementedNezhaServiceServer struct {
} }
func (*UnimplementedNezhaServiceServer) Heartbeat(*Beat, NezhaService_HeartbeatServer) error { func (*UnimplementedNezhaServiceServer) ReportSystemState(context.Context, *State) (*Receipt, error) {
return status.Errorf(codes.Unimplemented, "method Heartbeat not implemented") return nil, status.Errorf(codes.Unimplemented, "method ReportSystemState not implemented")
} }
func (*UnimplementedNezhaServiceServer) ReportState(context.Context, *State) (*Receipt, error) { func (*UnimplementedNezhaServiceServer) ReportSystemInfo(context.Context, *Host) (*Receipt, error) {
return nil, status.Errorf(codes.Unimplemented, "method ReportState not implemented") return nil, status.Errorf(codes.Unimplemented, "method ReportSystemInfo not implemented")
} }
func (*UnimplementedNezhaServiceServer) Register(context.Context, *Host) (*Receipt, error) { func (*UnimplementedNezhaServiceServer) ReportTask(context.Context, *TaskResult) (*Receipt, error) {
return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") return nil, status.Errorf(codes.Unimplemented, "method ReportTask not implemented")
}
func (*UnimplementedNezhaServiceServer) RequestTask(*Host, NezhaService_RequestTaskServer) error {
return status.Errorf(codes.Unimplemented, "method RequestTask not implemented")
} }
func RegisterNezhaServiceServer(s *grpc.Server, srv NezhaServiceServer) { func RegisterNezhaServiceServer(s *grpc.Server, srv NezhaServiceServer) {
s.RegisterService(&_NezhaService_serviceDesc, srv) s.RegisterService(&_NezhaService_serviceDesc, srv)
} }
func _NezhaService_Heartbeat_Handler(srv interface{}, stream grpc.ServerStream) error { func _NezhaService_ReportSystemState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
m := new(Beat)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(NezhaServiceServer).Heartbeat(m, &nezhaServiceHeartbeatServer{stream})
}
type NezhaService_HeartbeatServer interface {
Send(*Command) error
grpc.ServerStream
}
type nezhaServiceHeartbeatServer struct {
grpc.ServerStream
}
func (x *nezhaServiceHeartbeatServer) Send(m *Command) error {
return x.ServerStream.SendMsg(m)
}
func _NezhaService_ReportState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(State) in := new(State)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
if interceptor == nil { if interceptor == nil {
return srv.(NezhaServiceServer).ReportState(ctx, in) return srv.(NezhaServiceServer).ReportSystemState(ctx, in)
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/proto.NezhaService/ReportState", FullMethod: "/proto.NezhaService/ReportSystemState",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NezhaServiceServer).ReportState(ctx, req.(*State)) return srv.(NezhaServiceServer).ReportSystemState(ctx, req.(*State))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _NezhaService_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _NezhaService_ReportSystemInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Host) in := new(Host)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
if interceptor == nil { if interceptor == nil {
return srv.(NezhaServiceServer).Register(ctx, in) return srv.(NezhaServiceServer).ReportSystemInfo(ctx, in)
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/proto.NezhaService/Register", FullMethod: "/proto.NezhaService/ReportSystemInfo",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NezhaServiceServer).Register(ctx, req.(*Host)) return srv.(NezhaServiceServer).ReportSystemInfo(ctx, req.(*Host))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _NezhaService_ReportTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TaskResult)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NezhaServiceServer).ReportTask(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.NezhaService/ReportTask",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NezhaServiceServer).ReportTask(ctx, req.(*TaskResult))
}
return interceptor(ctx, in, info, handler)
}
func _NezhaService_RequestTask_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(Host)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(NezhaServiceServer).RequestTask(m, &nezhaServiceRequestTaskServer{stream})
}
type NezhaService_RequestTaskServer interface {
Send(*Task) error
grpc.ServerStream
}
type nezhaServiceRequestTaskServer struct {
grpc.ServerStream
}
func (x *nezhaServiceRequestTaskServer) Send(m *Task) error {
return x.ServerStream.SendMsg(m)
}
var _NezhaService_serviceDesc = grpc.ServiceDesc{ var _NezhaService_serviceDesc = grpc.ServiceDesc{
ServiceName: "proto.NezhaService", ServiceName: "proto.NezhaService",
HandlerType: (*NezhaServiceServer)(nil), HandlerType: (*NezhaServiceServer)(nil),
Methods: []grpc.MethodDesc{ Methods: []grpc.MethodDesc{
{ {
MethodName: "ReportState", MethodName: "ReportSystemState",
Handler: _NezhaService_ReportState_Handler, Handler: _NezhaService_ReportSystemState_Handler,
}, },
{ {
MethodName: "Register", MethodName: "ReportSystemInfo",
Handler: _NezhaService_Register_Handler, Handler: _NezhaService_ReportSystemInfo_Handler,
},
{
MethodName: "ReportTask",
Handler: _NezhaService_ReportTask_Handler,
}, },
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {
StreamName: "Heartbeat", StreamName: "RequestTask",
Handler: _NezhaService_Heartbeat_Handler, Handler: _NezhaService_RequestTask_Handler,
ServerStreams: true, ServerStreams: true,
}, },
}, },

View File

@ -1,11 +1,13 @@
syntax = "proto3"; syntax = "proto3";
option go_package = "proto";
package proto; package proto;
service NezhaService { service NezhaService {
rpc Heartbeat(Beat)returns(stream Command){} rpc ReportSystemState(State)returns(Receipt){}
rpc ReportState(State)returns(Receipt){} rpc ReportSystemInfo(Host)returns(Receipt){}
rpc Register(Host)returns(Receipt){} rpc ReportTask(TaskResult)returns(Receipt){}
rpc RequestTask(Host)returns(stream Task){}
} }
message Host { message Host {
@ -35,15 +37,20 @@ message State {
uint64 uptime = 10; uint64 uptime = 10;
} }
message Task {
uint64 id = 1;
uint64 type = 2;
string data = 3;
}
message TaskResult {
uint64 id = 1;
uint64 type = 2;
float delay = 3;
string data = 4;
bool successful = 5;
}
message Receipt{ message Receipt{
bool proced = 1; bool proced = 1;
} }
message Beat {
string timestamp = 1;
}
message Command {
uint64 type = 1;
string data = 2;
}

View File

@ -77,3 +77,56 @@
color: rgba(255, 255, 255, .7); color: rgba(255, 255, 255, .7);
font-weight: 700; font-weight: 700;
} }
.service-status .round>i {
width: 1rem;
height: 1rem;
border-radius: .5rem;
display: inline-block;
margin-right: .3rem;
background-color: slategray;
}
.service-status .danger.button {
background-color: crimson;
}
.service-status .good.button {
background-color: rgb(0, 235, 139);
}
.service-status .warning.button {
background-color: orange;
}
.service-status .danger>i {
background-color: crimson;
}
.service-status .good>i {
background-color: rgb(0, 235, 139);
}
.service-status .warning>i {
background-color: orange;
}
.service-status .three.column p {
display: inline-block;
}
.service-status .three.column p:last-child {
float: right;
font-size: smaller;
}
.service-status .eleven.column {
text-align: center;
}
.service-status .eleven.column>.ui.button {
width: 8px !important;
padding: unset !important;
margin-top: unset !important;
margin-bottom: unset !important;
}

View File

@ -39,7 +39,7 @@ function showFormModal(modelSelector, formID, URL, getData) {
obj[item.name] = (item.name.endsWith('_id') || obj[item.name] = (item.name.endsWith('_id') ||
item.name === 'id' || item.name === 'ID' || item.name === 'id' || item.name === 'ID' ||
item.name === 'RequestType' || item.name === 'RequestMethod' || item.name === 'RequestType' || item.name === 'RequestMethod' ||
item.name === 'DisplayIndex') ? parseInt(item.value) : item.value; item.name === 'DisplayIndex' || item.name === 'Type') ? parseInt(item.value) : item.value;
return obj; return obj;
}, {}); }, {});
$.post(URL, JSON.stringify(data)).done(function (resp) { $.post(URL, JSON.stringify(data)).done(function (resp) {
@ -118,6 +118,17 @@ function addOrEditServer(server) {
showFormModal('.server.modal', '#serverForm', '/api/server') showFormModal('.server.modal', '#serverForm', '/api/server')
} }
function addOrEditMonitor(monitor) {
const modal = $('.monitor.modal')
modal.children('.header').text((monitor ? '修改' : '添加') + '监控')
modal.find('.positive.button').html(monitor ? '修改<i class="edit icon"></i>' : '添加<i class="add icon"></i>')
modal.find('input[name=ID]').val(monitor ? monitor.ID : null)
modal.find('input[name=Name]').val(monitor ? monitor.Name : null)
modal.find('input[name=Target]').val(monitor ? monitor.Target : null)
modal.find('select[name=Type]').val(monitor ? monitor.Type : 1)
showFormModal('.monitor.modal', '#monitorForm', '/api/monitor')
}
function deleteRequest(api) { function deleteRequest(api) {
$.ajax({ $.ajax({
url: api, url: api,

View File

@ -1,8 +1,7 @@
{{define "common/footer"}} {{define "common/footer"}}
<div class="ui inverted vertical footer segment"> <div class="ui inverted vertical footer segment">
<div class="ui center aligned is-size-7 container"> <div class="ui center aligned is-size-7 container">
Powered by <a href="https://github.com/naiba/nezha" style="color: white;" target="_blank">哪吒面板</a> build · Powered by <a href="https://github.com/naiba/nezha" style="color: white;" target="_blank">哪吒面板</a> {{.Version}}
{{.Version}}
</div> </div>
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>

View File

@ -9,7 +9,7 @@
<title>{{.Title}}</title> <title>{{.Title}}</title>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.1/dist/semantic.min.css"> <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.1/dist/semantic.min.css">
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css"> <link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
<link rel="stylesheet" type="text/css" href="/static/main.css?v202012252026"> <link rel="stylesheet" type="text/css" href="/static/main.css?v202101160044">
<link rel="shortcut icon" type="image/png" href="/static/logo.png" /> <link rel="shortcut icon" type="image/png" href="/static/logo.png" />
</head> </head>

View File

@ -5,13 +5,14 @@
<img src="/static/logo.png"> <img src="/static/logo.png">
</div> </div>
<a class="item{{if eq .MatchedPath " /"}} active{{end}}" href="/">首页</a> <a class="item{{if eq .MatchedPath " /"}} active{{end}}" href="/">首页</a>
<a class="item{{if eq .MatchedPath " /service"}} active{{end}}" href="/service">服务状态</a>
{{if .Admin}} {{if .Admin}}
<a class="item{{if eq .MatchedPath " /server"}} active{{end}}" href="/server">服务器</a> <a class="item{{if eq .MatchedPath " /server"}} active{{end}}" href="/server">服务器</a>
<a class="item{{if eq .MatchedPath " /monitor"}} active{{end}}" href="/monitor">监控</a>
<a class="item{{if eq .MatchedPath " /notification"}} active{{end}}" href="/notification">通知</a> <a class="item{{if eq .MatchedPath " /notification"}} active{{end}}" href="/notification">通知</a>
<a class="item{{if eq .MatchedPath " /setting"}} active{{end}}" href="/setting">设置</a> <a class="item{{if eq .MatchedPath " /setting"}} active{{end}}" href="/setting">设置</a>
{{end}} {{end}}
<div class="right menu"> <div class="right menu">
<a class="item" href="https://github.com/naiba/nezha/issues" target="_blank">反馈</a>
<div class="item"> <div class="item">
{{if .Admin}} {{if .Admin}}
<div class="ui simple dropdown"> <div class="ui simple dropdown">

View File

@ -0,0 +1,31 @@
{{define "component/monitor"}}
<div class="ui tiny monitor modal transition hidden">
<div class="header">添加监控</div>
<div class="content">
<form id="monitorForm" class="ui form">
<input type="hidden" name="ID">
<div class="field">
<label>备注</label>
<input type="text" name="Name" placeholder="博客">
</div>
<div class="field">
<label>目标</label>
<input type="text" name="Target" placeholder="https://t.tt,t.tt,t.tt:80">
</div>
<div class="field">
<label>类型</label>
<select name="Type" class="ui fluid dropdown">
<option value="1">HTTP-GET</option>
<option value="2">ICMP-Ping</option>
<option value="3">TCP-Ping</option>
</select>
</div>
</form>
</div>
<div class=" actions">
<div class="ui negative button">取消</div>
<button class="ui positive right labeled icon button">确认<i class="checkmark icon"></i>
</button>
</div>
</div>
{{end}}

View File

@ -0,0 +1,49 @@
{{define "dashboard/monitor"}}
{{template "common/header" .}}
{{template "common/menu" .}}
<div class="nb-container">
<div class="ui container">
<div class="ui grid">
<div class="right floated right aligned twelve wide column">
<button class="ui right labeled positive icon button" onclick="addOrEditMonitor()"><i
class="add icon"></i> 添加监控
</button>
</div>
</div>
<table class="ui very basic table">
<thead>
<tr>
<th>ID</th>
<th>备注</th>
<th>类型</th>
<th>目标</th>
<th>管理</th>
</tr>
</thead>
<tbody>
{{range $monitor := .Monitors}}
<tr>
<td>{{$monitor.ID}}</td>
<td>{{$monitor.Name}}</td>
<td>{{$monitor.Target}}</td>
<td>{{$monitor.Type}}</td>
<td>
<div class="ui mini icon buttons">
<button class="ui button" onclick="addOrEditMonitor({{$monitor}})">
<i class="edit icon"></i>
</button>
<button class="ui button"
onclick="showConfirm('删除监控','确认删除此监控?',deleteRequest,'/api/monitor/'+{{$monitor.ID}})">
<i class="delete icon"></i>
</button>
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
{{template "component/monitor"}}
{{template "common/footer" .}}
{{end}}

View File

@ -14,8 +14,8 @@
</div> </div>
<div class="field"> <div class="field">
<select name="Theme"> <select name="Theme">
<option value="default">默认主题</option> <option value="default"{{if eq .Conf.Site.Theme "default"}} selected="selected"{{end}}>默认主题</option>
<option value="hotaru">CakeMine-Hotaru</option> <option value="hotaru"{{if eq .Conf.Site.Theme "hotaru"}} selected="selected"{{end}}>CokeMine Hotaru</option>
</select> </select>
</div> </div>
<div class="field"> <div class="field">

View File

@ -0,0 +1,42 @@
{{define "theme-default/service"}}
{{template "common/header" .}}
{{template "common/menu" .}}
<div class="nb-container">
<div class="ui container">
<div class="ui segment service-status">
{{range $service := .Services}}
<div class="ui grid">
<div class="three wide column">
<p>{{$service.Monitor.Name}}</p>
<p>30天在线率{{divU64 $service.TotalDown (addU64 $service.TotalUp $service.TotalDown)}}%</p>
</div>
<div class="eleven wide column">
{{range $i,$d := $service.Delay}}
<div class="ui icon button{{if gt (add (index $service.Up $i) (index $service.Down $i)) 0}}
{{if gt (div (index $service.Down $i) (add (index $service.Up $i) (index $service.Down $i))) 30.0}}danger
{{else if gt (div (index $service.Down $i) (add (index $service.Up $i) (index $service.Down $i))) 10.0}}
warning{{else}}good{{end}}
{{end}}" data-tooltip="{{dayBefore $i}},平均延迟:{{$d}}ms">
<i class="delay"></i>
</div>
{{end}}
</div>
<div class="two wide column round{{if gt (addU64 $service.TotalUp $service.TotalDown) 0}}
{{if gt (divU64 $service.TotalDown (addU64 $service.TotalUp $service.TotalDown)) 30.0}}danger{{else if gt (divU64 $service.TotalDown (addU64 $service.TotalUp $service.TotalDown)) 10.0}}warning{{else}}good{{end}}
{{end}}">
<i></i>
{{if gt (addU64 $service.TotalUp $service.TotalDown) 0}}
{{if gt (divU64 $service.TotalDown (addU64 $service.TotalUp $service.TotalDown)) 30.0}}故障
{{else if gt (divU64 $service.TotalDown (addU64 $service.TotalUp $service.TotalDown)) 10.0}}
低可用{{else}}良好{{end}}
{{else}}无数据
{{end}}
</div>
</div>
<div class="ui divider"></div>
{{end}}
</div>
</div>
</div>
{{template "common/footer" .}}
{{end}}

View File

@ -8,7 +8,6 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"github.com/naiba/nezha/model" "github.com/naiba/nezha/model"
pb "github.com/naiba/nezha/proto"
) )
const ( const (
@ -16,30 +15,18 @@ const (
ReportDelay = 2 ReportDelay = 2
) )
// Conf ..
var Conf *model.Config var Conf *model.Config
// Cache ..
var Cache *cache.Cache var Cache *cache.Cache
// DB ..
var DB *gorm.DB var DB *gorm.DB
// ServerList ..
var ServerList map[uint64]*model.Server var ServerList map[uint64]*model.Server
var SortedServerList []*model.Server var SortedServerList []*model.Server
// ServerLock ..
var ServerLock sync.RWMutex var ServerLock sync.RWMutex
// Version .. var Version = "v0.2.0"
var Version = "debug"
func init() {
if len(Version) > 7 {
Version = Version[:7]
}
}
func ReSortServer() { func ReSortServer() {
SortedServerList = []*model.Server{} SortedServerList = []*model.Server{}
@ -51,19 +38,3 @@ func ReSortServer() {
return SortedServerList[i].DisplayIndex > SortedServerList[j].DisplayIndex return SortedServerList[i].DisplayIndex > SortedServerList[j].DisplayIndex
}) })
} }
// SendCommand ..
func SendCommand(cmd *pb.Command) {
ServerLock.RLock()
defer ServerLock.RUnlock()
var err error
for _, server := range ServerList {
if server.Stream != nil {
err = server.Stream.Send(cmd)
if err != nil {
close(server.StreamClose)
server.Stream = nil
}
}
}
}

View File

@ -26,7 +26,6 @@ type ipDotSbGeoIP struct {
var netInSpeed, netOutSpeed, netInTransfer, netOutTransfer, lastUpdate uint64 var netInSpeed, netOutSpeed, netInTransfer, netOutTransfer, lastUpdate uint64
// GetHost ..
func GetHost() *model.Host { func GetHost() *model.Host {
hi, _ := host.Info() hi, _ := host.Info()
var cpus []string var cpus []string
@ -60,8 +59,7 @@ func GetHost() *model.Host {
} }
} }
// GetState .. func GetState(delay int64) *model.HostState {
func GetState(delay int64) *model.State {
hi, _ := host.Info() hi, _ := host.Info()
// Memory // Memory
mv, _ := mem.VirtualMemory() mv, _ := mem.VirtualMemory()
@ -75,7 +73,7 @@ func GetState(delay int64) *model.State {
// Disk // Disk
u, _ := disk.Usage("/") u, _ := disk.Usage("/")
return &model.State{ return &model.HostState{
CPU: cpuPercent, CPU: cpuPercent,
MemUsed: mv.Used, MemUsed: mv.Used,
SwapUsed: ms.Used, SwapUsed: ms.Used,
@ -88,7 +86,6 @@ func GetState(delay int64) *model.State {
} }
} }
// TrackNetworkSpeed ..
func TrackNetworkSpeed() { func TrackNetworkSpeed() {
var innerNetInTransfer, innerNetOutTransfer uint64 var innerNetInTransfer, innerNetOutTransfer uint64
nc, err := net.IOCounters(false) nc, err := net.IOCounters(false)

View File

@ -10,23 +10,19 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
) )
// AuthHandler ..
type AuthHandler struct { type AuthHandler struct {
ClientID string ClientID string
ClientSecret string ClientSecret string
} }
// GetRequestMetadata ..
func (a *AuthHandler) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { func (a *AuthHandler) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{"client_id": a.ClientID, "client_secret": a.ClientSecret}, nil return map[string]string{"client_id": a.ClientID, "client_secret": a.ClientSecret}, nil
} }
// RequireTransportSecurity ..
func (a *AuthHandler) RequireTransportSecurity() bool { func (a *AuthHandler) RequireTransportSecurity() bool {
return !dao.Conf.Debug return !dao.Conf.Debug
} }
// Check ..
func (a *AuthHandler) Check(ctx context.Context) (clientID uint64, err error) { func (a *AuthHandler) Check(ctx context.Context) (clientID uint64, err error) {
md, ok := metadata.FromIncomingContext(ctx) md, ok := metadata.FromIncomingContext(ctx)
if !ok { if !ok {

View File

@ -3,7 +3,6 @@ package rpc
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"time" "time"
"github.com/naiba/nezha/model" "github.com/naiba/nezha/model"
@ -12,13 +11,60 @@ import (
"github.com/naiba/nezha/service/dao" "github.com/naiba/nezha/service/dao"
) )
// NezhaHandler ..
type NezhaHandler struct { type NezhaHandler struct {
Auth *AuthHandler Auth *AuthHandler
} }
// ReportState .. func (s *NezhaHandler) ReportTask(c context.Context, r *pb.TaskResult) (*pb.Receipt, error) {
func (s *NezhaHandler) ReportState(c context.Context, r *pb.State) (*pb.Receipt, error) { var err error
if _, err = s.Auth.Check(c); err != nil {
return nil, err
}
if r.GetType() == model.MonitorTypeHTTPGET {
// SSL 证书变更报警
var last model.MonitorHistory
if err := dao.DB.Where("monitor_id = ?", r.GetId()).Order("id DESC").First(&last).Error; err == nil {
if last.Data != "" && last.Data != r.GetData() {
var monitor model.Monitor
dao.DB.First(&monitor, "id = ?", last.MonitorID)
alertmanager.SendNotification(fmt.Sprintf(
"监控:%s SSL证书变更%s%s。",
monitor.Name, last.Data, r.GetData()))
}
}
}
// 存入历史记录
mh := model.PB2MonitorHistory(r)
if err := dao.DB.Create(&mh).Error; err != nil {
return nil, err
}
// 更新最后检测时间
var m model.Monitor
m.ID = r.GetId()
if err := dao.DB.Model(&m).Update("last_check", time.Now()).Error; err != nil {
return nil, err
}
return &pb.Receipt{Proced: true}, nil
}
func (s *NezhaHandler) RequestTask(h *pb.Host, stream pb.NezhaService_RequestTaskServer) error {
var clientID uint64
var err error
if clientID, err = s.Auth.Check(stream.Context()); err != nil {
return err
}
closeCh := make(chan error)
dao.ServerLock.Lock()
dao.ServerList[clientID].TaskStream = stream
dao.ServerList[clientID].TaskClose = closeCh
dao.ServerLock.Unlock()
select {
case err = <-closeCh:
return err
}
}
func (s *NezhaHandler) ReportSystemState(c context.Context, r *pb.State) (*pb.Receipt, error) {
var clientID uint64 var clientID uint64
var err error var err error
if clientID, err = s.Auth.Check(c); err != nil { if clientID, err = s.Auth.Check(c); err != nil {
@ -32,28 +78,7 @@ func (s *NezhaHandler) ReportState(c context.Context, r *pb.State) (*pb.Receipt,
return &pb.Receipt{Proced: true}, nil return &pb.Receipt{Proced: true}, nil
} }
// Heartbeat .. func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Receipt, error) {
func (s *NezhaHandler) Heartbeat(r *pb.Beat, stream pb.NezhaService_HeartbeatServer) error {
var clientID uint64
var err error
defer log.Printf("Heartbeat exit server:%v err:%v", clientID, err)
if clientID, err = s.Auth.Check(stream.Context()); err != nil {
return err
}
// 放入在线服务器列表
dao.ServerLock.RLock()
closeCh := make(chan error)
dao.ServerList[clientID].StreamClose = closeCh
dao.ServerList[clientID].Stream = stream
dao.ServerLock.RUnlock()
select {
case err = <-closeCh:
return err
}
}
// Register ..
func (s *NezhaHandler) Register(c context.Context, r *pb.Host) (*pb.Receipt, error) {
var clientID uint64 var clientID uint64
var err error var err error
if clientID, err = s.Auth.Check(c); err != nil { if clientID, err = s.Auth.Check(c); err != nil {